netfilter: conntrack: allow sctp hearbeat after connection re-use
If an sctp connection gets re-used, heartbeats are flagged as invalid because their vtag doesn't match. Handle this in a similar way as TCP conntrack when it suspects that the endpoints and conntrack are out-of-sync. When a HEARTBEAT request fails its vtag validation, flag this in the conntrack state and accept the packet. When a HEARTBEAT_ACK is received with an invalid vtag in the reverse direction after we allowed such a HEARTBEAT through, assume we are out-of-sync and re-set the vtag info. v2: remove left-over snippet from an older incarnation that moved new_state/old_state assignments, thats not needed so keep that as-is. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
parent
cf96d97738
commit
cc5453a5b7
|
@ -9,6 +9,8 @@ struct ip_ct_sctp {
|
||||||
enum sctp_conntrack state;
|
enum sctp_conntrack state;
|
||||||
|
|
||||||
__be32 vtag[IP_CT_DIR_MAX];
|
__be32 vtag[IP_CT_DIR_MAX];
|
||||||
|
u8 last_dir;
|
||||||
|
u8 flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _NF_CONNTRACK_SCTP_H */
|
#endif /* _NF_CONNTRACK_SCTP_H */
|
||||||
|
|
|
@ -62,6 +62,8 @@ static const unsigned int sctp_timeouts[SCTP_CONNTRACK_MAX] = {
|
||||||
[SCTP_CONNTRACK_HEARTBEAT_ACKED] = 210 SECS,
|
[SCTP_CONNTRACK_HEARTBEAT_ACKED] = 210 SECS,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define SCTP_FLAG_HEARTBEAT_VTAG_FAILED 1
|
||||||
|
|
||||||
#define sNO SCTP_CONNTRACK_NONE
|
#define sNO SCTP_CONNTRACK_NONE
|
||||||
#define sCL SCTP_CONNTRACK_CLOSED
|
#define sCL SCTP_CONNTRACK_CLOSED
|
||||||
#define sCW SCTP_CONNTRACK_COOKIE_WAIT
|
#define sCW SCTP_CONNTRACK_COOKIE_WAIT
|
||||||
|
@ -369,6 +371,7 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct,
|
||||||
u_int32_t offset, count;
|
u_int32_t offset, count;
|
||||||
unsigned int *timeouts;
|
unsigned int *timeouts;
|
||||||
unsigned long map[256 / sizeof(unsigned long)] = { 0 };
|
unsigned long map[256 / sizeof(unsigned long)] = { 0 };
|
||||||
|
bool ignore = false;
|
||||||
|
|
||||||
if (sctp_error(skb, dataoff, state))
|
if (sctp_error(skb, dataoff, state))
|
||||||
return -NF_ACCEPT;
|
return -NF_ACCEPT;
|
||||||
|
@ -427,15 +430,39 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct,
|
||||||
/* Sec 8.5.1 (D) */
|
/* Sec 8.5.1 (D) */
|
||||||
if (sh->vtag != ct->proto.sctp.vtag[dir])
|
if (sh->vtag != ct->proto.sctp.vtag[dir])
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
} else if (sch->type == SCTP_CID_HEARTBEAT ||
|
} else if (sch->type == SCTP_CID_HEARTBEAT) {
|
||||||
sch->type == SCTP_CID_HEARTBEAT_ACK) {
|
if (ct->proto.sctp.vtag[dir] == 0) {
|
||||||
|
pr_debug("Setting %d vtag %x for dir %d\n", sch->type, sh->vtag, dir);
|
||||||
|
ct->proto.sctp.vtag[dir] = sh->vtag;
|
||||||
|
} else if (sh->vtag != ct->proto.sctp.vtag[dir]) {
|
||||||
|
if (test_bit(SCTP_CID_DATA, map) || ignore)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
ct->proto.sctp.flags |= SCTP_FLAG_HEARTBEAT_VTAG_FAILED;
|
||||||
|
ct->proto.sctp.last_dir = dir;
|
||||||
|
ignore = true;
|
||||||
|
continue;
|
||||||
|
} else if (ct->proto.sctp.flags & SCTP_FLAG_HEARTBEAT_VTAG_FAILED) {
|
||||||
|
ct->proto.sctp.flags &= ~SCTP_FLAG_HEARTBEAT_VTAG_FAILED;
|
||||||
|
}
|
||||||
|
} else if (sch->type == SCTP_CID_HEARTBEAT_ACK) {
|
||||||
if (ct->proto.sctp.vtag[dir] == 0) {
|
if (ct->proto.sctp.vtag[dir] == 0) {
|
||||||
pr_debug("Setting vtag %x for dir %d\n",
|
pr_debug("Setting vtag %x for dir %d\n",
|
||||||
sh->vtag, dir);
|
sh->vtag, dir);
|
||||||
ct->proto.sctp.vtag[dir] = sh->vtag;
|
ct->proto.sctp.vtag[dir] = sh->vtag;
|
||||||
} else if (sh->vtag != ct->proto.sctp.vtag[dir]) {
|
} else if (sh->vtag != ct->proto.sctp.vtag[dir]) {
|
||||||
pr_debug("Verification tag check failed\n");
|
if (test_bit(SCTP_CID_DATA, map) || ignore)
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
|
|
||||||
|
if ((ct->proto.sctp.flags & SCTP_FLAG_HEARTBEAT_VTAG_FAILED) == 0 ||
|
||||||
|
ct->proto.sctp.last_dir == dir)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
ct->proto.sctp.flags &= ~SCTP_FLAG_HEARTBEAT_VTAG_FAILED;
|
||||||
|
ct->proto.sctp.vtag[dir] = sh->vtag;
|
||||||
|
ct->proto.sctp.vtag[!dir] = 0;
|
||||||
|
} else if (ct->proto.sctp.flags & SCTP_FLAG_HEARTBEAT_VTAG_FAILED) {
|
||||||
|
ct->proto.sctp.flags &= ~SCTP_FLAG_HEARTBEAT_VTAG_FAILED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -470,6 +497,10 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct,
|
||||||
}
|
}
|
||||||
spin_unlock_bh(&ct->lock);
|
spin_unlock_bh(&ct->lock);
|
||||||
|
|
||||||
|
/* allow but do not refresh timeout */
|
||||||
|
if (ignore)
|
||||||
|
return NF_ACCEPT;
|
||||||
|
|
||||||
timeouts = nf_ct_timeout_lookup(ct);
|
timeouts = nf_ct_timeout_lookup(ct);
|
||||||
if (!timeouts)
|
if (!timeouts)
|
||||||
timeouts = nf_sctp_pernet(nf_ct_net(ct))->timeouts;
|
timeouts = nf_sctp_pernet(nf_ct_net(ct))->timeouts;
|
||||||
|
|
Loading…
Reference in New Issue