|
@@ -396,6 +396,12 @@ static int help(struct sk_buff *skb,
|
|
|
|
|
|
/* Look up to see if we're just after a \n. */
|
|
|
if (!find_nl_seq(ntohl(th->seq), ct_ftp_info, dir)) {
|
|
|
+ /* We're picking up this, clear flags and let it continue */
|
|
|
+ if (unlikely(ct_ftp_info->flags[dir] & NF_CT_FTP_SEQ_PICKUP)) {
|
|
|
+ ct_ftp_info->flags[dir] ^= NF_CT_FTP_SEQ_PICKUP;
|
|
|
+ goto skip_nl_seq;
|
|
|
+ }
|
|
|
+
|
|
|
/* Now if this ends in \n, update ftp info. */
|
|
|
pr_debug("nf_conntrack_ftp: wrong seq pos %s(%u) or %s(%u)\n",
|
|
|
ct_ftp_info->seq_aft_nl_num[dir] > 0 ? "" : "(UNSET)",
|
|
@@ -406,6 +412,7 @@ static int help(struct sk_buff *skb,
|
|
|
goto out_update_nl;
|
|
|
}
|
|
|
|
|
|
+skip_nl_seq:
|
|
|
/* Initialize IP/IPv6 addr to expected address (it's not mentioned
|
|
|
in EPSV responses) */
|
|
|
cmd.l3num = nf_ct_l3num(ct);
|
|
@@ -512,6 +519,19 @@ out_update_nl:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static int nf_ct_ftp_from_nlattr(struct nlattr *attr, struct nf_conn *ct)
|
|
|
+{
|
|
|
+ struct nf_ct_ftp_master *ftp = nfct_help_data(ct);
|
|
|
+
|
|
|
+ /* This conntrack has been injected from user-space, always pick up
|
|
|
+ * sequence tracking. Otherwise, the first FTP command after the
|
|
|
+ * failover breaks.
|
|
|
+ */
|
|
|
+ ftp->flags[IP_CT_DIR_ORIGINAL] |= NF_CT_FTP_SEQ_PICKUP;
|
|
|
+ ftp->flags[IP_CT_DIR_REPLY] |= NF_CT_FTP_SEQ_PICKUP;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static struct nf_conntrack_helper ftp[MAX_PORTS][2] __read_mostly;
|
|
|
|
|
|
static const struct nf_conntrack_expect_policy ftp_exp_policy = {
|
|
@@ -561,6 +581,7 @@ static int __init nf_conntrack_ftp_init(void)
|
|
|
ftp[i][j].expect_policy = &ftp_exp_policy;
|
|
|
ftp[i][j].me = THIS_MODULE;
|
|
|
ftp[i][j].help = help;
|
|
|
+ ftp[i][j].from_nlattr = nf_ct_ftp_from_nlattr;
|
|
|
if (ports[i] == FTP_PORT)
|
|
|
sprintf(ftp[i][j].name, "ftp");
|
|
|
else
|