1 #include <linux/types.h>
2 #include <linux/netfilter.h>
5 #include <net/netfilter/nf_conntrack.h>
6 #include <net/netfilter/nf_conntrack_extend.h>
7 #include <net/netfilter/nf_conntrack_seqadj.h>
9 int nf_ct_seqadj_init(struct nf_conn
*ct
, enum ip_conntrack_info ctinfo
,
12 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
13 struct nf_conn_seqadj
*seqadj
;
14 struct nf_ct_seqadj
*this_way
;
19 set_bit(IPS_SEQ_ADJUST_BIT
, &ct
->status
);
21 seqadj
= nfct_seqadj(ct
);
22 this_way
= &seqadj
->seq
[dir
];
23 this_way
->offset_before
= off
;
24 this_way
->offset_after
= off
;
27 EXPORT_SYMBOL_GPL(nf_ct_seqadj_init
);
29 int nf_ct_seqadj_set(struct nf_conn
*ct
, enum ip_conntrack_info ctinfo
,
32 struct nf_conn_seqadj
*seqadj
= nfct_seqadj(ct
);
33 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
34 struct nf_ct_seqadj
*this_way
;
39 if (unlikely(!seqadj
)) {
40 WARN_ONCE(1, "Missing nfct_seqadj_ext_add() setup call\n");
44 set_bit(IPS_SEQ_ADJUST_BIT
, &ct
->status
);
46 spin_lock_bh(&ct
->lock
);
47 this_way
= &seqadj
->seq
[dir
];
48 if (this_way
->offset_before
== this_way
->offset_after
||
49 before(this_way
->correction_pos
, ntohl(seq
))) {
50 this_way
->correction_pos
= ntohl(seq
);
51 this_way
->offset_before
= this_way
->offset_after
;
52 this_way
->offset_after
+= off
;
54 spin_unlock_bh(&ct
->lock
);
57 EXPORT_SYMBOL_GPL(nf_ct_seqadj_set
);
59 void nf_ct_tcp_seqadj_set(struct sk_buff
*skb
,
60 struct nf_conn
*ct
, enum ip_conntrack_info ctinfo
,
63 const struct tcphdr
*th
;
65 if (nf_ct_protonum(ct
) != IPPROTO_TCP
)
68 th
= (struct tcphdr
*)(skb_network_header(skb
) + ip_hdrlen(skb
));
69 nf_ct_seqadj_set(ct
, ctinfo
, th
->seq
, off
);
71 EXPORT_SYMBOL_GPL(nf_ct_tcp_seqadj_set
);
73 /* Adjust one found SACK option including checksum correction */
74 static void nf_ct_sack_block_adjust(struct sk_buff
*skb
,
78 struct nf_ct_seqadj
*seq
)
80 while (sackoff
< sackend
) {
81 struct tcp_sack_block_wire
*sack
;
82 __be32 new_start_seq
, new_end_seq
;
84 sack
= (void *)skb
->data
+ sackoff
;
85 if (after(ntohl(sack
->start_seq
) - seq
->offset_before
,
87 new_start_seq
= htonl(ntohl(sack
->start_seq
) -
90 new_start_seq
= htonl(ntohl(sack
->start_seq
) -
93 if (after(ntohl(sack
->end_seq
) - seq
->offset_before
,
95 new_end_seq
= htonl(ntohl(sack
->end_seq
) -
98 new_end_seq
= htonl(ntohl(sack
->end_seq
) -
101 pr_debug("sack_adjust: start_seq: %u->%u, end_seq: %u->%u\n",
102 ntohl(sack
->start_seq
), ntohl(new_start_seq
),
103 ntohl(sack
->end_seq
), ntohl(new_end_seq
));
105 inet_proto_csum_replace4(&tcph
->check
, skb
,
106 sack
->start_seq
, new_start_seq
, false);
107 inet_proto_csum_replace4(&tcph
->check
, skb
,
108 sack
->end_seq
, new_end_seq
, false);
109 sack
->start_seq
= new_start_seq
;
110 sack
->end_seq
= new_end_seq
;
111 sackoff
+= sizeof(*sack
);
115 /* TCP SACK sequence number adjustment */
116 static unsigned int nf_ct_sack_adjust(struct sk_buff
*skb
,
117 unsigned int protoff
,
120 enum ip_conntrack_info ctinfo
)
122 unsigned int dir
, optoff
, optend
;
123 struct nf_conn_seqadj
*seqadj
= nfct_seqadj(ct
);
125 optoff
= protoff
+ sizeof(struct tcphdr
);
126 optend
= protoff
+ tcph
->doff
* 4;
128 if (!skb_make_writable(skb
, optend
))
131 dir
= CTINFO2DIR(ctinfo
);
133 while (optoff
< optend
) {
134 /* Usually: option, length. */
135 unsigned char *op
= skb
->data
+ optoff
;
144 /* no partial options */
145 if (optoff
+ 1 == optend
||
146 optoff
+ op
[1] > optend
||
149 if (op
[0] == TCPOPT_SACK
&&
150 op
[1] >= 2+TCPOLEN_SACK_PERBLOCK
&&
151 ((op
[1] - 2) % TCPOLEN_SACK_PERBLOCK
) == 0)
152 nf_ct_sack_block_adjust(skb
, tcph
, optoff
+ 2,
161 /* TCP sequence number adjustment. Returns 1 on success, 0 on failure */
162 int nf_ct_seq_adjust(struct sk_buff
*skb
,
163 struct nf_conn
*ct
, enum ip_conntrack_info ctinfo
,
164 unsigned int protoff
)
166 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
168 __be32 newseq
, newack
;
170 struct nf_conn_seqadj
*seqadj
= nfct_seqadj(ct
);
171 struct nf_ct_seqadj
*this_way
, *other_way
;
174 this_way
= &seqadj
->seq
[dir
];
175 other_way
= &seqadj
->seq
[!dir
];
177 if (!skb_make_writable(skb
, protoff
+ sizeof(*tcph
)))
180 tcph
= (void *)skb
->data
+ protoff
;
181 spin_lock_bh(&ct
->lock
);
182 if (after(ntohl(tcph
->seq
), this_way
->correction_pos
))
183 seqoff
= this_way
->offset_after
;
185 seqoff
= this_way
->offset_before
;
187 newseq
= htonl(ntohl(tcph
->seq
) + seqoff
);
188 inet_proto_csum_replace4(&tcph
->check
, skb
, tcph
->seq
, newseq
, false);
189 pr_debug("Adjusting sequence number from %u->%u\n",
190 ntohl(tcph
->seq
), ntohl(newseq
));
196 if (after(ntohl(tcph
->ack_seq
) - other_way
->offset_before
,
197 other_way
->correction_pos
))
198 ackoff
= other_way
->offset_after
;
200 ackoff
= other_way
->offset_before
;
202 newack
= htonl(ntohl(tcph
->ack_seq
) - ackoff
);
203 inet_proto_csum_replace4(&tcph
->check
, skb
, tcph
->ack_seq
, newack
,
205 pr_debug("Adjusting ack number from %u->%u, ack from %u->%u\n",
206 ntohl(tcph
->seq
), ntohl(newseq
), ntohl(tcph
->ack_seq
),
208 tcph
->ack_seq
= newack
;
210 res
= nf_ct_sack_adjust(skb
, protoff
, tcph
, ct
, ctinfo
);
212 spin_unlock_bh(&ct
->lock
);
216 EXPORT_SYMBOL_GPL(nf_ct_seq_adjust
);
218 s32
nf_ct_seq_offset(const struct nf_conn
*ct
,
219 enum ip_conntrack_dir dir
,
222 struct nf_conn_seqadj
*seqadj
= nfct_seqadj(ct
);
223 struct nf_ct_seqadj
*this_way
;
228 this_way
= &seqadj
->seq
[dir
];
229 return after(seq
, this_way
->correction_pos
) ?
230 this_way
->offset_after
: this_way
->offset_before
;
232 EXPORT_SYMBOL_GPL(nf_ct_seq_offset
);
234 static struct nf_ct_ext_type nf_ct_seqadj_extend __read_mostly
= {
235 .len
= sizeof(struct nf_conn_seqadj
),
236 .align
= __alignof__(struct nf_conn_seqadj
),
237 .id
= NF_CT_EXT_SEQADJ
,
240 int nf_conntrack_seqadj_init(void)
242 return nf_ct_extend_register(&nf_ct_seqadj_extend
);
245 void nf_conntrack_seqadj_fini(void)
247 nf_ct_extend_unregister(&nf_ct_seqadj_extend
);