Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | #ifndef _NET_TCP_ECN_H_ |
| 2 | #define _NET_TCP_ECN_H_ 1 |
| 3 | |
| 4 | #include <net/inet_ecn.h> |
Arnaldo Carvalho de Melo | 2e6599c | 2005-06-18 22:46:52 -0700 | [diff] [blame] | 5 | #include <net/request_sock.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6 | |
| 7 | #define TCP_HP_BITS (~(TCP_RESERVED_BITS|TCP_FLAG_PSH)) |
| 8 | |
| 9 | #define TCP_ECN_OK 1 |
| 10 | #define TCP_ECN_QUEUE_CWR 2 |
| 11 | #define TCP_ECN_DEMAND_CWR 4 |
| 12 | |
| 13 | static inline void TCP_ECN_queue_cwr(struct tcp_sock *tp) |
| 14 | { |
| 15 | if (tp->ecn_flags&TCP_ECN_OK) |
| 16 | tp->ecn_flags |= TCP_ECN_QUEUE_CWR; |
| 17 | } |
| 18 | |
| 19 | |
| 20 | /* Output functions */ |
| 21 | |
| 22 | static inline void TCP_ECN_send_synack(struct tcp_sock *tp, |
| 23 | struct sk_buff *skb) |
| 24 | { |
| 25 | TCP_SKB_CB(skb)->flags &= ~TCPCB_FLAG_CWR; |
| 26 | if (!(tp->ecn_flags&TCP_ECN_OK)) |
| 27 | TCP_SKB_CB(skb)->flags &= ~TCPCB_FLAG_ECE; |
| 28 | } |
| 29 | |
Ilpo Järvinen | 9e412ba | 2007-04-20 22:18:02 -0700 | [diff] [blame] | 30 | static inline void TCP_ECN_send_syn(struct sock *sk, struct sk_buff *skb) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 31 | { |
Ilpo Järvinen | 9e412ba | 2007-04-20 22:18:02 -0700 | [diff] [blame] | 32 | struct tcp_sock *tp = tcp_sk(sk); |
| 33 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 34 | tp->ecn_flags = 0; |
Michael Chan | b0da8537 | 2006-06-29 12:30:00 -0700 | [diff] [blame] | 35 | if (sysctl_tcp_ecn) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 36 | TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_ECE|TCPCB_FLAG_CWR; |
| 37 | tp->ecn_flags = TCP_ECN_OK; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 38 | } |
| 39 | } |
| 40 | |
| 41 | static __inline__ void |
Arnaldo Carvalho de Melo | 60236fd | 2005-06-18 22:47:21 -0700 | [diff] [blame] | 42 | TCP_ECN_make_synack(struct request_sock *req, struct tcphdr *th) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 43 | { |
Arnaldo Carvalho de Melo | 2e6599c | 2005-06-18 22:46:52 -0700 | [diff] [blame] | 44 | if (inet_rsk(req)->ecn_ok) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 45 | th->ece = 1; |
| 46 | } |
| 47 | |
Ilpo Järvinen | 9e412ba | 2007-04-20 22:18:02 -0700 | [diff] [blame] | 48 | static inline void TCP_ECN_send(struct sock *sk, struct sk_buff *skb, |
| 49 | int tcp_header_len) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 50 | { |
Ilpo Järvinen | 9e412ba | 2007-04-20 22:18:02 -0700 | [diff] [blame] | 51 | struct tcp_sock *tp = tcp_sk(sk); |
| 52 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 53 | if (tp->ecn_flags & TCP_ECN_OK) { |
| 54 | /* Not-retransmitted data segment: set ECT and inject CWR. */ |
| 55 | if (skb->len != tcp_header_len && |
| 56 | !before(TCP_SKB_CB(skb)->seq, tp->snd_nxt)) { |
| 57 | INET_ECN_xmit(sk); |
| 58 | if (tp->ecn_flags&TCP_ECN_QUEUE_CWR) { |
| 59 | tp->ecn_flags &= ~TCP_ECN_QUEUE_CWR; |
Arnaldo Carvalho de Melo | aa8223c | 2007-04-10 21:04:22 -0700 | [diff] [blame] | 60 | tcp_hdr(skb)->cwr = 1; |
Herbert Xu | f83ef8c | 2006-06-30 13:37:03 -0700 | [diff] [blame] | 61 | skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 62 | } |
| 63 | } else { |
| 64 | /* ACK or retransmitted segment: clear ECT|CE */ |
| 65 | INET_ECN_dontxmit(sk); |
| 66 | } |
| 67 | if (tp->ecn_flags & TCP_ECN_DEMAND_CWR) |
Arnaldo Carvalho de Melo | aa8223c | 2007-04-10 21:04:22 -0700 | [diff] [blame] | 68 | tcp_hdr(skb)->ece = 1; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 69 | } |
| 70 | } |
| 71 | |
| 72 | /* Input functions */ |
| 73 | |
| 74 | static inline void TCP_ECN_accept_cwr(struct tcp_sock *tp, struct sk_buff *skb) |
| 75 | { |
Arnaldo Carvalho de Melo | aa8223c | 2007-04-10 21:04:22 -0700 | [diff] [blame] | 76 | if (tcp_hdr(skb)->cwr) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 77 | tp->ecn_flags &= ~TCP_ECN_DEMAND_CWR; |
| 78 | } |
| 79 | |
| 80 | static inline void TCP_ECN_withdraw_cwr(struct tcp_sock *tp) |
| 81 | { |
| 82 | tp->ecn_flags &= ~TCP_ECN_DEMAND_CWR; |
| 83 | } |
| 84 | |
| 85 | static inline void TCP_ECN_check_ce(struct tcp_sock *tp, struct sk_buff *skb) |
| 86 | { |
| 87 | if (tp->ecn_flags&TCP_ECN_OK) { |
| 88 | if (INET_ECN_is_ce(TCP_SKB_CB(skb)->flags)) |
| 89 | tp->ecn_flags |= TCP_ECN_DEMAND_CWR; |
| 90 | /* Funny extension: if ECT is not set on a segment, |
| 91 | * it is surely retransmit. It is not in ECN RFC, |
| 92 | * but Linux follows this rule. */ |
| 93 | else if (INET_ECN_is_not_ect((TCP_SKB_CB(skb)->flags))) |
Arnaldo Carvalho de Melo | 463c84b | 2005-08-09 20:10:42 -0700 | [diff] [blame] | 94 | tcp_enter_quickack_mode((struct sock *)tp); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 95 | } |
| 96 | } |
| 97 | |
| 98 | static inline void TCP_ECN_rcv_synack(struct tcp_sock *tp, struct tcphdr *th) |
| 99 | { |
| 100 | if ((tp->ecn_flags&TCP_ECN_OK) && (!th->ece || th->cwr)) |
| 101 | tp->ecn_flags &= ~TCP_ECN_OK; |
| 102 | } |
| 103 | |
| 104 | static inline void TCP_ECN_rcv_syn(struct tcp_sock *tp, struct tcphdr *th) |
| 105 | { |
| 106 | if ((tp->ecn_flags&TCP_ECN_OK) && (!th->ece || !th->cwr)) |
| 107 | tp->ecn_flags &= ~TCP_ECN_OK; |
| 108 | } |
| 109 | |
| 110 | static inline int TCP_ECN_rcv_ecn_echo(struct tcp_sock *tp, struct tcphdr *th) |
| 111 | { |
| 112 | if (th->ece && !th->syn && (tp->ecn_flags&TCP_ECN_OK)) |
| 113 | return 1; |
| 114 | return 0; |
| 115 | } |
| 116 | |
| 117 | static inline void TCP_ECN_openreq_child(struct tcp_sock *tp, |
Arnaldo Carvalho de Melo | 60236fd | 2005-06-18 22:47:21 -0700 | [diff] [blame] | 118 | struct request_sock *req) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 119 | { |
Arnaldo Carvalho de Melo | 2e6599c | 2005-06-18 22:46:52 -0700 | [diff] [blame] | 120 | tp->ecn_flags = inet_rsk(req)->ecn_ok ? TCP_ECN_OK : 0; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 121 | } |
| 122 | |
| 123 | static __inline__ void |
Arnaldo Carvalho de Melo | 60236fd | 2005-06-18 22:47:21 -0700 | [diff] [blame] | 124 | TCP_ECN_create_request(struct request_sock *req, struct tcphdr *th) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 125 | { |
| 126 | if (sysctl_tcp_ecn && th->ece && th->cwr) |
Arnaldo Carvalho de Melo | 2e6599c | 2005-06-18 22:46:52 -0700 | [diff] [blame] | 127 | inet_rsk(req)->ecn_ok = 1; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 128 | } |
| 129 | |
| 130 | #endif |