Skip to content

Commit 762c400

Browse files
Julian Anastasovummakynes
authored andcommitted
ipvs: drop conn templates under attack
Before now, connection templates were ignored by the random dropentry procedure. But Michal Koutný suggests that we should add exception for connections under SYN attack. He provided patch that implements it for TCP: <quote> IPVS includes protection against filling the ip_vs_conn_tab by dropping 1/32 of feasible entries every second. The template entries (for persistent services) are never directly deleted by this mechanism but when a picked TCP connection entry is being dropped (1), the respective template entry is dropped too (realized by expiring 60 seconds after the connection entry being dropped). There is another mechanism that removes connection entries when they time out (2), in this case the associated template entry is not deleted. Under SYN flood template entries would accumulate (due to their entry longer timeout). The accumulation takes place also with drop_entry being enabled. Roughly 15% ((31/32)^60) of SYN_RECV connections survive the dropping mechanism (1) and are removed by the timeout mechanism (2)(defaults to 60 seconds for SYN_RECV), thus template entries would still accumulate. The patch ensures that when a connection entry times out, we also remove the template entry from the table. To prevent breaking persistent services (since the connection may time out in already established state) we add a new entry flag to protect templates what spawned at least one established TCP connection. </quote> We already added ASSURED flag for the templates in previous patch, so that we can use it now to decide which connection templates should be dropped under attack. But we also have some cases that need special handling. We modify the dropentry procedure as follows: - Linux timers currently use LIFO ordering but we can not rely on this to drop controlling connections. So, set cp->timeout to 0 to indicate that connection was dropped and that on expiration we should try to drop our controlling connections. As result, we can now avoid the ip_vs_conn_expire_now call. - move the cp->n_control check above, so that it avoids restarting the timer for controlling connections when not needed. - drop unassured connection templates here if they are not referred by any connections. On connection expiration: if connection was dropped (cp->timeout=0) try to drop our controlling connection except if it is a template in assured state. In ip_vs_conn_flush change order of ip_vs_conn_expire_now calls according to the LIFO timer expiration order. It should work faster for controlling connections with single controlled one. Suggested-by: Michal Koutný <mkoutny@suse.com> Signed-off-by: Julian Anastasov <ja@ssi.bg> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
1 parent 2754114 commit 762c400

File tree

1 file changed

+39
-20
lines changed

1 file changed

+39
-20
lines changed

net/netfilter/ipvs/ip_vs_conn.c

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -825,12 +825,23 @@ static void ip_vs_conn_expire(struct timer_list *t)
825825

826826
/* Unlink conn if not referenced anymore */
827827
if (likely(ip_vs_conn_unlink(cp))) {
828+
struct ip_vs_conn *ct = cp->control;
829+
828830
/* delete the timer if it is activated by other users */
829831
del_timer(&cp->timer);
830832

831833
/* does anybody control me? */
832-
if (cp->control)
834+
if (ct) {
833835
ip_vs_control_del(cp);
836+
/* Drop CTL or non-assured TPL if not used anymore */
837+
if (!cp->timeout && !atomic_read(&ct->n_control) &&
838+
(!(ct->flags & IP_VS_CONN_F_TEMPLATE) ||
839+
!(ct->state & IP_VS_CTPL_S_ASSURED))) {
840+
IP_VS_DBG(4, "drop controlling connection\n");
841+
ct->timeout = 0;
842+
ip_vs_conn_expire_now(ct);
843+
}
844+
}
834845

835846
if ((cp->flags & IP_VS_CONN_F_NFCT) &&
836847
!(cp->flags & IP_VS_CONN_F_ONE_PACKET)) {
@@ -872,6 +883,10 @@ static void ip_vs_conn_expire(struct timer_list *t)
872883

873884
/* Modify timer, so that it expires as soon as possible.
874885
* Can be called without reference only if under RCU lock.
886+
* We can have such chain of conns linked with ->control: DATA->CTL->TPL
887+
* - DATA (eg. FTP) and TPL (persistence) can be present depending on setup
888+
* - cp->timeout=0 indicates all conns from chain should be dropped but
889+
* TPL is not dropped if in assured state
875890
*/
876891
void ip_vs_conn_expire_now(struct ip_vs_conn *cp)
877892
{
@@ -1197,8 +1212,11 @@ static const struct seq_operations ip_vs_conn_sync_seq_ops = {
11971212
#endif
11981213

11991214

1200-
/*
1201-
* Randomly drop connection entries before running out of memory
1215+
/* Randomly drop connection entries before running out of memory
1216+
* Can be used for DATA and CTL conns. For TPL conns there are exceptions:
1217+
* - traffic for services in OPS mode increases ct->in_pkts, so it is supported
1218+
* - traffic for services not in OPS mode does not increase ct->in_pkts in
1219+
* all cases, so it is not supported
12021220
*/
12031221
static inline int todrop_entry(struct ip_vs_conn *cp)
12041222
{
@@ -1242,7 +1260,7 @@ static inline bool ip_vs_conn_ops_mode(struct ip_vs_conn *cp)
12421260
void ip_vs_random_dropentry(struct netns_ipvs *ipvs)
12431261
{
12441262
int idx;
1245-
struct ip_vs_conn *cp, *cp_c;
1263+
struct ip_vs_conn *cp;
12461264

12471265
rcu_read_lock();
12481266
/*
@@ -1254,13 +1272,15 @@ void ip_vs_random_dropentry(struct netns_ipvs *ipvs)
12541272
hlist_for_each_entry_rcu(cp, &ip_vs_conn_tab[hash], c_list) {
12551273
if (cp->ipvs != ipvs)
12561274
continue;
1275+
if (atomic_read(&cp->n_control))
1276+
continue;
12571277
if (cp->flags & IP_VS_CONN_F_TEMPLATE) {
1258-
if (atomic_read(&cp->n_control) ||
1259-
!ip_vs_conn_ops_mode(cp))
1260-
continue;
1261-
else
1262-
/* connection template of OPS */
1278+
/* connection template of OPS */
1279+
if (ip_vs_conn_ops_mode(cp))
12631280
goto try_drop;
1281+
if (!(cp->state & IP_VS_CTPL_S_ASSURED))
1282+
goto drop;
1283+
continue;
12641284
}
12651285
if (cp->protocol == IPPROTO_TCP) {
12661286
switch(cp->state) {
@@ -1294,15 +1314,10 @@ void ip_vs_random_dropentry(struct netns_ipvs *ipvs)
12941314
continue;
12951315
}
12961316

1297-
IP_VS_DBG(4, "del connection\n");
1317+
drop:
1318+
IP_VS_DBG(4, "drop connection\n");
1319+
cp->timeout = 0;
12981320
ip_vs_conn_expire_now(cp);
1299-
cp_c = cp->control;
1300-
/* cp->control is valid only with reference to cp */
1301-
if (cp_c && __ip_vs_conn_get(cp)) {
1302-
IP_VS_DBG(4, "del conn template\n");
1303-
ip_vs_conn_expire_now(cp_c);
1304-
__ip_vs_conn_put(cp);
1305-
}
13061321
}
13071322
cond_resched_rcu();
13081323
}
@@ -1325,15 +1340,19 @@ static void ip_vs_conn_flush(struct netns_ipvs *ipvs)
13251340
hlist_for_each_entry_rcu(cp, &ip_vs_conn_tab[idx], c_list) {
13261341
if (cp->ipvs != ipvs)
13271342
continue;
1328-
IP_VS_DBG(4, "del connection\n");
1329-
ip_vs_conn_expire_now(cp);
1343+
/* As timers are expired in LIFO order, restart
1344+
* the timer of controlling connection first, so
1345+
* that it is expired after us.
1346+
*/
13301347
cp_c = cp->control;
13311348
/* cp->control is valid only with reference to cp */
13321349
if (cp_c && __ip_vs_conn_get(cp)) {
1333-
IP_VS_DBG(4, "del conn template\n");
1350+
IP_VS_DBG(4, "del controlling connection\n");
13341351
ip_vs_conn_expire_now(cp_c);
13351352
__ip_vs_conn_put(cp);
13361353
}
1354+
IP_VS_DBG(4, "del connection\n");
1355+
ip_vs_conn_expire_now(cp);
13371356
}
13381357
cond_resched_rcu();
13391358
}

0 commit comments

Comments
 (0)