aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Quetchenbach <virtualphtn@gmail.com>2008-09-21 03:21:51 -0400
committerDavid S. Miller <davem@davemloft.net>2008-09-21 03:21:51 -0400
commitf5fff5dc8a7a3f395b0525c02ba92c95d42b7390 (patch)
tree2077107cbf6011c826937f56a008bb3d5a0306a2
parent6067804047b64dde89f4f133fc7eba48ee44107d (diff)
tcp: advertise MSS requested by user
I'm trying to use the TCP_MAXSEG option to setsockopt() to set the MSS for both sides of a bidirectional connection. man tcp says: "If this option is set before connection establishment, it also changes the MSS value announced to the other end in the initial packet." However, the kernel only uses the MTU/route cache to set the advertised MSS. That means if I set the MSS to, say, 500 before calling connect(), I will send at most 500-byte packets, but I will still receive 1500-byte packets in reply. This is a bug, either in the kernel or the documentation. This patch (applies to latest net-2.6) reduces the advertised value to that requested by the user as long as setsockopt() is called before connect() or accept(). This seems like the behavior that one would expect as well as that which is documented. I've tried to make sure that things that depend on the advertised MSS are set correctly. Signed-off-by: Tom Quetchenbach <virtualphtn@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/ipv4/tcp_ipv4.c4
-rw-r--r--net/ipv4/tcp_output.c13
2 files changed, 14 insertions, 3 deletions
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 3dfbc21e555a..44aef1c1f373 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1364,6 +1364,10 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
1364 tcp_mtup_init(newsk); 1364 tcp_mtup_init(newsk);
1365 tcp_sync_mss(newsk, dst_mtu(dst)); 1365 tcp_sync_mss(newsk, dst_mtu(dst));
1366 newtp->advmss = dst_metric(dst, RTAX_ADVMSS); 1366 newtp->advmss = dst_metric(dst, RTAX_ADVMSS);
1367 if (tcp_sk(sk)->rx_opt.user_mss &&
1368 tcp_sk(sk)->rx_opt.user_mss < newtp->advmss)
1369 newtp->advmss = tcp_sk(sk)->rx_opt.user_mss;
1370
1367 tcp_initialize_rcv_mss(newsk); 1371 tcp_initialize_rcv_mss(newsk);
1368 1372
1369#ifdef CONFIG_TCP_MD5SIG 1373#ifdef CONFIG_TCP_MD5SIG
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 8f9793a37b61..c3d58ee3e16f 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2232,6 +2232,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
2232 struct sk_buff *skb; 2232 struct sk_buff *skb;
2233 struct tcp_md5sig_key *md5; 2233 struct tcp_md5sig_key *md5;
2234 __u8 *md5_hash_location; 2234 __u8 *md5_hash_location;
2235 int mss;
2235 2236
2236 skb = sock_wmalloc(sk, MAX_TCP_HEADER + 15, 1, GFP_ATOMIC); 2237 skb = sock_wmalloc(sk, MAX_TCP_HEADER + 15, 1, GFP_ATOMIC);
2237 if (skb == NULL) 2238 if (skb == NULL)
@@ -2242,13 +2243,17 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
2242 2243
2243 skb->dst = dst_clone(dst); 2244 skb->dst = dst_clone(dst);
2244 2245
2246 mss = dst_metric(dst, RTAX_ADVMSS);
2247 if (tp->rx_opt.user_mss && tp->rx_opt.user_mss < mss)
2248 mss = tp->rx_opt.user_mss;
2249
2245 if (req->rcv_wnd == 0) { /* ignored for retransmitted syns */ 2250 if (req->rcv_wnd == 0) { /* ignored for retransmitted syns */
2246 __u8 rcv_wscale; 2251 __u8 rcv_wscale;
2247 /* Set this up on the first call only */ 2252 /* Set this up on the first call only */
2248 req->window_clamp = tp->window_clamp ? : dst_metric(dst, RTAX_WINDOW); 2253 req->window_clamp = tp->window_clamp ? : dst_metric(dst, RTAX_WINDOW);
2249 /* tcp_full_space because it is guaranteed to be the first packet */ 2254 /* tcp_full_space because it is guaranteed to be the first packet */
2250 tcp_select_initial_window(tcp_full_space(sk), 2255 tcp_select_initial_window(tcp_full_space(sk),
2251 dst_metric(dst, RTAX_ADVMSS) - (ireq->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0), 2256 mss - (ireq->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0),
2252 &req->rcv_wnd, 2257 &req->rcv_wnd,
2253 &req->window_clamp, 2258 &req->window_clamp,
2254 ireq->wscale_ok, 2259 ireq->wscale_ok,
@@ -2258,8 +2263,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
2258 2263
2259 memset(&opts, 0, sizeof(opts)); 2264 memset(&opts, 0, sizeof(opts));
2260 TCP_SKB_CB(skb)->when = tcp_time_stamp; 2265 TCP_SKB_CB(skb)->when = tcp_time_stamp;
2261 tcp_header_size = tcp_synack_options(sk, req, 2266 tcp_header_size = tcp_synack_options(sk, req, mss,
2262 dst_metric(dst, RTAX_ADVMSS),
2263 skb, &opts, &md5) + 2267 skb, &opts, &md5) +
2264 sizeof(struct tcphdr); 2268 sizeof(struct tcphdr);
2265 2269
@@ -2333,6 +2337,9 @@ static void tcp_connect_init(struct sock *sk)
2333 if (!tp->window_clamp) 2337 if (!tp->window_clamp)
2334 tp->window_clamp = dst_metric(dst, RTAX_WINDOW); 2338 tp->window_clamp = dst_metric(dst, RTAX_WINDOW);
2335 tp->advmss = dst_metric(dst, RTAX_ADVMSS); 2339 tp->advmss = dst_metric(dst, RTAX_ADVMSS);
2340 if (tp->rx_opt.user_mss && tp->rx_opt.user_mss < tp->advmss)
2341 tp->advmss = tp->rx_opt.user_mss;
2342
2336 tcp_initialize_rcv_mss(sk); 2343 tcp_initialize_rcv_mss(sk);
2337 2344
2338 tcp_select_initial_window(tcp_full_space(sk), 2345 tcp_select_initial_window(tcp_full_space(sk),