diff options
Diffstat (limited to 'net/ipv4/tcp.c')
-rw-r--r-- | net/ipv4/tcp.c | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 019243408623..1f3d52946b3b 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c | |||
@@ -2465,6 +2465,106 @@ out: | |||
2465 | } | 2465 | } |
2466 | EXPORT_SYMBOL(tcp_tso_segment); | 2466 | EXPORT_SYMBOL(tcp_tso_segment); |
2467 | 2467 | ||
2468 | struct sk_buff **tcp_gro_receive(struct sk_buff **head, struct sk_buff *skb) | ||
2469 | { | ||
2470 | struct sk_buff **pp = NULL; | ||
2471 | struct sk_buff *p; | ||
2472 | struct tcphdr *th; | ||
2473 | struct tcphdr *th2; | ||
2474 | unsigned int thlen; | ||
2475 | unsigned int flags; | ||
2476 | unsigned int total; | ||
2477 | unsigned int mss = 1; | ||
2478 | int flush = 1; | ||
2479 | |||
2480 | if (!pskb_may_pull(skb, sizeof(*th))) | ||
2481 | goto out; | ||
2482 | |||
2483 | th = tcp_hdr(skb); | ||
2484 | thlen = th->doff * 4; | ||
2485 | if (thlen < sizeof(*th)) | ||
2486 | goto out; | ||
2487 | |||
2488 | if (!pskb_may_pull(skb, thlen)) | ||
2489 | goto out; | ||
2490 | |||
2491 | th = tcp_hdr(skb); | ||
2492 | __skb_pull(skb, thlen); | ||
2493 | |||
2494 | flags = tcp_flag_word(th); | ||
2495 | |||
2496 | for (; (p = *head); head = &p->next) { | ||
2497 | if (!NAPI_GRO_CB(p)->same_flow) | ||
2498 | continue; | ||
2499 | |||
2500 | th2 = tcp_hdr(p); | ||
2501 | |||
2502 | if (th->source != th2->source || th->dest != th2->dest) { | ||
2503 | NAPI_GRO_CB(p)->same_flow = 0; | ||
2504 | continue; | ||
2505 | } | ||
2506 | |||
2507 | goto found; | ||
2508 | } | ||
2509 | |||
2510 | goto out_check_final; | ||
2511 | |||
2512 | found: | ||
2513 | flush = NAPI_GRO_CB(p)->flush; | ||
2514 | flush |= flags & TCP_FLAG_CWR; | ||
2515 | flush |= (flags ^ tcp_flag_word(th2)) & | ||
2516 | ~(TCP_FLAG_CWR | TCP_FLAG_FIN | TCP_FLAG_PSH); | ||
2517 | flush |= th->ack_seq != th2->ack_seq || th->window != th2->window; | ||
2518 | flush |= memcmp(th + 1, th2 + 1, thlen - sizeof(*th)); | ||
2519 | |||
2520 | total = p->len; | ||
2521 | mss = total; | ||
2522 | if (skb_shinfo(p)->frag_list) | ||
2523 | mss = skb_shinfo(p)->frag_list->len; | ||
2524 | |||
2525 | flush |= skb->len > mss || skb->len <= 0; | ||
2526 | flush |= ntohl(th2->seq) + total != ntohl(th->seq); | ||
2527 | |||
2528 | if (flush || skb_gro_receive(head, skb)) { | ||
2529 | mss = 1; | ||
2530 | goto out_check_final; | ||
2531 | } | ||
2532 | |||
2533 | p = *head; | ||
2534 | th2 = tcp_hdr(p); | ||
2535 | tcp_flag_word(th2) |= flags & (TCP_FLAG_FIN | TCP_FLAG_PSH); | ||
2536 | |||
2537 | out_check_final: | ||
2538 | flush = skb->len < mss; | ||
2539 | flush |= flags & (TCP_FLAG_URG | TCP_FLAG_PSH | TCP_FLAG_RST | | ||
2540 | TCP_FLAG_SYN | TCP_FLAG_FIN); | ||
2541 | |||
2542 | if (p && (!NAPI_GRO_CB(skb)->same_flow || flush)) | ||
2543 | pp = head; | ||
2544 | |||
2545 | out: | ||
2546 | NAPI_GRO_CB(skb)->flush |= flush; | ||
2547 | |||
2548 | return pp; | ||
2549 | } | ||
2550 | |||
2551 | int tcp_gro_complete(struct sk_buff *skb) | ||
2552 | { | ||
2553 | struct tcphdr *th = tcp_hdr(skb); | ||
2554 | |||
2555 | skb->csum_start = skb_transport_header(skb) - skb->head; | ||
2556 | skb->csum_offset = offsetof(struct tcphdr, check); | ||
2557 | skb->ip_summed = CHECKSUM_PARTIAL; | ||
2558 | |||
2559 | skb_shinfo(skb)->gso_size = skb_shinfo(skb)->frag_list->len; | ||
2560 | skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count; | ||
2561 | |||
2562 | if (th->cwr) | ||
2563 | skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN; | ||
2564 | |||
2565 | return 0; | ||
2566 | } | ||
2567 | |||
2468 | #ifdef CONFIG_TCP_MD5SIG | 2568 | #ifdef CONFIG_TCP_MD5SIG |
2469 | static unsigned long tcp_md5sig_users; | 2569 | static unsigned long tcp_md5sig_users; |
2470 | static struct tcp_md5sig_pool **tcp_md5sig_pool; | 2570 | static struct tcp_md5sig_pool **tcp_md5sig_pool; |