diff options
-rw-r--r-- | include/net/tcp.h | 6 | ||||
-rw-r--r-- | net/ipv4/af_inet.c | 2 | ||||
-rw-r--r-- | net/ipv4/tcp.c | 100 | ||||
-rw-r--r-- | net/ipv4/tcp_ipv4.c | 35 |
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 | ||
1359 | extern int tcp_v4_gso_send_check(struct sk_buff *skb); | 1359 | extern int tcp_v4_gso_send_check(struct sk_buff *skb); |
1360 | extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features); | 1360 | extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features); |
1361 | extern struct sk_buff **tcp_gro_receive(struct sk_buff **head, | ||
1362 | struct sk_buff *skb); | ||
1363 | extern struct sk_buff **tcp4_gro_receive(struct sk_buff **head, | ||
1364 | struct sk_buff *skb); | ||
1365 | extern int tcp_gro_complete(struct sk_buff *skb); | ||
1366 | extern int tcp4_gro_complete(struct sk_buff *skb); | ||
1361 | 1367 | ||
1362 | #ifdef CONFIG_PROC_FS | 1368 | #ifdef CONFIG_PROC_FS |
1363 | extern int tcp4_proc_init(void); | 1369 | extern 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 | } |
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; |
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 | ||
2349 | struct 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 | } | ||
2369 | EXPORT_SYMBOL(tcp4_gro_receive); | ||
2370 | |||
2371 | int 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 | } | ||
2382 | EXPORT_SYMBOL(tcp4_gro_complete); | ||
2383 | |||
2349 | struct proto tcp_prot = { | 2384 | struct proto tcp_prot = { |
2350 | .name = "TCP", | 2385 | .name = "TCP", |
2351 | .owner = THIS_MODULE, | 2386 | .owner = THIS_MODULE, |