1 // SPDX-License-Identifier: GPL-2.0-only
2 #include <linux/types.h>
3 #include <linux/netfilter.h>
6 #include <net/netfilter/nf_conntrack.h>
7 #include <net/netfilter/nf_conntrack_extend.h>
8 #include <net/netfilter/nf_conntrack_seqadj.h>
10 int nf_ct_seqadj_init(struct nf_conn
*ct
, enum ip_conntrack_info ctinfo
,
13 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
14 struct nf_conn_seqadj
*seqadj
;
15 struct nf_ct_seqadj
*this_way
;
20 set_bit(IPS_SEQ_ADJUST_BIT
, &ct
->status
);
22 seqadj
= nfct_seqadj(ct
);
23 this_way
= &seqadj
->seq
[dir
];
24 this_way
->offset_before
= off
;
25 this_way
->offset_after
= off
;
28 EXPORT_SYMBOL_GPL(nf_ct_seqadj_init
);
30 int nf_ct_seqadj_set(struct nf_conn
*ct
, enum ip_conntrack_info ctinfo
,
33 struct nf_conn_seqadj
*seqadj
= nfct_seqadj(ct
);
34 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
35 struct nf_ct_seqadj
*this_way
;
40 if (unlikely(!seqadj
)) {
41 WARN_ONCE(1, "Missing nfct_seqadj_ext_add() setup call\n");
45 set_bit(IPS_SEQ_ADJUST_BIT
, &ct
->status
);
47 spin_lock_bh(&ct
->lock
);
48 this_way
= &seqadj
->seq
[dir
];
49 if (this_way
->offset_before
== this_way
->offset_after
||
50 before(this_way
->correction_pos
, ntohl(seq
))) {
51 this_way
->correction_pos
= ntohl(seq
);
52 this_way
->offset_before
= this_way
->offset_after
;
53 this_way
->offset_after
+= off
;
55 spin_unlock_bh(&ct
->lock
);
58 EXPORT_SYMBOL_GPL(nf_ct_seqadj_set
);
60 void nf_ct_tcp_seqadj_set(struct sk_buff
*skb
,
61 struct nf_conn
*ct
, enum ip_conntrack_info ctinfo
,
64 const struct tcphdr
*th
;
66 if (nf_ct_protonum(ct
) != IPPROTO_TCP
)
69 th
= (struct tcphdr
*)(skb_network_header(skb
) + ip_hdrlen(skb
));
70 nf_ct_seqadj_set(ct
, ctinfo
, th
->seq
, off
);
72 EXPORT_SYMBOL_GPL(nf_ct_tcp_seqadj_set
);
74 /* Adjust one found SACK option including checksum correction */
75 static void nf_ct_sack_block_adjust(struct sk_buff
*skb
,
79 struct nf_ct_seqadj
*seq
)
81 while (sackoff
< sackend
) {
82 struct tcp_sack_block_wire
*sack
;
83 __be32 new_start_seq
, new_end_seq
;
85 sack
= (void *)skb
->data
+ sackoff
;
86 if (after(ntohl(sack
->start_seq
) - seq
->offset_before
,
88 new_start_seq
= htonl(ntohl(sack
->start_seq
) -
91 new_start_seq
= htonl(ntohl(sack
->start_seq
) -
94 if (after(ntohl(sack
->end_seq
) - seq
->offset_before
,
96 new_end_seq
= htonl(ntohl(sack
->end_seq
) -
99 new_end_seq
= htonl(ntohl(sack
->end_seq
) -
102 pr_debug("sack_adjust: start_seq: %u->%u, end_seq: %u->%u\n",
103 ntohl(sack
->start_seq
), ntohl(new_start_seq
),
104 ntohl(sack
->end_seq
), ntohl(new_end_seq
));
106 inet_proto_csum_replace4(&tcph
->check
, skb
,
107 sack
->start_seq
, new_start_seq
, false);
108 inet_proto_csum_replace4(&tcph
->check
, skb
,
109 sack
->end_seq
, new_end_seq
, false);
110 sack
->start_seq
= new_start_seq
;
111 sack
->end_seq
= new_end_seq
;
112 sackoff
+= sizeof(*sack
);
116 /* TCP SACK sequence number adjustment */
117 static unsigned int nf_ct_sack_adjust(struct sk_buff
*skb
,
118 unsigned int protoff
,
120 enum ip_conntrack_info ctinfo
)
122 struct tcphdr
*tcph
= (void *)skb
->data
+ protoff
;
123 struct nf_conn_seqadj
*seqadj
= nfct_seqadj(ct
);
124 unsigned int dir
, optoff
, optend
;
126 optoff
= protoff
+ sizeof(struct tcphdr
);
127 optend
= protoff
+ tcph
->doff
* 4;
129 if (skb_ensure_writable(skb
, optend
))
132 tcph
= (void *)skb
->data
+ protoff
;
133 dir
= CTINFO2DIR(ctinfo
);
135 while (optoff
< optend
) {
136 /* Usually: option, length. */
137 unsigned char *op
= skb
->data
+ optoff
;
146 /* no partial options */
147 if (optoff
+ 1 == optend
||
148 optoff
+ op
[1] > optend
||
151 if (op
[0] == TCPOPT_SACK
&&
152 op
[1] >= 2+TCPOLEN_SACK_PERBLOCK
&&
153 ((op
[1] - 2) % TCPOLEN_SACK_PERBLOCK
) == 0)
154 nf_ct_sack_block_adjust(skb
, tcph
, optoff
+ 2,
163 /* TCP sequence number adjustment. Returns 1 on success, 0 on failure */
164 int nf_ct_seq_adjust(struct sk_buff
*skb
,
165 struct nf_conn
*ct
, enum ip_conntrack_info ctinfo
,
166 unsigned int protoff
)
168 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
170 __be32 newseq
, newack
;
172 struct nf_conn_seqadj
*seqadj
= nfct_seqadj(ct
);
173 struct nf_ct_seqadj
*this_way
, *other_way
;
176 this_way
= &seqadj
->seq
[dir
];
177 other_way
= &seqadj
->seq
[!dir
];
179 if (skb_ensure_writable(skb
, protoff
+ sizeof(*tcph
)))
182 tcph
= (void *)skb
->data
+ protoff
;
183 spin_lock_bh(&ct
->lock
);
184 if (after(ntohl(tcph
->seq
), this_way
->correction_pos
))
185 seqoff
= this_way
->offset_after
;
187 seqoff
= this_way
->offset_before
;
189 newseq
= htonl(ntohl(tcph
->seq
) + seqoff
);
190 inet_proto_csum_replace4(&tcph
->check
, skb
, tcph
->seq
, newseq
, false);
191 pr_debug("Adjusting sequence number from %u->%u\n",
192 ntohl(tcph
->seq
), ntohl(newseq
));
198 if (after(ntohl(tcph
->ack_seq
) - other_way
->offset_before
,
199 other_way
->correction_pos
))
200 ackoff
= other_way
->offset_after
;
202 ackoff
= other_way
->offset_before
;
204 newack
= htonl(ntohl(tcph
->ack_seq
) - ackoff
);
205 inet_proto_csum_replace4(&tcph
->check
, skb
, tcph
->ack_seq
, newack
,
207 pr_debug("Adjusting ack number from %u->%u, ack from %u->%u\n",
208 ntohl(tcph
->seq
), ntohl(newseq
), ntohl(tcph
->ack_seq
),
210 tcph
->ack_seq
= newack
;
212 res
= nf_ct_sack_adjust(skb
, protoff
, ct
, ctinfo
);
214 spin_unlock_bh(&ct
->lock
);
218 EXPORT_SYMBOL_GPL(nf_ct_seq_adjust
);
220 s32
nf_ct_seq_offset(const struct nf_conn
*ct
,
221 enum ip_conntrack_dir dir
,
224 struct nf_conn_seqadj
*seqadj
= nfct_seqadj(ct
);
225 struct nf_ct_seqadj
*this_way
;
230 this_way
= &seqadj
->seq
[dir
];
231 return after(seq
, this_way
->correction_pos
) ?
232 this_way
->offset_after
: this_way
->offset_before
;
234 EXPORT_SYMBOL_GPL(nf_ct_seq_offset
);