Skip to content

Commit 1a4f14b

Browse files
committed
Merge branch 'tcp-robust-ooo'
Eric Dumazet says: ==================== Juha-Matti Tilli reported that malicious peers could inject tiny packets in out_of_order_queue, forcing very expensive calls to tcp_collapse_ofo_queue() and tcp_prune_ofo_queue() for every incoming packet. With tcp_rmem[2] default of 6MB, the ooo queue could contain ~7000 nodes. This patch series makes sure we cut cpu cycles enough to render the attack not critical. We might in the future go further, like disconnecting or black-holing proven malicious flows. ==================== Signed-off-by: David S. Miller <[email protected]>
2 parents 3dd1c9a + 58152ec commit 1a4f14b

File tree

1 file changed

+50
-12
lines changed

1 file changed

+50
-12
lines changed

net/ipv4/tcp_input.c

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4358,6 +4358,23 @@ static bool tcp_try_coalesce(struct sock *sk,
43584358
return true;
43594359
}
43604360

4361+
static bool tcp_ooo_try_coalesce(struct sock *sk,
4362+
struct sk_buff *to,
4363+
struct sk_buff *from,
4364+
bool *fragstolen)
4365+
{
4366+
bool res = tcp_try_coalesce(sk, to, from, fragstolen);
4367+
4368+
/* In case tcp_drop() is called later, update to->gso_segs */
4369+
if (res) {
4370+
u32 gso_segs = max_t(u16, 1, skb_shinfo(to)->gso_segs) +
4371+
max_t(u16, 1, skb_shinfo(from)->gso_segs);
4372+
4373+
skb_shinfo(to)->gso_segs = min_t(u32, gso_segs, 0xFFFF);
4374+
}
4375+
return res;
4376+
}
4377+
43614378
static void tcp_drop(struct sock *sk, struct sk_buff *skb)
43624379
{
43634380
sk_drops_add(sk, skb);
@@ -4481,8 +4498,8 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb)
44814498
/* In the typical case, we are adding an skb to the end of the list.
44824499
* Use of ooo_last_skb avoids the O(Log(N)) rbtree lookup.
44834500
*/
4484-
if (tcp_try_coalesce(sk, tp->ooo_last_skb,
4485-
skb, &fragstolen)) {
4501+
if (tcp_ooo_try_coalesce(sk, tp->ooo_last_skb,
4502+
skb, &fragstolen)) {
44864503
coalesce_done:
44874504
tcp_grow_window(sk, skb);
44884505
kfree_skb_partial(skb, fragstolen);
@@ -4510,7 +4527,7 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb)
45104527
/* All the bits are present. Drop. */
45114528
NET_INC_STATS(sock_net(sk),
45124529
LINUX_MIB_TCPOFOMERGE);
4513-
__kfree_skb(skb);
4530+
tcp_drop(sk, skb);
45144531
skb = NULL;
45154532
tcp_dsack_set(sk, seq, end_seq);
45164533
goto add_sack;
@@ -4529,11 +4546,11 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb)
45294546
TCP_SKB_CB(skb1)->end_seq);
45304547
NET_INC_STATS(sock_net(sk),
45314548
LINUX_MIB_TCPOFOMERGE);
4532-
__kfree_skb(skb1);
4549+
tcp_drop(sk, skb1);
45334550
goto merge_right;
45344551
}
4535-
} else if (tcp_try_coalesce(sk, skb1,
4536-
skb, &fragstolen)) {
4552+
} else if (tcp_ooo_try_coalesce(sk, skb1,
4553+
skb, &fragstolen)) {
45374554
goto coalesce_done;
45384555
}
45394556
p = &parent->rb_right;
@@ -4902,6 +4919,7 @@ tcp_collapse(struct sock *sk, struct sk_buff_head *list, struct rb_root *root,
49024919
static void tcp_collapse_ofo_queue(struct sock *sk)
49034920
{
49044921
struct tcp_sock *tp = tcp_sk(sk);
4922+
u32 range_truesize, sum_tiny = 0;
49054923
struct sk_buff *skb, *head;
49064924
u32 start, end;
49074925

@@ -4913,6 +4931,7 @@ static void tcp_collapse_ofo_queue(struct sock *sk)
49134931
}
49144932
start = TCP_SKB_CB(skb)->seq;
49154933
end = TCP_SKB_CB(skb)->end_seq;
4934+
range_truesize = skb->truesize;
49164935

49174936
for (head = skb;;) {
49184937
skb = skb_rb_next(skb);
@@ -4923,11 +4942,20 @@ static void tcp_collapse_ofo_queue(struct sock *sk)
49234942
if (!skb ||
49244943
after(TCP_SKB_CB(skb)->seq, end) ||
49254944
before(TCP_SKB_CB(skb)->end_seq, start)) {
4926-
tcp_collapse(sk, NULL, &tp->out_of_order_queue,
4927-
head, skb, start, end);
4945+
/* Do not attempt collapsing tiny skbs */
4946+
if (range_truesize != head->truesize ||
4947+
end - start >= SKB_WITH_OVERHEAD(SK_MEM_QUANTUM)) {
4948+
tcp_collapse(sk, NULL, &tp->out_of_order_queue,
4949+
head, skb, start, end);
4950+
} else {
4951+
sum_tiny += range_truesize;
4952+
if (sum_tiny > sk->sk_rcvbuf >> 3)
4953+
return;
4954+
}
49284955
goto new_range;
49294956
}
49304957

4958+
range_truesize += skb->truesize;
49314959
if (unlikely(before(TCP_SKB_CB(skb)->seq, start)))
49324960
start = TCP_SKB_CB(skb)->seq;
49334961
if (after(TCP_SKB_CB(skb)->end_seq, end))
@@ -4942,27 +4970,34 @@ static void tcp_collapse_ofo_queue(struct sock *sk)
49424970
* 2) not add too big latencies if thousands of packets sit there.
49434971
* (But if application shrinks SO_RCVBUF, we could still end up
49444972
* freeing whole queue here)
4973+
* 3) Drop at least 12.5 % of sk_rcvbuf to avoid malicious attacks.
49454974
*
49464975
* Return true if queue has shrunk.
49474976
*/
49484977
static bool tcp_prune_ofo_queue(struct sock *sk)
49494978
{
49504979
struct tcp_sock *tp = tcp_sk(sk);
49514980
struct rb_node *node, *prev;
4981+
int goal;
49524982

49534983
if (RB_EMPTY_ROOT(&tp->out_of_order_queue))
49544984
return false;
49554985

49564986
NET_INC_STATS(sock_net(sk), LINUX_MIB_OFOPRUNED);
4987+
goal = sk->sk_rcvbuf >> 3;
49574988
node = &tp->ooo_last_skb->rbnode;
49584989
do {
49594990
prev = rb_prev(node);
49604991
rb_erase(node, &tp->out_of_order_queue);
4992+
goal -= rb_to_skb(node)->truesize;
49614993
tcp_drop(sk, rb_to_skb(node));
4962-
sk_mem_reclaim(sk);
4963-
if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf &&
4964-
!tcp_under_memory_pressure(sk))
4965-
break;
4994+
if (!prev || goal <= 0) {
4995+
sk_mem_reclaim(sk);
4996+
if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf &&
4997+
!tcp_under_memory_pressure(sk))
4998+
break;
4999+
goal = sk->sk_rcvbuf >> 3;
5000+
}
49665001
node = prev;
49675002
} while (node);
49685003
tp->ooo_last_skb = rb_to_skb(prev);
@@ -4997,6 +5032,9 @@ static int tcp_prune_queue(struct sock *sk)
49975032
else if (tcp_under_memory_pressure(sk))
49985033
tp->rcv_ssthresh = min(tp->rcv_ssthresh, 4U * tp->advmss);
49995034

5035+
if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf)
5036+
return 0;
5037+
50005038
tcp_collapse_ofo_queue(sk);
50015039
if (!skb_queue_empty(&sk->sk_receive_queue))
50025040
tcp_collapse(sk, &sk->sk_receive_queue, NULL,

0 commit comments

Comments
 (0)