@@ -28,8 +28,11 @@ struct mptcp_pernet {
2828#endif
2929
3030 unsigned int add_addr_timeout ;
31+ unsigned int blackhole_timeout ;
3132 unsigned int close_timeout ;
3233 unsigned int stale_loss_cnt ;
34+ atomic_t active_disable_times ;
35+ unsigned long active_disable_stamp ;
3336 u8 mptcp_enabled ;
3437 u8 checksum_enabled ;
3538 u8 allow_join_initial_addr_port ;
@@ -88,6 +91,8 @@ static void mptcp_pernet_set_defaults(struct mptcp_pernet *pernet)
8891{
8992 pernet -> mptcp_enabled = 1 ;
9093 pernet -> add_addr_timeout = TCP_RTO_MAX ;
94+ pernet -> blackhole_timeout = 3600 ;
95+ atomic_set (& pernet -> active_disable_times , 0 );
9196 pernet -> close_timeout = TCP_TIMEWAIT_LEN ;
9297 pernet -> checksum_enabled = 0 ;
9398 pernet -> allow_join_initial_addr_port = 1 ;
@@ -152,6 +157,20 @@ static int proc_available_schedulers(const struct ctl_table *ctl,
152157 return ret ;
153158}
154159
160+ static int proc_blackhole_detect_timeout (const struct ctl_table * table ,
161+ int write , void * buffer , size_t * lenp ,
162+ loff_t * ppos )
163+ {
164+ struct mptcp_pernet * pernet = mptcp_get_pernet (current -> nsproxy -> net_ns );
165+ int ret ;
166+
167+ ret = proc_dointvec_minmax (table , write , buffer , lenp , ppos );
168+ if (write && ret == 0 )
169+ atomic_set (& pernet -> active_disable_times , 0 );
170+
171+ return ret ;
172+ }
173+
155174static struct ctl_table mptcp_sysctl_table [] = {
156175 {
157176 .procname = "enabled" ,
@@ -218,6 +237,13 @@ static struct ctl_table mptcp_sysctl_table[] = {
218237 .mode = 0644 ,
219238 .proc_handler = proc_dointvec_jiffies ,
220239 },
240+ {
241+ .procname = "blackhole_timeout" ,
242+ .maxlen = sizeof (unsigned int ),
243+ .mode = 0644 ,
244+ .proc_handler = proc_blackhole_detect_timeout ,
245+ .extra1 = SYSCTL_ZERO ,
246+ },
221247};
222248
223249static int mptcp_pernet_new_table (struct net * net , struct mptcp_pernet * pernet )
@@ -241,6 +267,7 @@ static int mptcp_pernet_new_table(struct net *net, struct mptcp_pernet *pernet)
241267 table [6 ].data = & pernet -> scheduler ;
242268 /* table[7] is for available_schedulers which is read-only info */
243269 table [8 ].data = & pernet -> close_timeout ;
270+ table [9 ].data = & pernet -> blackhole_timeout ;
244271
245272 hdr = register_net_sysctl_sz (net , MPTCP_SYSCTL_PATH , table ,
246273 ARRAY_SIZE (mptcp_sysctl_table ));
@@ -278,6 +305,88 @@ static void mptcp_pernet_del_table(struct mptcp_pernet *pernet) {}
278305
279306#endif /* CONFIG_SYSCTL */
280307
308+ /* The following code block is to deal with middle box issues with MPTCP,
309+ * similar to what is done with TFO.
310+ * The proposed solution is to disable active MPTCP globally when SYN+MPC are
311+ * dropped, while SYN without MPC aren't. In this case, active side MPTCP is
312+ * disabled globally for 1hr at first. Then if it happens again, it is disabled
313+ * for 2h, then 4h, 8h, ...
314+ * The timeout is reset back to 1hr when a successful active MPTCP connection is
315+ * fully established.
316+ */
317+
318+ /* Disable active MPTCP and record current jiffies and active_disable_times */
319+ void mptcp_active_disable (struct sock * sk )
320+ {
321+ struct net * net = sock_net (sk );
322+ struct mptcp_pernet * pernet ;
323+
324+ pernet = mptcp_get_pernet (net );
325+
326+ if (!READ_ONCE (pernet -> blackhole_timeout ))
327+ return ;
328+
329+ /* Paired with READ_ONCE() in mptcp_active_should_disable() */
330+ WRITE_ONCE (pernet -> active_disable_stamp , jiffies );
331+
332+ /* Paired with smp_rmb() in mptcp_active_should_disable().
333+ * We want pernet->active_disable_stamp to be updated first.
334+ */
335+ smp_mb__before_atomic ();
336+ atomic_inc (& pernet -> active_disable_times );
337+
338+ MPTCP_INC_STATS (net , MPTCP_MIB_BLACKHOLE );
339+ }
340+
341+ /* Calculate timeout for MPTCP active disable
342+ * Return true if we are still in the active MPTCP disable period
343+ * Return false if timeout already expired and we should use active MPTCP
344+ */
345+ bool mptcp_active_should_disable (struct sock * ssk )
346+ {
347+ struct net * net = sock_net (ssk );
348+ unsigned int blackhole_timeout ;
349+ struct mptcp_pernet * pernet ;
350+ unsigned long timeout ;
351+ int disable_times ;
352+ int multiplier ;
353+
354+ pernet = mptcp_get_pernet (net );
355+ blackhole_timeout = READ_ONCE (pernet -> blackhole_timeout );
356+
357+ if (!blackhole_timeout )
358+ return false;
359+
360+ disable_times = atomic_read (& pernet -> active_disable_times );
361+ if (!disable_times )
362+ return false;
363+
364+ /* Paired with smp_mb__before_atomic() in mptcp_active_disable() */
365+ smp_rmb ();
366+
367+ /* Limit timeout to max: 2^6 * initial timeout */
368+ multiplier = 1 << min (disable_times - 1 , 6 );
369+
370+ /* Paired with the WRITE_ONCE() in mptcp_active_disable(). */
371+ timeout = READ_ONCE (pernet -> active_disable_stamp ) +
372+ multiplier * blackhole_timeout * HZ ;
373+
374+ return time_before (jiffies , timeout );
375+ }
376+
377+ /* Enable active MPTCP and reset active_disable_times if needed */
378+ void mptcp_active_enable (struct sock * sk )
379+ {
380+ struct mptcp_pernet * pernet = mptcp_get_pernet (sock_net (sk ));
381+
382+ if (atomic_read (& pernet -> active_disable_times )) {
383+ struct dst_entry * dst = sk_dst_get (sk );
384+
385+ if (dst && dst -> dev && (dst -> dev -> flags & IFF_LOOPBACK ))
386+ atomic_set (& pernet -> active_disable_times , 0 );
387+ }
388+ }
389+
281390/* Check the number of retransmissions, and fallback to TCP if needed */
282391void mptcp_active_detect_blackhole (struct sock * ssk , bool expired )
283392{
@@ -290,10 +399,14 @@ void mptcp_active_detect_blackhole(struct sock *ssk, bool expired)
290399 timeouts = inet_csk (ssk )-> icsk_retransmits ;
291400 subflow = mptcp_subflow_ctx (ssk );
292401
293- if (subflow -> request_mptcp && ssk -> sk_state == TCP_SYN_SENT &&
294- (timeouts == 2 || (timeouts < 2 && expired ))) {
295- MPTCP_INC_STATS (sock_net (ssk ), MPTCP_MIB_MPCAPABLEACTIVEDROP );
296- mptcp_subflow_early_fallback (mptcp_sk (subflow -> conn ), subflow );
402+ if (subflow -> request_mptcp && ssk -> sk_state == TCP_SYN_SENT ) {
403+ if (timeouts == 2 || (timeouts < 2 && expired )) {
404+ MPTCP_INC_STATS (sock_net (ssk ), MPTCP_MIB_MPCAPABLEACTIVEDROP );
405+ subflow -> mpc_drop = 1 ;
406+ mptcp_subflow_early_fallback (mptcp_sk (subflow -> conn ), subflow );
407+ } else {
408+ subflow -> mpc_drop = 0 ;
409+ }
297410 }
298411}
299412
0 commit comments