tcp: bugs and cleanup updated to 2.6.33-rc8 (again)
Subsystems (4)
cover tcp: bugs and cleanup updated to 2.6.33-rc8 (again)
Combination of patches reported in October, November, December, January and February. These patches fix perceived bugs, and other cleanup.
This code has had previous review and several months of limited testing.
Some portions were removed during the various TCPCT part 1 patch splits, then were cut off by the sudden unexpected end of that merge window. [03 Dec 2009] I've restarted the sub-numbering (again).
Of particular interest are the TCPCT header extensions that already appear in the next phase of testing with other platforms. These patches allow correct reception without data corruption.
The remainder of the original TCPCT part 2 will be merged with part 3.
These are patches against the current linux-2.6 tree.
[Parts 2f and 2g updated, fixing some more problems introduced by David Miller in commit bb5b7c11263dbbe78253cd05945a6bf8f55add8e]
PATCH v3 1/7 net: tcp_header_len_th and tcp_option_len_th
Redefine two TCP header functions to accept TCP header pointer. When subtracting, return signed int to allow error checking.
These functions will be used in subsequent patches that implement additional features.
--- include/linux/tcp.h | 12 ++++++++++++ 1 files changed, 12 insertions(+), 0 deletions(-)
include/linux/tcp.h +12 -0
@@ -223,6 +223,18 @@ static inline unsigned int tcp_optlen(const struct sk_buff *skb)return (tcp_hdr (skb)->doff -5 ) *4 ; } +/* Length of fixed header plus standard options. */ +static inline unsigned int tcp_header_len_th (const struct tcphdr *th) +{ +return th->doff *4 ; +} + +/* Length of standard options only. This could be negative. */ +static inline int tcp_option_len_th (const struct tcphdr *th) +{ +return (int )(th->doff *4 ) -sizeof (*th); +} +/* This defines a selective acknowledgement block. */ struct tcp_sack_block_wire {__be32 start_seq;
Signature · 1 line
1.6.3.3
PATCH v3 2/7 net: remove old tcp_optlen function
In reply to William Allen SimpsonThe tcp_optlen() function returns a potential *negative* unsigned.
In the only two existing files using the old tcp_optlen() function, clean up confusing and inconsistent mixing of both byte and word offsets, and other coding style issues. Document assumptions.
Quoth David Miller:
This is transmit, and the packets can only come from the Linux TCP stack, not some external entity.
You're being way too anal here, and adding these checks to drivers would be just a lot of rediculious bloat. [sic]
Therefore, there are *no* checks for bad TCP and IP header sizes, nor any semantic changes. The drivers should function exactly as existing.
No response from testers in 17+ weeks.
Requires:
net: tcp_header_len_th and tcp_option_len_th
--- drivers/net/bnx2.c | 29 +++++++++++++----------- drivers/net/tg3.c | 60 +++++++++++++++++++++++--------------------------- include/linux/tcp.h | 5 ---- 3 files changed, 44 insertions(+), 50 deletions(-)
drivers/net/bnx2.c +16 -13
@@ -6352,6 +6352,8 @@ bnx2_vlan_rx_register(struct net_device *dev, struct vlan_group *vlgrp)/* Called with netif_tx_lock. * bnx2_tx_int() runs without netif_tx_lock unless it needs to call * netif_wake_queue(). +* +* No TCP or IP length checking, per David Miller (see commit log). */ static netdev_tx_t bnx2_start_xmit (struct sk_buff *skb,struct net_device *dev)
@@ -6396,19 +6398,19 @@ bnx2_start_xmit(struct sk_buff *skb, struct net_device *dev) (TX_BD_FLAGS_VLAN_TAG | (vlan_tx_tag_get(skb) << 16)); }#endif -if ((mss =skb_shinfo (skb)->gso_size)) { -u32 tcp_opt_len; -struct iphdr *iph; + mss =skb_shinfo (skb)->gso_size; +if (mss !=0 ) { +struct tcphdr *th =tcp_hdr (skb); +int tcp_opt_words = th->doff - (sizeof (*th) >>2 ); +/* assumes positive tcp_opt_words without checking */ vlan_tag_flags |=TX_BD_FLAGS_SW_LSO ; - tcp_opt_len =tcp_optlen (skb); -if (skb_shinfo (skb)->gso_type &SKB_GSO_TCPV6 ) {u32 tcp_off =skb_transport_offset (skb) -sizeof (struct ipv6hdr ) -ETH_HLEN ; - vlan_tag_flags |= ((tcp_opt_len >>2 ) <<8 ) | + vlan_tag_flags |= (tcp_opt_words <<8 ) |TX_BD_FLAGS_SW_FLAGS ;if (likely (tcp_off ==0 )) vlan_tag_flags &= ~TX_BD_FLAGS_TCP6_OFF0_MSK ;
@@ -6421,14 +6423,15 @@ bnx2_start_xmit(struct sk_buff *skb, struct net_device *dev) mss |= (tcp_off &0xc ) <<TX_BD_TCP6_OFF2_SHL ; } }else { - iph =ip_hdr (skb); -if (tcp_opt_len || (iph->ihl >5 )) { - vlan_tag_flags |= ((iph->ihl -5 ) + - (tcp_opt_len >>2 )) <<8 ; - } +struct iphdr *iph =ip_hdr (skb); +int ip_opt_words = iph->ihl - (sizeof (*iph) >>2 ); +/* assumes positive ip_opt_words without checking */ +int opt_words = ip_opt_words + tcp_opt_words; + +if (opt_words >0 ) + vlan_tag_flags |= opt_words <<8 ; } - }else - mss =0 ; + } mapping =pci_map_single (bp->pdev, skb->data, len,PCI_DMA_TODEVICE );if (pci_dma_mapping_error (bp->pdev, mapping)) {
drivers/net/tg3.c +28 -32
126 lines not on this page · raw message
include/linux/tcp.h +0 -5
@@ -218,11 +218,6 @@ static inline unsigned int tcp_hdrlen(const struct sk_buff *skb)return tcp_hdr (skb)->doff *4 ; } -static inline unsigned int tcp_optlen (const struct sk_buff *skb) -{ -return (tcp_hdr (skb)->doff -5 ) *4 ; -} -/* Length of fixed header plus standard options. */ static inline unsigned int tcp_header_len_th (const struct tcphdr *th) {
Signature · 1 line
1.6.3.3
PATCH v5 3/7 tcp: harmonize tcp_vx_rcv header length assumptions
In reply to William Allen SimpsonHarmonize tcp_v4_rcv() and tcp_v6_rcv() -- better document tcp doff and header length assumptions, and carefully compare implementations.
Reduces multiply/shifts, marginally improving speed.
Removes redundant tcp header length checks before checksumming.
Instead, assumes (and documents) that any backlog processing and transform policies will carefully preserve the header, and will ensure the socket buffer length remains >= the header size.
Stand-alone patch, originally developed for TCPCT.
--- include/net/xfrm.h | 7 ++++++ net/ipv4/tcp_ipv4.c | 45 +++++++++++++++++++++----------------- net/ipv6/tcp_ipv6.c | 59 ++++++++++++++++++++++++++++---------------------- 3 files changed, 65 insertions(+), 46 deletions(-)
include/net/xfrm.h +7 -0
@@ -975,6 +975,13 @@ xfrm_state_addr_cmp(struct xfrm_tmpl *tmpl, struct xfrm_state *x, unsigned short }#ifdef CONFIG_XFRM +/* +* For transport, the policy is checked before the presumed more expensive +* checksum. The transport header has already been checked for size, and is +* guaranteed to be contiguous. These policies must not alter the header or +* its position in the buffer, and should not shorten the buffer length +* without ensuring the length remains >= the header size. +*/ extern int __xfrm_policy_check (struct sock *,int dir,struct sk_buff *skb,unsigned short family); static inline int __xfrm_policy_check2(struct sock *sk, int dir,
net/ipv4/tcp_ipv4.c +25 -20
109 lines not on this page · raw message
net/ipv6/tcp_ipv6.c +33 -26
126 lines not on this page · raw message
Signature · 1 line
1.6.3.3
Re: [PATCH v5 3/7] tcp: harmonize tcp_vx_rcv header length assumptions
Quoted 10 lines · William Allen Simpson
Harmonize
tcp_v4_rcv()andtcp_v6_rcv()-- better document tcp doff and header length assumptions, and carefully compare implementations.Reduces multiply/shifts, marginally improving speed.
Removes redundant tcp header length checks before checksumming.
Instead, assumes (and documents) that any backlog processing and transform policies will carefully preserve the header, and will ensure the socket buffer length remains >= the header size.
I reviewed the patch and it looks ok to me. As far as I can see it's mostly manual CSE, no real behaviour change.
Normally it's customary to separate formatting changes from real changes, but it was only in a few places and not too bad.
I didn't fully understand that new comment:
/* nf_reset(skb); in ip6_input.c ip6_input_finish() */
Overall you can add a
-Andi
Re: [PATCH v5 3/7] tcp: harmonize tcp_vx_rcv header length assumptions
Quoted 8 lines · Andi Kleen
Quoted 2 lines · William Allen Simpson
Harmonize
tcp_v4_rcv()andtcp_v6_rcv()-- better document tcp doff and header length assumptions, and carefully compare implementations.I didn't fully understand that new comment:
/*
nf_reset(skb); inip6_input.cip6_input_finish()*/
That's part of the harmonization. IPv4 has a nf_reset() in this code position. I asked on the list where IPv6 did the same thing, so that the difference could be documented. The information was provided by Patrick McHardy. If someday somebody actually finishes merging the two functions, that's the only actual difference.
Quoted 4 lines
Overall you can add a
Reviewed-by Andi Kleen <[email protected]>
Thanks, hopefully as applied.
PATCH v4 4/7 tcp: input header length, prediction, and timestamp bugs
In reply to William Allen SimpsonFix incorrect header prediction flags documentation.
Relieve register pressure in (the i386) fast path by accessing skb->len directly, instead of carrying a rarely used len parameter.
Eliminate unused len parameters in two other functions.
Don't use output calculated tp->tcp_header_len for input decisions. While the output header is usually the same as the input (same options in both directions), that's a poor assumption. In particular, Sack will be different. Newer options are not guaranteed.
Moreover, in the fast path, that only saved a shift or two. The other efficiencies in this patch more than make up the difference.
Instead, use tp->rx_opt.tstamp_ok to accurately predict header length.
Likewise, use tp->rx_opt.tstamp_ok for received MSS calculations.
Don't use "sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED" to guess that the timestamp is present. This may have been OK in the days with fewer possible options, but various combinations of newer options may yield the same header length. (This bug is in 3 places.)
Instead, use tp->rx_opt.saw_tstamp to determine a timestamp is present.
There's no need to test buffer length against header length, already checked by tcp_v[4,6]_rcv(). Straighten code for minor efficiency gain.
Stand-alone patch, originally developed for TCPCT.
Requires:
net: tcp_header_len_th and tcp_option_len_th
tcp: harmonize tcp_vx_rcv header length assumptions
include/linux/tcp.h +5 -1
@@ -301,7 +301,11 @@ struct tcp_sock {/* * Header prediction flags -* 0x5?10 << 16 + snd_wnd in net byte order +* S << 28 + TCP_FLAG_ACK + snd_wnd, in net byte order +* (PSH flag is ignored) +* S is 5 (no options), or 8 (timestamp aligned) +* otherwise, 0 to turn it off -- for instance, when there are +* holes in receive space. */ __be32 pred_flags;
include/net/tcp.h +10 -5
@@ -310,13 +310,11 @@ extern int tcp_ioctl(struct sock *sk,extern int tcp_rcv_state_process (struct sock *sk,struct sk_buff *skb, -struct tcphdr *th, -unsigned len); +struct tcphdr *th);extern int tcp_rcv_established (struct sock *sk,struct sk_buff *skb, -struct tcphdr *th, -unsigned len); +struct tcphdr *th);extern void tcp_rcv_space_adjust (struct sock *sk);
@@ -533,9 +531,16 @@ static inline u32 __tcp_set_rto(const struct tcp_sock *tp)return (tp->srtt >>3 ) + tp->rttvar; } +static inline u16 __tcp_fast_path_header_length (const struct tcp_sock *tp) +{ +return tp->rx_opt.tstamp_ok + ?sizeof (struct tcphdr ) +TCPOLEN_TSTAMP_ALIGNED + :sizeof (struct tcphdr ); +} +static inline void __tcp_fast_path_on (struct tcp_sock *tp,u32 snd_wnd) { - tp->pred_flags =htonl ((tp->tcp_header_len <<26 ) | + tp->pred_flags =htonl ((__tcp_fast_path_header_length (tp) << (28 -2 )) |ntohl (TCP_FLAG_ACK ) | snd_wnd); }
net/ipv4/tcp_input.c +37 -57
166 lines not on this page · raw message
net/ipv4/tcp_ipv4.c +2 -2
@@ -1551,7 +1551,7 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)if (sk->sk_state ==TCP_ESTABLISHED ) {/* Fast path */ TCP_CHECK_TIMER (sk); -if (tcp_rcv_established (sk, skb,tcp_hdr (skb), skb->len)) { +if (tcp_rcv_established (sk, skb,tcp_hdr (skb))) { rsk = sk; goto reset; }
@@ -1578,7 +1578,7 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) }TCP_CHECK_TIMER (sk); -if (tcp_rcv_state_process (sk, skb,tcp_hdr (skb), skb->len)) { +if (tcp_rcv_state_process (sk, skb,tcp_hdr (skb))) { rsk = sk; goto reset; }
net/ipv4/tcp_minisocks.c +1 -2
@@ -718,8 +718,7 @@ int tcp_child_process(struct sock *parent, struct sock *child,int state = child->sk_state;if (!sock_owned_by_user (child)) { - ret =tcp_rcv_state_process (child, skb,tcp_hdr (skb), - skb->len); + ret =tcp_rcv_state_process (child, skb,tcp_hdr (skb));/* Wakeup parent, send SIGIO */ if (state ==TCP_SYN_RECV && child->sk_state != state) parent->sk_data_ready (parent,0 );
net/ipv4/tcp_probe.c +1 -1
@@ -88,7 +88,7 @@ static inline int tcp_probe_avail(void)* Note: arguments must match tcp_rcv_established()! */ static int jtcp_rcv_established (struct sock *sk,struct sk_buff *skb, -struct tcphdr *th,unsigned len) +struct tcphdr *th) {const struct tcp_sock *tp =tcp_sk (sk);const struct inet_sock *inet =inet_sk (sk);
net/ipv6/tcp_ipv6.c +2 -2
@@ -1586,7 +1586,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)if (sk->sk_state ==TCP_ESTABLISHED ) {/* Fast path */ TCP_CHECK_TIMER (sk); -if (tcp_rcv_established (sk, skb,tcp_hdr (skb), skb->len)) +if (tcp_rcv_established (sk, skb,tcp_hdr (skb))) goto reset;TCP_CHECK_TIMER (sk);if (opt_skb)
@@ -1618,7 +1618,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) }TCP_CHECK_TIMER (sk); -if (tcp_rcv_state_process (sk, skb,tcp_hdr (skb), skb->len)) +if (tcp_rcv_state_process (sk, skb,tcp_hdr (skb))) goto reset;TCP_CHECK_TIMER (sk);if (opt_skb)
Signature · 1 line
1.6.3.3
Re: [PATCH v4 4/7] tcp: input header length, prediction, and timestamp bugs
Quoted 11 lines · William Allen Simpson
Fix incorrect header prediction flags documentation.
Relieve register pressure in (the i386) fast path by accessing
skb->lendirectly, instead of carrying a rarely used len parameter.Eliminate unused len parameters in two other functions.
Don't use output calculated
tp->tcp_header_lenfor input decisions. While the output header is usually the same as the input (same options in both directions), that's a poor assumption. In particular, Sack will be different. Newer options are not guaranteed.
Is this a bug fix?
tcp_ack() is so bloated these days that I sometimes wonder if the fast path still makes sense. It would be probably an interesting study to actually count cycles for the common cases.
On the other hand CPU cycles on modern systems are so cheap that it might be simpler to simply ignore them and only optimize for cache misses. I suppose a cache optimized tcp_rcv_establish() would look quite different.
Anyways that's not directly related to the patch.
Quoted 4 lines
There's no need to test buffer length against header length, already checked by tcp_v[4,6]_rcv(). Straighten code for minor efficiency gain.
Stand-alone patch, originally developed for TCPCT.
Normally it would be better to split this into smaller patches that do one thing at a time (typically this requires getting used to patch stack tools like "quilt")
But it's not too bad here.
include/net/tcp.h static inline u32 __tcp_set_rto(const struct tcp_sock *tp) · line 543
Quoted 4 lines
static inline void
__tcp_fast_path_on(struct tcp_sock *tp, u32 snd_wnd){include/net/tcp.h +1
-1- tp->pred_flags = htonl((tp->tcp_header_len << 26) | + tp->pred_flags = htonl((__tcp_fast_path_header_length(tp) << (28 - 2)) |
It would be better to use defines or sizeof for the magic numbers.
net/ipv4/tcp_input.c int tcp_rcv_established(struct sock *sk, struct sk_buff *skb, · line 5238
Quoted 10 lines
net/ipv4/tcp_input.c +0
-10- tp->rx_opt.saw_tstamp =0 ; - -/* pred_flags is 0xS?10 << 16 + snd_wnd -* if header_prediction is to be made -* 'S' will always be tp->tcp_header_len >> 2 -* '?' will be 0 for the fast path, otherwise pred_flags is 0 to -* turn it off (when there are holes in the receive -* space for instance) -* PSH flag is ignored. -*/
I liked the comment at this place.
I did a quick review of the rest and it seems ok to me.
-Andi
Signature · 1 line
[email protected] -- Speaking for myself only.
Re: [PATCH v4 4/7] tcp: input header length, prediction, and timestamp bugs
Quoted 8 lines · Andi Kleen
Quoted 4 lines · William Allen Simpson
Don't use output calculated
tp->tcp_header_lenfor input decisions. While the output header is usually the same as the input (same options in both directions), that's a poor assumption. In particular, Sack will be different. Newer options are not guaranteed.Is this a bug fix?
Yes. One of many, all inter-related.
I don't know how much description folks want in the patch "summary", so simply used declarative statements that are one-to-one with the order of the patch, but it took me a bit to grok this problem!
1) unknown options can be stripped out of the header in middleware, see RFC 1122 section 4.2.2.5.
2) new options Cookie Pair and 64-bit Timestamps (defined in patch 7).
3) stripping them leaves a Sack covering 1 segment, which has the exact same word count as 32-bit Timestamps. Boom! All the silly checks against the size of the options field (instead of the proper saw_tstamp) start setting fields based on completely useless data!
4) and of course, using the size of the previous output to predict the expected input header size is a poor assumption (to be generous).
There are 29+ options these days, not 4 or 5. There are options that are only sent one way. There are options that have different data in different directions.
Yes, it was originally for TCPCT, but fixes a broad spectrum of bugs.
Quoted 8 lines
Quoted 1 line
Stand-alone patch, originally developed for TCPCT.
Normally it would be better to split this into smaller patches that do one thing at a time (typically this requires getting used to patch stack tools like "quilt")
But it's not too bad here.
There are small efficiency patches included, but it would be likely impossible to split them from the bug fixes without re-writing the same code over and over again. And I'm doing these patch splits by hand....
I did recently learn how to maintain branches that are branches on top of each other, so I've got tcpct1, tcpct2, and tcpct3 for the 3 parts. But it's a pain to keep updated with git fetch, and checkout, and rebase, for each branch.
At first, I was keeping a master patch set, and trying to maintain it over .31, .32, and net-next, and now .33 -- but I gave up.
Quoted 7 lines
include/net/tcp.h static inline u32 __tcp_set_rto(const struct tcp_sock *tp) · line 543
Quoted 4 lines
static inline void
__tcp_fast_path_on(struct tcp_sock *tp, u32 snd_wnd){include/net/tcp.h +1
-1- tp->pred_flags = htonl((tp->tcp_header_len << 26) | + tp->pred_flags = htonl((__tcp_fast_path_header_length(tp) << (28 - 2)) |It would be better to use defines or sizeof for the magic numbers.
I agree! I was just following the existing coding style, trying to improve understanding by splitting it into 28 (matches the shift documented in the header file), and 2 (the doff field is actually the number of 32-bit words).
These are field offsets in a 32-bit word, sizeof() wouldn't work.
It might be even better to have pred_flags be a union, but I didn't do the original design for this code....
I'll add a nice block comment explaining the shift value here.
Quoted 4 lines
Quoted 4 lines
net/ipv4/tcp_input.c +0
-4- tp->rx_opt.saw_tstamp =0 ; - -/* pred_flags is 0xS?10 << 16 + snd_wnd -* if header_prediction is to be made
Quoted 9 lines
net/ipv4/tcp_input.c int tcp_rcv_established(struct sock *sk, struct sk_buff *skb, · line 5238
Quoted 6 lines
net/ipv4/tcp_input.c +0
-6-* 'S' will always be tp->tcp_header_len >> 2 -* '?' will be 0 for the fast path, otherwise pred_flags is 0 to -* turn it off (when there are holes in the receive -* space for instance) -* PSH flag is ignored. -*/ I liked the comment at this place.
The existing comment here didn't match the comment in the header file, and both comments had errors. Here, the 'S' is wrong. Instead, it was only '5' in the header file, among other errors.
It's easier to avoid bit-rot by defining in only one place, but I will add a comment here saying "See linux/tcp.h for pred_flags details."
Quoted 5 lines
I did a quick review of the rest and it seems ok to me.
-Andi
Thank you again. As the fixes requested are merely adding comments, I'll quickly re-spin this patch without reposting the entire patch set.
Re: [PATCH v4 4/7] tcp: input header length, prediction, and timestamp bugs
Quoted 7 lines · William Allen Simpson
Quoted 6 lines · Andi Kleen
Quoted 4 lines · William Allen Simpson
Don't use output calculated
tp->tcp_header_lenfor input decisions. While the output header is usually the same as the input (same options in both directions), that's a poor assumption. In particular, Sack will be different. Newer options are not guaranteed.
Quoted 5 lines
Quoted 2 lines
Is this a bug fix?
Yes. One of many, all inter-related.
I don't know how much description folks want in the patch "summary", so
A good way to find out is to read some of the existing (newer) changelogs.
Quoted 2 lines
simply used declarative statements that are one-to-one with the order of the patch, but it took me a bit to grok this problem!
It's useful to distingush bug fixes from cleanups from optimizations etc.
Quoted 8 lines
1) unknown options can be stripped out of the header in middleware, see RFC 1122 section 4.2.2.5.
2) new options Cookie Pair and 64-bit Timestamps (defined in patch 7).
3) stripping them leaves a Sack covering 1 segment, which has the exact same word count as 32-bit Timestamps. Boom! All the silly checks
Boom == the code would use sack data as time stamp?
Quoted 5 lines
against the size of the options field (instead of the proper
saw_tstamp) start setting fields based on completely useless data!4) and of course, using the size of the previous output to predict the expected input header size is a poor assumption (to be generous).
Invalid fast path heuristics are probably not bugs in the strict sense, as long as they do not lead to actually incorrect behaviour.
Quoted 6 lines
There are 29+ options these days, not 4 or 5. There are options that are only sent one way. There are options that have different data in different directions.
Yes, it was originally for TCPCT, but fixes a broad spectrum of bugs.
It would be good if you could extract the pure bug fixes from the rest.
Normally there's a "fast path" in patch submission for bug fixes (e.g. might still go in for the current kernel). The other stuff goes in slower.
Quoted 8 lines
Quoted 8 lines
Quoted 3 lines
Stand-alone patch, originally developed for TCPCT.
Normally it would be better to split this into smaller patches that do one thing at a time (typically this requires getting used to patch stack tools like "quilt")
Quoted 5 lines
Quoted 2 lines
But it's not too bad here.
There are small efficiency patches included, but it would be likely impossible to split them from the bug fixes without re-writing the same code over and over again. And I'm doing these patch splits by hand....
That's the problem. Get the right tools. Splitting patches by hand is usually the wrong approach for anything non trivial.
Quoted 4 lines
I did recently learn how to maintain branches that are branches on top of each other, so I've got tcpct1, tcpct2, and tcpct3 for the 3 parts. But it's a pain to keep updated with git fetch, and checkout, and rebase, for each branch.
I personally use quilt for such patchkits, it's much easier.
For git I believe there are several extensions that implement quilt like patch stacking.
But there are different ways to skin a cat and people do it differently.
-Andi
Signature · 1 line
[email protected] -- Speaking for myself only.
Re: [PATCH v4 4/7] tcp: input header length, prediction, and timestamp bugs
Quoted 8 lines · Andi Kleen
Quoted 5 lines · William Allen Simpson
Quoted 2 lines · Andi Kleen
Is this a bug fix?
Yes. One of many, all inter-related.
It's useful to distingush bug fixes from cleanups from optimizations etc.
...
The bug fixes depend on the cleanups.
* 1/7 has the new functions.
* 2/7 has bug fixes that require the new functions.
* 3/7 fixes (and optimizes) the incoming code path, so that
* 4/7 can fix the bugs.
* 5/7 is needed to test the code from part 1.
* 6/7 is the cleanup of parsing (also fixes some bugs introduced by davem's patch in December), so that
* 7/7 can fix the bugs.
I originally tried submitting these as separate individual pairs of patches (in November, December, January), but nobody paid any attention.
PATCH v5 4/7 tcp: input header length, prediction, and timestamp bugs
In reply to William Allen SimpsonFix incorrect header prediction flags documentation.
Relieve register pressure in (the i386) fast path by accessing skb->len directly, instead of carrying a rarely used len parameter.
Eliminate unused len parameters in two other functions.
Don't use output calculated tp->tcp_header_len for input decisions. While the output header is usually the same as the input (same options in both directions), that's a poor assumption. In particular, Sack will be different. Newer options are not guaranteed.
Moreover, in the fast path, that only saved a shift or two. The other efficiencies in this patch more than make up the difference.
Instead, use tp->rx_opt.tstamp_ok to accurately predict header length.
Likewise, use tp->rx_opt.tstamp_ok for received MSS calculations.
Don't use "sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED" to guess that the timestamp is present. This may have been OK in the days with fewer possible options, but various combinations of newer options may yield the same header length. (This bug is in 3 places.)
Instead, use tp->rx_opt.saw_tstamp to determine a timestamp is present.
There's no need to test buffer length against header length, already checked by tcp_v[4,6]_rcv(). Straighten code for minor efficiency gain.
Stand-alone patch, originally developed for TCPCT.
Requires:
net: tcp_header_len_th and tcp_option_len_th
tcp: harmonize tcp_vx_rcv header length assumptions
--- include/linux/tcp.h | 6 ++- include/net/tcp.h | 18 ++++++-- net/ipv4/tcp_input.c | 96 +++++++++++++++++++--------------------------- net/ipv4/tcp_ipv4.c | 4 +- net/ipv4/tcp_minisocks.c | 3 +- net/ipv4/tcp_probe.c | 2 +- net/ipv6/tcp_ipv6.c | 4 +- 7 files changed, 63 insertions(+), 70 deletions(-)
include/linux/tcp.h +5 -1
@@ -301,7 +301,11 @@ struct tcp_sock {/* * Header prediction flags -* 0x5?10 << 16 + snd_wnd in net byte order +* S << 28 + TCP_FLAG_ACK + snd_wnd, in net byte order +* (PSH flag is ignored) +* S is 5 (no options), or 8 (timestamp aligned) +* otherwise, 0 to turn it off -- for instance, when there are +* holes in receive space. */ __be32 pred_flags;
include/net/tcp.h +13 -5
@@ -310,13 +310,11 @@ extern int tcp_ioctl(struct sock *sk,extern int tcp_rcv_state_process (struct sock *sk,struct sk_buff *skb, -struct tcphdr *th, -unsigned len); +struct tcphdr *th);extern int tcp_rcv_established (struct sock *sk,struct sk_buff *skb, -struct tcphdr *th, -unsigned len); +struct tcphdr *th);extern void tcp_rcv_space_adjust (struct sock *sk);
@@ -533,9 +531,19 @@ static inline u32 __tcp_set_rto(const struct tcp_sock *tp)return (tp->srtt >>3 ) + tp->rttvar; } +static inline u8 __tcp_fast_path_header_length (const struct tcp_sock *tp) +{ +return tp->rx_opt.tstamp_ok + ?sizeof (struct tcphdr ) +TCPOLEN_TSTAMP_ALIGNED + :sizeof (struct tcphdr ); +} +static inline void __tcp_fast_path_on (struct tcp_sock *tp,u32 snd_wnd) { - tp->pred_flags =htonl ((tp->tcp_header_len <<26 ) | +/* See linux/tcp.h for pred_flags details. +* 'S' (doff) is 32-bit words, convert from bytes: 26 = 28 - 2. +*/ + tp->pred_flags =htonl ((__tcp_fast_path_header_length (tp) <<26 ) |ntohl (TCP_FLAG_ACK ) | snd_wnd); }
net/ipv4/tcp_input.c +39 -57
169 lines not on this page · raw message
net/ipv4/tcp_ipv4.c +2 -2
@@ -1551,7 +1551,7 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)if (sk->sk_state ==TCP_ESTABLISHED ) {/* Fast path */ TCP_CHECK_TIMER (sk); -if (tcp_rcv_established (sk, skb,tcp_hdr (skb), skb->len)) { +if (tcp_rcv_established (sk, skb,tcp_hdr (skb))) { rsk = sk; goto reset; }
@@ -1578,7 +1578,7 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) }TCP_CHECK_TIMER (sk); -if (tcp_rcv_state_process (sk, skb,tcp_hdr (skb), skb->len)) { +if (tcp_rcv_state_process (sk, skb,tcp_hdr (skb))) { rsk = sk; goto reset; }
net/ipv4/tcp_minisocks.c +1 -2
@@ -718,8 +718,7 @@ int tcp_child_process(struct sock *parent, struct sock *child,int state = child->sk_state;if (!sock_owned_by_user (child)) { - ret =tcp_rcv_state_process (child, skb,tcp_hdr (skb), - skb->len); + ret =tcp_rcv_state_process (child, skb,tcp_hdr (skb));/* Wakeup parent, send SIGIO */ if (state ==TCP_SYN_RECV && child->sk_state != state) parent->sk_data_ready (parent,0 );
net/ipv4/tcp_probe.c +1 -1
@@ -88,7 +88,7 @@ static inline int tcp_probe_avail(void)* Note: arguments must match tcp_rcv_established()! */ static int jtcp_rcv_established (struct sock *sk,struct sk_buff *skb, -struct tcphdr *th,unsigned len) +struct tcphdr *th) {const struct tcp_sock *tp =tcp_sk (sk);const struct inet_sock *inet =inet_sk (sk);
net/ipv6/tcp_ipv6.c +2 -2
@@ -1586,7 +1586,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)if (sk->sk_state ==TCP_ESTABLISHED ) {/* Fast path */ TCP_CHECK_TIMER (sk); -if (tcp_rcv_established (sk, skb,tcp_hdr (skb), skb->len)) +if (tcp_rcv_established (sk, skb,tcp_hdr (skb))) goto reset;TCP_CHECK_TIMER (sk);if (opt_skb)
@@ -1618,7 +1618,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) }TCP_CHECK_TIMER (sk); -if (tcp_rcv_state_process (sk, skb,tcp_hdr (skb), skb->len)) +if (tcp_rcv_state_process (sk, skb,tcp_hdr (skb))) goto reset;TCP_CHECK_TIMER (sk);if (opt_skb)
Signature · 1 line
1.6.3.3
PATCH v3 5/7 TCPCT part 2e: accept SYNACK data
In reply to William Allen SimpsonWhen accompanied by cookie option, Initiator (client) queues incoming SYNACK transaction data.
This is a straightforward re-implementation of an earlier (year-old) patch that no longer applies cleanly, with permission of the original author (Adam Langley). The patch was previously reviewed:
http://thread.gmane.org/gmane.linux.network/102586
This function will also be used in subsequent patches that implement additional features.
Requires:
TCPCT part 1g: Responder Cookie => Initiator
net: tcp_header_len_th and tcp_option_len_th
--- net/ipv4/tcp_input.c | 26 +++++++++++++++++++++++++- 1 files changed, 25 insertions(+), 1 deletions(-)
net/ipv4/tcp_input.c +25 -1
@@ -5395,6 +5395,12 @@ discard:return 0 ; } +/* +* Returns: +* +1 on reset, +* 0 success and/or SYNACK data, +* -1 on discard. +*/ static int tcp_rcv_synsent_state_process (struct sock *sk,struct sk_buff *skb,struct tcphdr *th) {
@@ -5403,6 +5409,7 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,struct tcp_sock *tp =tcp_sk (sk);struct tcp_cookie_values *cvp = tp->cookie_values;int saved_clamp = tp->rx_opt.mss_clamp; +int queued =0 ;tcp_parse_options (skb, &tp->rx_opt, &hash_location,0 );
@@ -5509,6 +5516,7 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, -TCPOLEN_COOKIE_BASE ;int cookie_pair_size = cookie_size + cvp->cookie_desired; +int tcp_header_len =tcp_header_len_th (th);/* A cookie extension option was sent and returned. * Note that each incoming SYNACK replaces the
@@ -5524,6 +5532,19 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, hash_location, cookie_size); cvp->cookie_pair_size = cookie_pair_size; } + + queued = skb->len - tcp_header_len; +if (queued >0 ) { +/* Queue incoming transaction data. */ +__skb_pull (skb, tcp_header_len); +__skb_queue_tail (&sk->sk_receive_queue, skb); +skb_set_owner_r (skb, sk); + sk->sk_data_ready (sk,0 ); + cvp->s_data_in =1 ;/* true */ + tp->rcv_nxt =TCP_SKB_CB (skb)->end_seq; + tp->rcv_wup =TCP_SKB_CB (skb)->end_seq; + tp->copied_seq =TCP_SKB_CB (skb)->seq +1 ; + } }smp_mb ();
@@ -5577,11 +5598,14 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, TCP_DELACK_MAX, TCP_RTO_MAX); discard: -__kfree_skb (skb); +if (queued <=0 ) +__kfree_skb (skb);return 0 ; }else {tcp_send_ack (sk); } +if (queued >0 ) +return 0 ;return -1 ; }
Signature · 1 line
1.6.3.3
PATCH v5 6/7 TCPCT part 2f: cleanup tcp_parse_options
In reply to William Allen SimpsonSplit switch, shift cases to the left, fix most lines beyond column 80.
Prepare (future) error return.
Harmonize initialization in syncookies.
Fix initialization in tcp_minisocks.
Repair net/ipv4/tcp_ipv4.c errors with goto targets, overlooked by David Miller in commit bb5b7c11263dbbe78253cd05945a6bf8f55add8e
Requires:
TCPCT part 1g: Responder Cookie => Initiator
net: tcp_header_len_th and tcp_option_len_th
--- include/net/tcp.h | 3 +- net/ipv4/syncookies.c | 9 ++- net/ipv4/tcp_input.c | 223 ++++++++++++++++++++++++++-------------------- net/ipv4/tcp_ipv4.c | 10 ++- net/ipv4/tcp_minisocks.c | 26 ++++-- net/ipv6/syncookies.c | 9 ++- net/ipv6/tcp_ipv6.c | 6 +- 7 files changed, 172 insertions(+), 114 deletions(-)
include/net/tcp.h +2 -1
@@ -403,7 +403,8 @@ extern int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, size_t len, int nonblock, int flags, int *addr_len); -extern void tcp_parse_options (struct sk_buff *skb, +extern int tcp_parse_options (struct sk_buff *skb, +const struct tcphdr *th,struct tcp_options_received *opt_rx,u8 **hvpp,int estab);
net/ipv4/syncookies.c +6 -3
@@ -255,15 +255,16 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, {struct tcp_options_received tcp_opt;u8 *hash_location; +struct rtable *rt; +struct request_sock *req;struct inet_request_sock *ireq;struct tcp_request_sock *treq;struct tcp_sock *tp =tcp_sk (sk);const struct tcphdr *th =tcp_hdr (skb);__u32 cookie =ntohl (th->ack_seq) -1 ;struct sock *ret = sk; -struct request_sock *req;int mss; -struct rtable *rt; +int parsed;__u8 rcv_wscale;if (!sysctl_tcp_syncookies || !th->ack)
@@ -279,7 +280,9 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,/* check for timestamp cookie support */ memset (&tcp_opt,0 ,sizeof (tcp_opt)); -tcp_parse_options (skb, &tcp_opt, &hash_location,0 ); + parsed =tcp_parse_options (skb, th, &tcp_opt, &hash_location,0 ); +if (parsed <0 ) + goto out;if (tcp_opt.saw_tstamp)cookie_check_timestamp (&tcp_opt);
net/ipv4/tcp_input.c +127 -96
275 lines not on this page · raw message
net/ipv4/tcp_ipv4.c +7 -3
@@ -1215,6 +1215,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) {struct tcp_extend_values tmp_ext;struct tcp_options_received tmp_opt; +int parsed;u8 *hash_location;struct request_sock *req;struct inet_request_sock *ireq;
@@ -1265,7 +1266,10 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)tcp_clear_options (&tmp_opt); tmp_opt.mss_clamp =TCP_MSS_DEFAULT ; tmp_opt.user_mss = tp->rx_opt.user_mss; -tcp_parse_options (skb, &tmp_opt, &hash_location,0 ); + parsed =tcp_parse_options (skb,tcp_hdr (skb), &tmp_opt, &hash_location, +0 ); +if (parsed <0 ) + goto drop_and_free; if (tmp_opt.cookie_plus > 0 && tmp_opt.saw_tstamp &&
@@ -1278,7 +1282,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)int l = tmp_opt.cookie_plus -TCPOLEN_COOKIE_BASE ;if (tcp_cookie_generator (&tmp_ext.cookie_bakery[0 ]) !=0 ) - goto drop_and_release; + goto drop_and_free;/* Secret recipe starts with IP addresses */ *mess++ ^= daddr;
@@ -1299,7 +1303,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) tmp_ext.cookie_out_never =1 ;/* true */ tmp_ext.cookie_plus =0 ; }else { - goto drop_and_release; + goto drop_and_free; } tmp_ext.cookie_in_always = tp->rx_opt.cookie_in_always;
net/ipv4/tcp_minisocks.c +19 -7
@@ -95,15 +95,21 @@ tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb,struct tcp_timewait_sock *tcptw =tcp_twsk ((struct sock *)tw);int paws_reject =0 ; - tmp_opt.saw_tstamp =0 ; +/* Fast check for options, compare doff directly to constant value. */ if (th->doff > (sizeof (*th) >>2 ) && tcptw->tw_ts_recent_stamp) { -tcp_parse_options (skb, &tmp_opt, &hash_location,0 ); +int parsed =tcp_parse_options (skb, th, &tmp_opt, + &hash_location,0 ); -if (tmp_opt.saw_tstamp) { +if (parsed <0 ) { + paws_reject =1 ;/* true */ + }else if (tmp_opt.saw_tstamp) { tmp_opt.ts_recent = tcptw->tw_ts_recent; tmp_opt.ts_recent_stamp = tcptw->tw_ts_recent_stamp; paws_reject =tcp_paws_reject (&tmp_opt, th->rst); } + }else { +/* otherwise initialized by tcp_parse_options() */ + tmp_opt.saw_tstamp =0 ;/* false */ }if (tw->tw_substate ==TCP_FIN_WAIT2 ) {
@@ -526,11 +532,14 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,__be32 flg =tcp_flag_word (th) & (TCP_FLAG_RST |TCP_FLAG_SYN |TCP_FLAG_ACK );int paws_reject =0 ; - tmp_opt.saw_tstamp =0 ; -if (th->doff > (sizeof (struct tcphdr )>>2 )) { -tcp_parse_options (skb, &tmp_opt, &hash_location,0 ); +/* Fast check for options, compare doff directly to constant value. */ +if (th->doff > (sizeof (*th) >>2 )) { +int parsed =tcp_parse_options (skb, th, &tmp_opt, + &hash_location,0 ); -if (tmp_opt.saw_tstamp) { +if (parsed <0 ) { + paws_reject =1 ;/* true */ + }else if (tmp_opt.saw_tstamp) { tmp_opt.ts_recent = req->ts_recent;/* We do not store true stamp, but it is not required, * it can be estimated (approximately)
@@ -539,6 +548,9 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, tmp_opt.ts_recent_stamp =get_seconds () - ((TCP_TIMEOUT_INIT /HZ )<<req->retrans); paws_reject =tcp_paws_reject (&tmp_opt, th->rst); } + }else { +/* otherwise initialized by tcp_parse_options() */ + tmp_opt.saw_tstamp =0 ;/* false */ }/* Check for pure retransmitted SYN. */
net/ipv6/syncookies.c +6 -3
@@ -161,6 +161,8 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) {struct tcp_options_received tcp_opt;u8 *hash_location; +struct dst_entry *dst; +struct request_sock *req;struct inet_request_sock *ireq;struct inet6_request_sock *ireq6;struct tcp_request_sock *treq;
@@ -169,9 +171,8 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)const struct tcphdr *th =tcp_hdr (skb);__u32 cookie =ntohl (th->ack_seq) -1 ;struct sock *ret = sk; -struct request_sock *req;int mss; -struct dst_entry *dst; +int parsed;__u8 rcv_wscale;if (!sysctl_tcp_syncookies || !th->ack)
@@ -187,7 +188,9 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)/* check for timestamp cookie support */ memset (&tcp_opt,0 ,sizeof (tcp_opt)); -tcp_parse_options (skb, &tcp_opt, &hash_location,0 ); + parsed =tcp_parse_options (skb, th, &tcp_opt, &hash_location,0 ); +if (parsed <0 ) + goto out;if (tcp_opt.saw_tstamp)cookie_check_timestamp (&tcp_opt);
net/ipv6/tcp_ipv6.c +5 -1
@@ -1164,6 +1164,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) {struct tcp_extend_values tmp_ext;struct tcp_options_received tmp_opt; +int parsed;u8 *hash_location;struct request_sock *req;struct inet6_request_sock *treq;
@@ -1207,7 +1208,10 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)tcp_clear_options (&tmp_opt); tmp_opt.mss_clamp =IPV6_MIN_MTU -sizeof (struct tcphdr ) -sizeof (struct ipv6hdr ); tmp_opt.user_mss = tp->rx_opt.user_mss; -tcp_parse_options (skb, &tmp_opt, &hash_location,0 ); + parsed =tcp_parse_options (skb,tcp_hdr (skb), &tmp_opt, &hash_location, +0 ); +if (parsed <0 ) + goto drop_and_free; if (tmp_opt.cookie_plus > 0 && tmp_opt.saw_tstamp &&
Signature · 1 line
1.6.3.3
PATCH v7 7/7 TCPCT part 2g: parse cookie pair and 64-bit timestamp
In reply to William Allen SimpsonEvery bit is sacred. Use as few bits as possible in tcp_options_received. Group related timestamp flag bits for cache line memory efficiency.
Fix #define spacing for TCP options.
Define and parse 64-bit timestamp extended option (and minor cleanup). However, only 32-bits are used at this time (permitted by specification).
Parse cookie pair extended option (previously defined).
Handle header extension.
Fix initialization in tcp_minisocks.
Requires:
net: tcp_header_len_th and tcp_option_len_th
TCPCT part 2f: cleanup tcp_parse_options
--- include/linux/tcp.h | 12 ++++- include/net/tcp.h | 45 +++++++++-------- net/ipv4/tcp_input.c | 123 ++++++++++++++++++++++++++++++++++++++++++---- net/ipv4/tcp_minisocks.c | 6 ++ 4 files changed, 152 insertions(+), 34 deletions(-)
include/linux/tcp.h +10 -2
@@ -247,7 +247,7 @@ struct tcp_options_received {u32 ts_recent;/* Time stamp to echo next */ u32 rcv_tsval;/* Time stamp value */ u32 rcv_tsecr;/* Time stamp echo reply */ - u16 saw_tstamp : 1, /* Saw TIMESTAMP on last packet */ + u16 tstamp64_ok:1, /* Verified with cookie pair */ tstamp_ok : 1, /* TIMESTAMP seen on SYN packet */ dsack : 1, /* D-SACK is scheduled */ wscale_ok : 1, /* Wscale seen on SYN packet */
@@ -260,13 +260,21 @@ struct tcp_options_received {u8 num_sacks;/* Number of SACK blocks */ u16 user_mss;/* mss requested by user in ioctl */ u16 mss_clamp;/* Maximal mss, negotiated at connection setup */ + +/* When the options are extended beyond the standard 40 bytes, +* this holds the additional data offset (up to 1,020 bytes). +*/ +u8 extended;/* in 32-bit words */ + u8 saw_tstamp64:1, /* 64-bit TIMESTAMP seen on last packet */ + saw_tstamp:1, /* TIMESTAMP seen on last packet */ + __unused:6; };static inline void tcp_clear_options (struct tcp_options_received *rx_opt) { + rx_opt->tstamp64_ok =0 ; rx_opt->tstamp_ok = rx_opt->sack_ok =0 ; rx_opt->wscale_ok = rx_opt->snd_wscale =0 ; - rx_opt->cookie_plus =0 ; }/* This is the max number of SACKS that we'll generate and process. It's safe
include/net/tcp.h +23 -22
62 lines not on this page · raw message
net/ipv4/tcp_input.c +113 -10
202 lines not on this page · raw message
net/ipv4/tcp_minisocks.c +6 -0
@@ -109,6 +109,9 @@ tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb, } }else {/* otherwise initialized by tcp_parse_options() */ + tmp_opt.cookie_plus =0 ; + tmp_opt.extended =0 ; + tmp_opt.saw_tstamp64 =0 ;/* false */ tmp_opt.saw_tstamp =0 ;/* false */ }
@@ -550,6 +553,9 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, } }else {/* otherwise initialized by tcp_parse_options() */ + tmp_opt.cookie_plus =0 ; + tmp_opt.extended =0 ; + tmp_opt.saw_tstamp64 =0 ;/* false */ tmp_opt.saw_tstamp =0 ;/* false */ }
Signature · 1 line
1.6.3.3