Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tcp/dccp: do not touch listener sk_refcnt under synflood

When a SYNFLOOD targets a non SO_REUSEPORT listener, multiple
cpus contend on sk->sk_refcnt and sk->sk_wmem_alloc changes.

By letting listeners use SOCK_RCU_FREE infrastructure,
we can relax TCP_LISTEN lookup rules and avoid touching sk_refcnt

Note that we still use SLAB_DESTROY_BY_RCU rules for other sockets,
only listeners are impacted by this change.

Peak performance under SYNFLOOD is increased by ~33% :

On my test machine, I could process 3.2 Mpps instead of 2.4 Mpps

Most consuming functions are now skb_set_owner_w() and sock_wfree()
contending on sk->sk_wmem_alloc when cooking SYNACK and freeing them.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Eric Dumazet and committed by
David S. Miller
3b24d854 3a5d1c0e

+134 -163
+8 -4
include/net/inet6_hashtables.h
··· 66 66 const __be16 sport, 67 67 const struct in6_addr *daddr, 68 68 const u16 hnum, 69 - const int dif) 69 + const int dif, 70 + bool *refcounted) 70 71 { 71 72 struct sock *sk = __inet6_lookup_established(net, hashinfo, saddr, 72 73 sport, daddr, hnum, dif); 74 + *refcounted = true; 73 75 if (sk) 74 76 return sk; 75 - 77 + *refcounted = false; 76 78 return inet6_lookup_listener(net, hashinfo, skb, doff, saddr, sport, 77 79 daddr, hnum, dif); 78 80 } ··· 83 81 struct sk_buff *skb, int doff, 84 82 const __be16 sport, 85 83 const __be16 dport, 86 - int iif) 84 + int iif, 85 + bool *refcounted) 87 86 { 88 87 struct sock *sk = skb_steal_sock(skb); 89 88 89 + *refcounted = true; 90 90 if (sk) 91 91 return sk; 92 92 93 93 return __inet6_lookup(dev_net(skb_dst(skb)->dev), hashinfo, skb, 94 94 doff, &ipv6_hdr(skb)->saddr, sport, 95 95 &ipv6_hdr(skb)->daddr, ntohs(dport), 96 - iif); 96 + iif, refcounted); 97 97 } 98 98 99 99 struct sock *inet6_lookup(struct net *net, struct inet_hashinfo *hashinfo,
+24 -16
include/net/inet_hashtables.h
··· 100 100 101 101 /* 102 102 * Sockets can be hashed in established or listening table 103 - * We must use different 'nulls' end-of-chain value for listening 104 - * hash table, or we might find a socket that was closed and 105 - * reallocated/inserted into established hash table 106 103 */ 107 - #define LISTENING_NULLS_BASE (1U << 29) 108 104 struct inet_listen_hashbucket { 109 105 spinlock_t lock; 110 - struct hlist_nulls_head head; 106 + struct hlist_head head; 111 107 }; 112 108 113 109 /* This is for listening sockets, thus all sockets which possess wildcards. */ ··· 300 304 struct sk_buff *skb, int doff, 301 305 const __be32 saddr, const __be16 sport, 302 306 const __be32 daddr, const __be16 dport, 303 - const int dif) 307 + const int dif, 308 + bool *refcounted) 304 309 { 305 310 u16 hnum = ntohs(dport); 306 - struct sock *sk = __inet_lookup_established(net, hashinfo, 307 - saddr, sport, daddr, hnum, dif); 311 + struct sock *sk; 308 312 309 - return sk ? : __inet_lookup_listener(net, hashinfo, skb, doff, saddr, 310 - sport, daddr, hnum, dif); 313 + sk = __inet_lookup_established(net, hashinfo, saddr, sport, 314 + daddr, hnum, dif); 315 + *refcounted = true; 316 + if (sk) 317 + return sk; 318 + *refcounted = false; 319 + return __inet_lookup_listener(net, hashinfo, skb, doff, saddr, 320 + sport, daddr, hnum, dif); 311 321 } 312 322 313 323 static inline struct sock *inet_lookup(struct net *net, ··· 324 322 const int dif) 325 323 { 326 324 struct sock *sk; 325 + bool refcounted; 327 326 328 327 sk = __inet_lookup(net, hashinfo, skb, doff, saddr, sport, daddr, 329 - dport, dif); 328 + dport, dif, &refcounted); 330 329 330 + if (sk && !refcounted && !atomic_inc_not_zero(&sk->sk_refcnt)) 331 + sk = NULL; 331 332 return sk; 332 333 } 333 334 ··· 338 333 struct sk_buff *skb, 339 334 int doff, 340 335 const __be16 sport, 341 - const __be16 dport) 336 + const __be16 dport, 337 + bool *refcounted) 342 338 { 343 339 struct sock *sk = skb_steal_sock(skb); 344 340 const struct iphdr *iph = ip_hdr(skb); 345 341 342 + *refcounted = true; 346 343 if (sk) 347 344 return sk; 348 - else 349 - return __inet_lookup(dev_net(skb_dst(skb)->dev), hashinfo, skb, 350 - doff, iph->saddr, sport, 351 - iph->daddr, dport, inet_iif(skb)); 345 + 346 + return __inet_lookup(dev_net(skb_dst(skb)->dev), hashinfo, skb, 347 + doff, iph->saddr, sport, 348 + iph->daddr, dport, inet_iif(skb), 349 + refcounted); 352 350 } 353 351 354 352 u32 sk_ehashfn(const struct sock *sk);
+5 -2
net/dccp/ipv4.c
··· 764 764 { 765 765 const struct dccp_hdr *dh; 766 766 const struct iphdr *iph; 767 + bool refcounted; 767 768 struct sock *sk; 768 769 int min_cov; 769 770 ··· 802 801 803 802 lookup: 804 803 sk = __inet_lookup_skb(&dccp_hashinfo, skb, __dccp_hdr_len(dh), 805 - dh->dccph_sport, dh->dccph_dport); 804 + dh->dccph_sport, dh->dccph_dport, &refcounted); 806 805 if (!sk) { 807 806 dccp_pr_debug("failed to look up flow ID in table and " 808 807 "get corresponding socket\n"); ··· 831 830 goto lookup; 832 831 } 833 832 sock_hold(sk); 833 + refcounted = true; 834 834 nsk = dccp_check_req(sk, skb, req); 835 835 if (!nsk) { 836 836 reqsk_put(req); ··· 888 886 return 0; 889 887 890 888 discard_and_relse: 891 - sock_put(sk); 889 + if (refcounted) 890 + sock_put(sk); 892 891 goto discard_it; 893 892 } 894 893
+5 -2
net/dccp/ipv6.c
··· 642 642 static int dccp_v6_rcv(struct sk_buff *skb) 643 643 { 644 644 const struct dccp_hdr *dh; 645 + bool refcounted; 645 646 struct sock *sk; 646 647 int min_cov; 647 648 ··· 671 670 lookup: 672 671 sk = __inet6_lookup_skb(&dccp_hashinfo, skb, __dccp_hdr_len(dh), 673 672 dh->dccph_sport, dh->dccph_dport, 674 - inet6_iif(skb)); 673 + inet6_iif(skb), &refcounted); 675 674 if (!sk) { 676 675 dccp_pr_debug("failed to look up flow ID in table and " 677 676 "get corresponding socket\n"); ··· 700 699 goto lookup; 701 700 } 702 701 sock_hold(sk); 702 + refcounted = true; 703 703 nsk = dccp_check_req(sk, skb, req); 704 704 if (!nsk) { 705 705 reqsk_put(req); ··· 754 752 return 0; 755 753 756 754 discard_and_relse: 757 - sock_put(sk); 755 + if (refcounted) 756 + sock_put(sk); 758 757 goto discard_it; 759 758 } 760 759
+1 -2
net/ipv4/inet_diag.c
··· 775 775 776 776 for (i = s_i; i < INET_LHTABLE_SIZE; i++) { 777 777 struct inet_listen_hashbucket *ilb; 778 - struct hlist_nulls_node *node; 779 778 struct sock *sk; 780 779 781 780 num = 0; 782 781 ilb = &hashinfo->listening_hash[i]; 783 782 spin_lock_bh(&ilb->lock); 784 - sk_nulls_for_each(sk, node, &ilb->head) { 783 + sk_for_each(sk, &ilb->head) { 785 784 struct inet_sock *inet = inet_sk(sk); 786 785 787 786 if (!net_eq(sock_net(sk), net))
+25 -48
net/ipv4/inet_hashtables.c
··· 198 198 } 199 199 200 200 /* 201 - * Don't inline this cruft. Here are some nice properties to exploit here. The 202 - * BSD API does not allow a listening sock to specify the remote port nor the 201 + * Here are some nice properties to exploit here. The BSD API 202 + * does not allow a listening sock to specify the remote port nor the 203 203 * remote address for the connection. So always assume those are both 204 204 * wildcarded during the search since they can never be otherwise. 205 205 */ 206 206 207 - 207 + /* called with rcu_read_lock() : No refcount taken on the socket */ 208 208 struct sock *__inet_lookup_listener(struct net *net, 209 209 struct inet_hashinfo *hashinfo, 210 210 struct sk_buff *skb, int doff, ··· 212 212 const __be32 daddr, const unsigned short hnum, 213 213 const int dif) 214 214 { 215 - struct sock *sk, *result; 216 - struct hlist_nulls_node *node; 217 215 unsigned int hash = inet_lhashfn(net, hnum); 218 216 struct inet_listen_hashbucket *ilb = &hashinfo->listening_hash[hash]; 219 - int score, hiscore, matches = 0, reuseport = 0; 220 - bool select_ok = true; 217 + int score, hiscore = 0, matches = 0, reuseport = 0; 218 + struct sock *sk, *result = NULL; 221 219 u32 phash = 0; 222 220 223 - begin: 224 - result = NULL; 225 - hiscore = 0; 226 - sk_nulls_for_each_rcu(sk, node, &ilb->head) { 221 + sk_for_each_rcu(sk, &ilb->head) { 227 222 score = compute_score(sk, net, hnum, daddr, dif); 228 223 if (score > hiscore) { 229 - result = sk; 230 - hiscore = score; 231 224 reuseport = sk->sk_reuseport; 232 225 if (reuseport) { 233 226 phash = inet_ehashfn(net, daddr, hnum, 234 227 saddr, sport); 235 - if (select_ok) { 236 - struct sock *sk2; 237 - sk2 = reuseport_select_sock(sk, phash, 238 - skb, doff); 239 - if (sk2) { 240 - result = sk2; 241 - goto found; 242 - } 243 - } 228 + result = reuseport_select_sock(sk, phash, 229 + skb, doff); 230 + if (result) 231 + return result; 244 232 matches = 1; 245 233 } 234 + result = sk; 235 + hiscore = score; 246 236 } else if (score == hiscore && reuseport) { 247 237 matches++; 248 238 if (reciprocal_scale(phash, matches) == 0) 249 239 result = sk; 250 240 phash = next_pseudo_random32(phash); 251 - } 252 - } 253 - /* 254 - * if the nulls value we got at the end of this lookup is 255 - * not the expected one, we must restart lookup. 256 - * We probably met an item that was moved to another chain. 257 - */ 258 - if (get_nulls_value(node) != hash + LISTENING_NULLS_BASE) 259 - goto begin; 260 - if (result) { 261 - found: 262 - if (unlikely(!atomic_inc_not_zero(&result->sk_refcnt))) 263 - result = NULL; 264 - else if (unlikely(compute_score(result, net, hnum, daddr, 265 - dif) < hiscore)) { 266 - sock_put(result); 267 - select_ok = false; 268 - goto begin; 269 241 } 270 242 } 271 243 return result; ··· 480 508 if (err) 481 509 goto unlock; 482 510 } 483 - __sk_nulls_add_node_rcu(sk, &ilb->head); 511 + hlist_add_head_rcu(&sk->sk_node, &ilb->head); 512 + sock_set_flag(sk, SOCK_RCU_FREE); 484 513 sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); 485 514 unlock: 486 515 spin_unlock(&ilb->lock); ··· 508 535 { 509 536 struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo; 510 537 spinlock_t *lock; 538 + bool listener = false; 511 539 int done; 512 540 513 541 if (sk_unhashed(sk)) 514 542 return; 515 543 516 - if (sk->sk_state == TCP_LISTEN) 544 + if (sk->sk_state == TCP_LISTEN) { 517 545 lock = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)].lock; 518 - else 546 + listener = true; 547 + } else { 519 548 lock = inet_ehash_lockp(hashinfo, sk->sk_hash); 520 - 549 + } 521 550 spin_lock_bh(lock); 522 551 if (rcu_access_pointer(sk->sk_reuseport_cb)) 523 552 reuseport_detach_sock(sk); 524 - done = __sk_nulls_del_node_init_rcu(sk); 553 + if (listener) 554 + done = __sk_del_node_init(sk); 555 + else 556 + done = __sk_nulls_del_node_init_rcu(sk); 525 557 if (done) 526 558 sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); 527 559 spin_unlock_bh(lock); ··· 662 684 663 685 for (i = 0; i < INET_LHTABLE_SIZE; i++) { 664 686 spin_lock_init(&h->listening_hash[i].lock); 665 - INIT_HLIST_NULLS_HEAD(&h->listening_hash[i].head, 666 - i + LISTENING_NULLS_BASE); 667 - } 687 + INIT_HLIST_HEAD(&h->listening_hash[i].head); 688 + } 668 689 } 669 690 EXPORT_SYMBOL_GPL(inet_hashinfo_init); 670 691
+33 -33
net/ipv4/tcp_ipv4.c
··· 628 628 629 629 net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev); 630 630 #ifdef CONFIG_TCP_MD5SIG 631 + rcu_read_lock(); 631 632 hash_location = tcp_parse_md5sig_option(th); 632 633 if (sk && sk_fullsock(sk)) { 633 634 key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *) ··· 647 646 ntohs(th->source), inet_iif(skb)); 648 647 /* don't send rst if it can't find key */ 649 648 if (!sk1) 650 - return; 651 - rcu_read_lock(); 649 + goto out; 650 + 652 651 key = tcp_md5_do_lookup(sk1, (union tcp_md5_addr *) 653 652 &ip_hdr(skb)->saddr, AF_INET); 654 653 if (!key) 655 - goto release_sk1; 654 + goto out; 655 + 656 656 657 657 genhash = tcp_v4_md5_hash_skb(newhash, key, NULL, skb); 658 658 if (genhash || memcmp(hash_location, newhash, 16) != 0) 659 - goto release_sk1; 659 + goto out; 660 + 660 661 } 661 662 662 663 if (key) { ··· 701 698 TCP_INC_STATS_BH(net, TCP_MIB_OUTRSTS); 702 699 703 700 #ifdef CONFIG_TCP_MD5SIG 704 - release_sk1: 705 - if (sk1) { 706 - rcu_read_unlock(); 707 - sock_put(sk1); 708 - } 701 + out: 702 + rcu_read_unlock(); 709 703 #endif 710 704 } 711 705 ··· 1538 1538 1539 1539 int tcp_v4_rcv(struct sk_buff *skb) 1540 1540 { 1541 + struct net *net = dev_net(skb->dev); 1541 1542 const struct iphdr *iph; 1542 1543 const struct tcphdr *th; 1544 + bool refcounted; 1543 1545 struct sock *sk; 1544 1546 int ret; 1545 - struct net *net = dev_net(skb->dev); 1546 1547 1547 1548 if (skb->pkt_type != PACKET_HOST) 1548 1549 goto discard_it; ··· 1589 1588 1590 1589 lookup: 1591 1590 sk = __inet_lookup_skb(&tcp_hashinfo, skb, __tcp_hdrlen(th), th->source, 1592 - th->dest); 1591 + th->dest, &refcounted); 1593 1592 if (!sk) 1594 1593 goto no_tcp_socket; 1595 1594 ··· 1610 1609 inet_csk_reqsk_queue_drop_and_put(sk, req); 1611 1610 goto lookup; 1612 1611 } 1612 + /* We own a reference on the listener, increase it again 1613 + * as we might lose it too soon. 1614 + */ 1613 1615 sock_hold(sk); 1616 + refcounted = true; 1614 1617 nsk = tcp_check_req(sk, skb, req, false); 1615 1618 if (!nsk) { 1616 1619 reqsk_put(req); ··· 1670 1665 bh_unlock_sock(sk); 1671 1666 1672 1667 put_and_return: 1673 - sock_put(sk); 1668 + if (refcounted) 1669 + sock_put(sk); 1674 1670 1675 1671 return ret; 1676 1672 ··· 1694 1688 return 0; 1695 1689 1696 1690 discard_and_relse: 1697 - sock_put(sk); 1691 + if (refcounted) 1692 + sock_put(sk); 1698 1693 goto discard_it; 1699 1694 1700 1695 do_time_wait: ··· 1719 1712 if (sk2) { 1720 1713 inet_twsk_deschedule_put(inet_twsk(sk)); 1721 1714 sk = sk2; 1715 + refcounted = false; 1722 1716 goto process; 1723 1717 } 1724 1718 /* Fall through to ACK */ ··· 1853 1845 */ 1854 1846 static void *listening_get_next(struct seq_file *seq, void *cur) 1855 1847 { 1856 - struct inet_connection_sock *icsk; 1857 - struct hlist_nulls_node *node; 1858 - struct sock *sk = cur; 1859 - struct inet_listen_hashbucket *ilb; 1860 1848 struct tcp_iter_state *st = seq->private; 1861 1849 struct net *net = seq_file_net(seq); 1850 + struct inet_listen_hashbucket *ilb; 1851 + struct inet_connection_sock *icsk; 1852 + struct sock *sk = cur; 1862 1853 1863 1854 if (!sk) { 1855 + get_head: 1864 1856 ilb = &tcp_hashinfo.listening_hash[st->bucket]; 1865 1857 spin_lock_bh(&ilb->lock); 1866 - sk = sk_nulls_head(&ilb->head); 1858 + sk = sk_head(&ilb->head); 1867 1859 st->offset = 0; 1868 1860 goto get_sk; 1869 1861 } ··· 1871 1863 ++st->num; 1872 1864 ++st->offset; 1873 1865 1874 - sk = sk_nulls_next(sk); 1866 + sk = sk_next(sk); 1875 1867 get_sk: 1876 - sk_nulls_for_each_from(sk, node) { 1868 + sk_for_each_from(sk) { 1877 1869 if (!net_eq(sock_net(sk), net)) 1878 1870 continue; 1879 - if (sk->sk_family == st->family) { 1880 - cur = sk; 1881 - goto out; 1882 - } 1871 + if (sk->sk_family == st->family) 1872 + return sk; 1883 1873 icsk = inet_csk(sk); 1884 1874 } 1885 1875 spin_unlock_bh(&ilb->lock); 1886 1876 st->offset = 0; 1887 - if (++st->bucket < INET_LHTABLE_SIZE) { 1888 - ilb = &tcp_hashinfo.listening_hash[st->bucket]; 1889 - spin_lock_bh(&ilb->lock); 1890 - sk = sk_nulls_head(&ilb->head); 1891 - goto get_sk; 1892 - } 1893 - cur = NULL; 1894 - out: 1895 - return cur; 1877 + if (++st->bucket < INET_LHTABLE_SIZE) 1878 + goto get_head; 1879 + return NULL; 1896 1880 } 1897 1881 1898 1882 static void *listening_get_idx(struct seq_file *seq, loff_t *pos)
+15 -41
net/ipv6/inet6_hashtables.c
··· 120 120 return score; 121 121 } 122 122 123 + /* called with rcu_read_lock() */ 123 124 struct sock *inet6_lookup_listener(struct net *net, 124 125 struct inet_hashinfo *hashinfo, 125 126 struct sk_buff *skb, int doff, ··· 128 127 const __be16 sport, const struct in6_addr *daddr, 129 128 const unsigned short hnum, const int dif) 130 129 { 131 - struct sock *sk; 132 - const struct hlist_nulls_node *node; 133 - struct sock *result; 134 - int score, hiscore, matches = 0, reuseport = 0; 135 - bool select_ok = true; 136 - u32 phash = 0; 137 130 unsigned int hash = inet_lhashfn(net, hnum); 138 131 struct inet_listen_hashbucket *ilb = &hashinfo->listening_hash[hash]; 132 + int score, hiscore = 0, matches = 0, reuseport = 0; 133 + struct sock *sk, *result = NULL; 134 + u32 phash = 0; 139 135 140 - begin: 141 - result = NULL; 142 - hiscore = 0; 143 - sk_nulls_for_each(sk, node, &ilb->head) { 136 + sk_for_each(sk, &ilb->head) { 144 137 score = compute_score(sk, net, hnum, daddr, dif); 145 138 if (score > hiscore) { 146 139 hiscore = score; 147 - result = sk; 148 - reuseport = sk->sk_reuseport; 149 140 if (reuseport) { 150 141 phash = inet6_ehashfn(net, daddr, hnum, 151 142 saddr, sport); 152 - if (select_ok) { 153 - struct sock *sk2; 154 - sk2 = reuseport_select_sock(sk, phash, 155 - skb, doff); 156 - if (sk2) { 157 - result = sk2; 158 - goto found; 159 - } 160 - } 143 + result = reuseport_select_sock(sk, phash, 144 + skb, doff); 145 + if (result) 146 + return result; 161 147 matches = 1; 162 148 } 149 + result = sk; 150 + reuseport = sk->sk_reuseport; 163 151 } else if (score == hiscore && reuseport) { 164 152 matches++; 165 153 if (reciprocal_scale(phash, matches) == 0) 166 154 result = sk; 167 155 phash = next_pseudo_random32(phash); 168 - } 169 - } 170 - /* 171 - * if the nulls value we got at the end of this lookup is 172 - * not the expected one, we must restart lookup. 173 - * We probably met an item that was moved to another chain. 174 - */ 175 - if (get_nulls_value(node) != hash + LISTENING_NULLS_BASE) 176 - goto begin; 177 - if (result) { 178 - found: 179 - if (unlikely(!atomic_inc_not_zero(&result->sk_refcnt))) 180 - result = NULL; 181 - else if (unlikely(compute_score(result, net, hnum, daddr, 182 - dif) < hiscore)) { 183 - sock_put(result); 184 - select_ok = false; 185 - goto begin; 186 156 } 187 157 } 188 158 return result; ··· 167 195 const int dif) 168 196 { 169 197 struct sock *sk; 198 + bool refcounted; 170 199 171 200 sk = __inet6_lookup(net, hashinfo, skb, doff, saddr, sport, daddr, 172 - ntohs(dport), dif); 173 - 201 + ntohs(dport), dif, &refcounted); 202 + if (sk && !refcounted && !atomic_inc_not_zero(&sk->sk_refcnt)) 203 + sk = NULL; 174 204 return sk; 175 205 } 176 206 EXPORT_SYMBOL_GPL(inet6_lookup);
+15 -12
net/ipv6/tcp_ipv6.c
··· 858 858 return; 859 859 860 860 #ifdef CONFIG_TCP_MD5SIG 861 + rcu_read_lock(); 861 862 hash_location = tcp_parse_md5sig_option(th); 862 863 if (sk && sk_fullsock(sk)) { 863 864 key = tcp_v6_md5_do_lookup(sk, &ipv6h->saddr); ··· 876 875 th->source, &ipv6h->daddr, 877 876 ntohs(th->source), tcp_v6_iif(skb)); 878 877 if (!sk1) 879 - return; 878 + goto out; 880 879 881 - rcu_read_lock(); 882 880 key = tcp_v6_md5_do_lookup(sk1, &ipv6h->saddr); 883 881 if (!key) 884 - goto release_sk1; 882 + goto out; 885 883 886 884 genhash = tcp_v6_md5_hash_skb(newhash, key, NULL, skb); 887 885 if (genhash || memcmp(hash_location, newhash, 16) != 0) 888 - goto release_sk1; 886 + goto out; 889 887 } 890 888 #endif 891 889 ··· 898 898 tcp_v6_send_response(sk, skb, seq, ack_seq, 0, 0, 0, oif, key, 1, 0, 0); 899 899 900 900 #ifdef CONFIG_TCP_MD5SIG 901 - release_sk1: 902 - if (sk1) { 903 - rcu_read_unlock(); 904 - sock_put(sk1); 905 - } 901 + out: 902 + rcu_read_unlock(); 906 903 #endif 907 904 } 908 905 ··· 1348 1351 { 1349 1352 const struct tcphdr *th; 1350 1353 const struct ipv6hdr *hdr; 1354 + bool refcounted; 1351 1355 struct sock *sk; 1352 1356 int ret; 1353 1357 struct net *net = dev_net(skb->dev); ··· 1379 1381 1380 1382 lookup: 1381 1383 sk = __inet6_lookup_skb(&tcp_hashinfo, skb, __tcp_hdrlen(th), 1382 - th->source, th->dest, inet6_iif(skb)); 1384 + th->source, th->dest, inet6_iif(skb), 1385 + &refcounted); 1383 1386 if (!sk) 1384 1387 goto no_tcp_socket; 1385 1388 ··· 1403 1404 goto lookup; 1404 1405 } 1405 1406 sock_hold(sk); 1407 + refcounted = true; 1406 1408 nsk = tcp_check_req(sk, skb, req, false); 1407 1409 if (!nsk) { 1408 1410 reqsk_put(req); ··· 1460 1460 bh_unlock_sock(sk); 1461 1461 1462 1462 put_and_return: 1463 - sock_put(sk); 1463 + if (refcounted) 1464 + sock_put(sk); 1464 1465 return ret ? -1 : 0; 1465 1466 1466 1467 no_tcp_socket: ··· 1484 1483 return 0; 1485 1484 1486 1485 discard_and_relse: 1487 - sock_put(sk); 1486 + if (refcounted) 1487 + sock_put(sk); 1488 1488 goto discard_it; 1489 1489 1490 1490 do_time_wait: ··· 1516 1514 inet_twsk_deschedule_put(tw); 1517 1515 sk = sk2; 1518 1516 tcp_v6_restore_cb(skb); 1517 + refcounted = false; 1519 1518 goto process; 1520 1519 } 1521 1520 /* Fall through to ACK */
+3 -3
net/netfilter/xt_socket.c
··· 120 120 { 121 121 switch (protocol) { 122 122 case IPPROTO_TCP: 123 - return __inet_lookup(net, &tcp_hashinfo, skb, doff, 124 - saddr, sport, daddr, dport, 125 - in->ifindex); 123 + return inet_lookup(net, &tcp_hashinfo, skb, doff, 124 + saddr, sport, daddr, dport, 125 + in->ifindex); 126 126 case IPPROTO_UDP: 127 127 return udp4_lib_lookup(net, saddr, sport, daddr, dport, 128 128 in->ifindex);