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 set_bit(IPS_SEQ_ADJUST_BIT
, &ct
->status
);
41 spin_lock_bh(&ct
->lock
);
42 this_way
= &seqadj
->seq
[dir
];
43 if (this_way
->offset_before
== this_way
->offset_after
||
44 before(this_way
->correction_pos
, seq
)) {
45 this_way
->correction_pos
= seq
;
46 this_way
->offset_before
= this_way
->offset_after
;
47 this_way
->offset_after
+= off
;
49 spin_unlock_bh(&ct
->lock
);
52 EXPORT_SYMBOL_GPL(nf_ct_seqadj_set
);
54 void nf_ct_tcp_seqadj_set(struct sk_buff
*skb
,
55 struct nf_conn
*ct
, enum ip_conntrack_info ctinfo
,
58 const struct tcphdr
*th
;
60 if (nf_ct_protonum(ct
) != IPPROTO_TCP
)
63 th
= (struct tcphdr
*)(skb_network_header(skb
) + ip_hdrlen(skb
));
64 nf_ct_seqadj_set(ct
, ctinfo
, th
->seq
, off
);
66 EXPORT_SYMBOL_GPL(nf_ct_tcp_seqadj_set
);
68 /* Adjust one found SACK option including checksum correction */
69 static void nf_ct_sack_block_adjust(struct sk_buff
*skb
,
73 struct nf_ct_seqadj
*seq
)
75 while (sackoff
< sackend
) {
76 struct tcp_sack_block_wire
*sack
;
77 __be32 new_start_seq
, new_end_seq
;
79 sack
= (void *)skb
->data
+ sackoff
;
80 if (after(ntohl(sack
->start_seq
) - seq
->offset_before
,
82 new_start_seq
= htonl(ntohl(sack
->start_seq
) -
85 new_start_seq
= htonl(ntohl(sack
->start_seq
) -
88 if (after(ntohl(sack
->end_seq
) - seq
->offset_before
,
90 new_end_seq
= htonl(ntohl(sack
->end_seq
) -
93 new_end_seq
= htonl(ntohl(sack
->end_seq
) -
96 pr_debug("sack_adjust: start_seq: %d->%d, end_seq: %d->%d\n",
97 ntohl(sack
->start_seq
), new_start_seq
,
98 ntohl(sack
->end_seq
), new_end_seq
);
100 inet_proto_csum_replace4(&tcph
->check
, skb
,
101 sack
->start_seq
, new_start_seq
, 0);
102 inet_proto_csum_replace4(&tcph
->check
, skb
,
103 sack
->end_seq
, new_end_seq
, 0);
104 sack
->start_seq
= new_start_seq
;
105 sack
->end_seq
= new_end_seq
;
106 sackoff
+= sizeof(*sack
);
110 /* TCP SACK sequence number adjustment */
111 static unsigned int nf_ct_sack_adjust(struct sk_buff
*skb
,
112 unsigned int protoff
,
115 enum ip_conntrack_info ctinfo
)
117 unsigned int dir
, optoff
, optend
;
118 struct nf_conn_seqadj
*seqadj
= nfct_seqadj(ct
);
120 optoff
= protoff
+ sizeof(struct tcphdr
);
121 optend
= protoff
+ tcph
->doff
* 4;
123 if (!skb_make_writable(skb
, optend
))
126 dir
= CTINFO2DIR(ctinfo
);
128 while (optoff
< optend
) {
129 /* Usually: option, length. */
130 unsigned char *op
= skb
->data
+ optoff
;
139 /* no partial options */
140 if (optoff
+ 1 == optend
||
141 optoff
+ op
[1] > optend
||
144 if (op
[0] == TCPOPT_SACK
&&
145 op
[1] >= 2+TCPOLEN_SACK_PERBLOCK
&&
146 ((op
[1] - 2) % TCPOLEN_SACK_PERBLOCK
) == 0)
147 nf_ct_sack_block_adjust(skb
, tcph
, optoff
+ 2,
156 /* TCP sequence number adjustment. Returns 1 on success, 0 on failure */
157 int nf_ct_seq_adjust(struct sk_buff
*skb
,
158 struct nf_conn
*ct
, enum ip_conntrack_info ctinfo
,
159 unsigned int protoff
)
161 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
163 __be32 newseq
, newack
;
165 struct nf_conn_seqadj
*seqadj
= nfct_seqadj(ct
);
166 struct nf_ct_seqadj
*this_way
, *other_way
;
169 this_way
= &seqadj
->seq
[dir
];
170 other_way
= &seqadj
->seq
[!dir
];
172 if (!skb_make_writable(skb
, protoff
+ sizeof(*tcph
)))
175 tcph
= (void *)skb
->data
+ protoff
;
176 spin_lock_bh(&ct
->lock
);
177 if (after(ntohl(tcph
->seq
), this_way
->correction_pos
))
178 seqoff
= this_way
->offset_after
;
180 seqoff
= this_way
->offset_before
;
182 if (after(ntohl(tcph
->ack_seq
) - other_way
->offset_before
,
183 other_way
->correction_pos
))
184 ackoff
= other_way
->offset_after
;
186 ackoff
= other_way
->offset_before
;
188 newseq
= htonl(ntohl(tcph
->seq
) + seqoff
);
189 newack
= htonl(ntohl(tcph
->ack_seq
) - ackoff
);
191 inet_proto_csum_replace4(&tcph
->check
, skb
, tcph
->seq
, newseq
, 0);
192 inet_proto_csum_replace4(&tcph
->check
, skb
, tcph
->ack_seq
, newack
, 0);
194 pr_debug("Adjusting sequence number from %u->%u, ack from %u->%u\n",
195 ntohl(tcph
->seq
), ntohl(newseq
), ntohl(tcph
->ack_seq
),
199 tcph
->ack_seq
= newack
;
201 res
= nf_ct_sack_adjust(skb
, protoff
, tcph
, ct
, ctinfo
);
202 spin_unlock_bh(&ct
->lock
);
206 EXPORT_SYMBOL_GPL(nf_ct_seq_adjust
);
208 s32
nf_ct_seq_offset(const struct nf_conn
*ct
,
209 enum ip_conntrack_dir dir
,
212 struct nf_conn_seqadj
*seqadj
= nfct_seqadj(ct
);
213 struct nf_ct_seqadj
*this_way
;
218 this_way
= &seqadj
->seq
[dir
];
219 return after(seq
, this_way
->correction_pos
) ?
220 this_way
->offset_after
: this_way
->offset_before
;
222 EXPORT_SYMBOL_GPL(nf_ct_seq_offset
);
224 static struct nf_ct_ext_type nf_ct_seqadj_extend __read_mostly
= {
225 .len
= sizeof(struct nf_conn_seqadj
),
226 .align
= __alignof__(struct nf_conn_seqadj
),
227 .id
= NF_CT_EXT_SEQADJ
,
230 int nf_conntrack_seqadj_init(void)
232 return nf_ct_extend_register(&nf_ct_seqadj_extend
);
235 void nf_conntrack_seqadj_fini(void)
237 nf_ct_extend_unregister(&nf_ct_seqadj_extend
);