aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/tcp.h6
-rw-r--r--net/ipv4/af_inet.c2
-rw-r--r--net/ipv4/tcp.c100
-rw-r--r--net/ipv4/tcp_ipv4.c35
4 files changed, 143 insertions, 0 deletions
diff --git a/include/net/tcp.h b/include/net/tcp.h
index de1e91d959b8..218235de8963 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1358,6 +1358,12 @@ extern void tcp_v4_destroy_sock(struct sock *sk);
1358 1358
1359extern int tcp_v4_gso_send_check(struct sk_buff *skb); 1359extern int tcp_v4_gso_send_check(struct sk_buff *skb);
1360extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features); 1360extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features);
1361extern struct sk_buff **tcp_gro_receive(struct sk_buff **head,
1362 struct sk_buff *skb);
1363extern struct sk_buff **tcp4_gro_receive(struct sk_buff **head,
1364 struct sk_buff *skb);
1365extern int tcp_gro_complete(struct sk_buff *skb);
1366extern int tcp4_gro_complete(struct sk_buff *skb);
1361 1367
1362#ifdef CONFIG_PROC_FS 1368#ifdef CONFIG_PROC_FS
1363extern int tcp4_proc_init(void); 1369extern int tcp4_proc_init(void);
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index a85595307fa7..664ff0ee1c83 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1410,6 +1410,8 @@ static struct net_protocol tcp_protocol = {
1410 .err_handler = tcp_v4_err, 1410 .err_handler = tcp_v4_err,
1411 .gso_send_check = tcp_v4_gso_send_check, 1411 .gso_send_check = tcp_v4_gso_send_check,
1412 .gso_segment = tcp_tso_segment, 1412 .gso_segment = tcp_tso_segment,
1413 .gro_receive = tcp4_gro_receive,
1414 .gro_complete = tcp4_gro_complete,
1413 .no_policy = 1, 1415 .no_policy = 1,
1414 .netns_ok = 1, 1416 .netns_ok = 1,
1415}; 1417};
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}
2466EXPORT_SYMBOL(tcp_tso_segment); 2466EXPORT_SYMBOL(tcp_tso_segment);
2467 2467
2468struct 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
2512found:
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
2537out_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
2545out:
2546 NAPI_GRO_CB(skb)->flush |= flush;
2547
2548 return pp;
2549}
2550
2551int 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
2469static unsigned long tcp_md5sig_users; 2569static unsigned long tcp_md5sig_users;
2470static struct tcp_md5sig_pool **tcp_md5sig_pool; 2570static struct tcp_md5sig_pool **tcp_md5sig_pool;
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 26b9030747cc..10172487921b 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -2346,6 +2346,41 @@ void tcp4_proc_exit(void)
2346} 2346}
2347#endif /* CONFIG_PROC_FS */ 2347#endif /* CONFIG_PROC_FS */
2348 2348
2349struct sk_buff **tcp4_gro_receive(struct sk_buff **head, struct sk_buff *skb)
2350{
2351 struct iphdr *iph = ip_hdr(skb);
2352
2353 switch (skb->ip_summed) {
2354 case CHECKSUM_COMPLETE:
2355 if (!tcp_v4_check(skb->len, iph->saddr, iph->daddr,
2356 skb->csum)) {
2357 skb->ip_summed = CHECKSUM_UNNECESSARY;
2358 break;
2359 }
2360
2361 /* fall through */
2362 case CHECKSUM_NONE:
2363 NAPI_GRO_CB(skb)->flush = 1;
2364 return NULL;
2365 }
2366
2367 return tcp_gro_receive(head, skb);
2368}
2369EXPORT_SYMBOL(tcp4_gro_receive);
2370
2371int tcp4_gro_complete(struct sk_buff *skb)
2372{
2373 struct iphdr *iph = ip_hdr(skb);
2374 struct tcphdr *th = tcp_hdr(skb);
2375
2376 th->check = ~tcp_v4_check(skb->len - skb_transport_offset(skb),
2377 iph->saddr, iph->daddr, 0);
2378 skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
2379
2380 return tcp_gro_complete(skb);
2381}
2382EXPORT_SYMBOL(tcp4_gro_complete);
2383
2349struct proto tcp_prot = { 2384struct proto tcp_prot = {
2350 .name = "TCP", 2385 .name = "TCP",
2351 .owner = THIS_MODULE, 2386 .owner = THIS_MODULE,