#include <net/netfilter/nf_conntrack.h>
 #include <net/netfilter/nf_conntrack_seqadj.h>
 #include <net/netfilter/nf_conntrack_synproxy.h>
+#include <net/netfilter/nf_conntrack_ecache.h>
 
 static struct iphdr *
 synproxy_build_ip(struct net *net, struct sk_buff *skb, __be32 saddr,
                synproxy->isn = ntohl(th->ack_seq);
                if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP)
                        synproxy->its = opts.tsecr;
+
+               nf_conntrack_event_cache(IPCT_SYNPROXY, ct);
                break;
        case TCP_CONNTRACK_SYN_RECV:
                if (!th->syn || !th->ack)
                if (!synproxy_parse_options(skb, thoff, th, &opts))
                        return NF_DROP;
 
-               if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP)
+               if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) {
                        synproxy->tsoff = opts.tsval - synproxy->its;
+                       nf_conntrack_event_cache(IPCT_SYNPROXY, ct);
+               }
 
                opts.options &= ~(XT_SYNPROXY_OPT_MSS |
                                  XT_SYNPROXY_OPT_WSCALE |
                synproxy_send_server_ack(net, state, skb, th, &opts);
 
                nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - ntohl(th->seq));
+               nf_conntrack_event_cache(IPCT_SEQADJ, ct);
 
                swap(opts.tsval, opts.tsecr);
                synproxy_send_client_ack(net, skb, th, &opts);
 
 #include <net/netfilter/nf_conntrack.h>
 #include <net/netfilter/nf_conntrack_seqadj.h>
 #include <net/netfilter/nf_conntrack_synproxy.h>
+#include <net/netfilter/nf_conntrack_ecache.h>
 
 static struct ipv6hdr *
 synproxy_build_ip(struct net *net, struct sk_buff *skb,
                synproxy->isn = ntohl(th->ack_seq);
                if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP)
                        synproxy->its = opts.tsecr;
+
+               nf_conntrack_event_cache(IPCT_SYNPROXY, ct);
                break;
        case TCP_CONNTRACK_SYN_RECV:
                if (!th->syn || !th->ack)
                if (!synproxy_parse_options(skb, thoff, th, &opts))
                        return NF_DROP;
 
-               if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP)
+               if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) {
                        synproxy->tsoff = opts.tsval - synproxy->its;
+                       nf_conntrack_event_cache(IPCT_SYNPROXY, ct);
+               }
 
                opts.options &= ~(XT_SYNPROXY_OPT_MSS |
                                  XT_SYNPROXY_OPT_WSCALE |
                synproxy_send_server_ack(net, state, skb, th, &opts);
 
                nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - ntohl(th->seq));
+               nf_conntrack_event_cache(IPCT_SEQADJ, ct);
 
                swap(opts.tsval, opts.tsecr);
                synproxy_send_client_ack(net, skb, th, &opts);
 
        return -1;
 }
 
+static int ctnetlink_dump_ct_synproxy(struct sk_buff *skb, struct nf_conn *ct)
+{
+       struct nf_conn_synproxy *synproxy = nfct_synproxy(ct);
+       struct nlattr *nest_parms;
+
+       if (!synproxy)
+               return 0;
+
+       nest_parms = nla_nest_start(skb, CTA_SYNPROXY | NLA_F_NESTED);
+       if (!nest_parms)
+               goto nla_put_failure;
+
+       if (nla_put_be32(skb, CTA_SYNPROXY_ISN, htonl(synproxy->isn)) ||
+           nla_put_be32(skb, CTA_SYNPROXY_ITS, htonl(synproxy->its)) ||
+           nla_put_be32(skb, CTA_SYNPROXY_TSOFF, htonl(synproxy->tsoff)))
+               goto nla_put_failure;
+
+       nla_nest_end(skb, nest_parms);
+
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
 static int ctnetlink_dump_id(struct sk_buff *skb, const struct nf_conn *ct)
 {
        if (nla_put_be32(skb, CTA_ID, htonl((unsigned long)ct)))
            ctnetlink_dump_id(skb, ct) < 0 ||
            ctnetlink_dump_use(skb, ct) < 0 ||
            ctnetlink_dump_master(skb, ct) < 0 ||
-           ctnetlink_dump_ct_seq_adj(skb, ct) < 0)
+           ctnetlink_dump_ct_seq_adj(skb, ct) < 0 ||
+           ctnetlink_dump_ct_synproxy(skb, ct) < 0)
                goto nla_put_failure;
 
        nlmsg_end(skb, nlh);
                if (events & (1 << IPCT_SEQADJ) &&
                    ctnetlink_dump_ct_seq_adj(skb, ct) < 0)
                        goto nla_put_failure;
+
+               if (events & (1 << IPCT_SYNPROXY) &&
+                   ctnetlink_dump_ct_synproxy(skb, ct) < 0)
+                       goto nla_put_failure;
        }
 
 #ifdef CONFIG_NF_CONNTRACK_MARK
        return ret;
 }
 
+static const struct nla_policy synproxy_policy[CTA_SYNPROXY_MAX + 1] = {
+       [CTA_SYNPROXY_ISN]      = { .type = NLA_U32 },
+       [CTA_SYNPROXY_ITS]      = { .type = NLA_U32 },
+       [CTA_SYNPROXY_TSOFF]    = { .type = NLA_U32 },
+};
+
+static int ctnetlink_change_synproxy(struct nf_conn *ct,
+                                    const struct nlattr * const cda[])
+{
+       struct nf_conn_synproxy *synproxy = nfct_synproxy(ct);
+       struct nlattr *tb[CTA_SYNPROXY_MAX + 1];
+       int err;
+
+       if (!synproxy)
+               return 0;
+
+       err = nla_parse_nested(tb, CTA_SYNPROXY_MAX, cda[CTA_SYNPROXY],
+                              synproxy_policy, NULL);
+       if (err < 0)
+               return err;
+
+       if (!tb[CTA_SYNPROXY_ISN] ||
+           !tb[CTA_SYNPROXY_ITS] ||
+           !tb[CTA_SYNPROXY_TSOFF])
+               return -EINVAL;
+
+       synproxy->isn = ntohl(nla_get_be32(tb[CTA_SYNPROXY_ISN]));
+       synproxy->its = ntohl(nla_get_be32(tb[CTA_SYNPROXY_ITS]));
+       synproxy->tsoff = ntohl(nla_get_be32(tb[CTA_SYNPROXY_TSOFF]));
+
+       return 0;
+}
+
 static int
 ctnetlink_attach_labels(struct nf_conn *ct, const struct nlattr * const cda[])
 {
                        return err;
        }
 
+       if (cda[CTA_SYNPROXY]) {
+               err = ctnetlink_change_synproxy(ct, cda);
+               if (err < 0)
+                       return err;
+       }
+
        if (cda[CTA_LABELS]) {
                err = ctnetlink_attach_labels(ct, cda);
                if (err < 0)
                        goto err2;
        }
 
+       if (cda[CTA_SYNPROXY]) {
+               err = ctnetlink_change_synproxy(ct, cda);
+               if (err < 0)
+                       goto err2;
+       }
+
 #if defined(CONFIG_NF_CONNTRACK_MARK)
        if (cda[CTA_MARK])
                ct->mark = ntohl(nla_get_be32(cda[CTA_MARK]));
                                                      (1 << IPCT_HELPER) |
                                                      (1 << IPCT_PROTOINFO) |
                                                      (1 << IPCT_SEQADJ) |
-                                                     (1 << IPCT_MARK) | events,
+                                                     (1 << IPCT_MARK) |
+                                                     (1 << IPCT_SYNPROXY) |
+                                                     events,
                                                      ct, NETLINK_CB(skb).portid,
                                                      nlmsg_report(nlh));
                        nf_ct_put(ct);
                                                      (1 << IPCT_LABEL) |
                                                      (1 << IPCT_PROTOINFO) |
                                                      (1 << IPCT_SEQADJ) |
-                                                     (1 << IPCT_MARK),
+                                                     (1 << IPCT_MARK) |
+                                                     (1 << IPCT_SYNPROXY),
                                                      ct, NETLINK_CB(skb).portid,
                                                      nlmsg_report(nlh));
                }
            ctnetlink_dump_ct_seq_adj(skb, ct) < 0)
                goto nla_put_failure;
 
+       if (ctnetlink_dump_ct_synproxy(skb, ct) < 0)
+               goto nla_put_failure;
+
 #ifdef CONFIG_NF_CONNTRACK_MARK
        if (ct->mark && ctnetlink_dump_mark(skb, ct) < 0)
                goto nla_put_failure;