]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blame - net/netfilter/nf_conntrack_seqadj.c
net: syncookies: export cookie_v4_init_sequence/cookie_v4_check
[mirror_ubuntu-hirsute-kernel.git] / net / netfilter / nf_conntrack_seqadj.c
CommitLineData
41d73ec0
PM
1#include <linux/types.h>
2#include <linux/netfilter.h>
3#include <net/tcp.h>
4
5#include <net/netfilter/nf_conntrack.h>
6#include <net/netfilter/nf_conntrack_extend.h>
7#include <net/netfilter/nf_conntrack_seqadj.h>
8
9int nf_ct_seqadj_set(struct nf_conn *ct, enum ip_conntrack_info ctinfo,
10 __be32 seq, s32 off)
11{
12 struct nf_conn_seqadj *seqadj = nfct_seqadj(ct);
13 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
14 struct nf_ct_seqadj *this_way;
15
16 if (off == 0)
17 return 0;
18
19 set_bit(IPS_SEQ_ADJUST_BIT, &ct->status);
20
21 spin_lock_bh(&ct->lock);
22 this_way = &seqadj->seq[dir];
23 if (this_way->offset_before == this_way->offset_after ||
24 before(this_way->correction_pos, seq)) {
25 this_way->correction_pos = seq;
26 this_way->offset_before = this_way->offset_after;
27 this_way->offset_after += off;
28 }
29 spin_unlock_bh(&ct->lock);
30 return 0;
31}
32EXPORT_SYMBOL_GPL(nf_ct_seqadj_set);
33
34void nf_ct_tcp_seqadj_set(struct sk_buff *skb,
35 struct nf_conn *ct, enum ip_conntrack_info ctinfo,
36 s32 off)
37{
38 const struct tcphdr *th;
39
40 if (nf_ct_protonum(ct) != IPPROTO_TCP)
41 return;
42
43 th = (struct tcphdr *)(skb_network_header(skb) + ip_hdrlen(skb));
44 nf_ct_seqadj_set(ct, ctinfo, th->seq, off);
45}
46EXPORT_SYMBOL_GPL(nf_ct_tcp_seqadj_set);
47
48/* Adjust one found SACK option including checksum correction */
49static void nf_ct_sack_block_adjust(struct sk_buff *skb,
50 struct tcphdr *tcph,
51 unsigned int sackoff,
52 unsigned int sackend,
53 struct nf_ct_seqadj *seq)
54{
55 while (sackoff < sackend) {
56 struct tcp_sack_block_wire *sack;
57 __be32 new_start_seq, new_end_seq;
58
59 sack = (void *)skb->data + sackoff;
60 if (after(ntohl(sack->start_seq) - seq->offset_before,
61 seq->correction_pos))
62 new_start_seq = htonl(ntohl(sack->start_seq) -
63 seq->offset_after);
64 else
65 new_start_seq = htonl(ntohl(sack->start_seq) -
66 seq->offset_before);
67
68 if (after(ntohl(sack->end_seq) - seq->offset_before,
69 seq->correction_pos))
70 new_end_seq = htonl(ntohl(sack->end_seq) -
71 seq->offset_after);
72 else
73 new_end_seq = htonl(ntohl(sack->end_seq) -
74 seq->offset_before);
75
76 pr_debug("sack_adjust: start_seq: %d->%d, end_seq: %d->%d\n",
77 ntohl(sack->start_seq), new_start_seq,
78 ntohl(sack->end_seq), new_end_seq);
79
80 inet_proto_csum_replace4(&tcph->check, skb,
81 sack->start_seq, new_start_seq, 0);
82 inet_proto_csum_replace4(&tcph->check, skb,
83 sack->end_seq, new_end_seq, 0);
84 sack->start_seq = new_start_seq;
85 sack->end_seq = new_end_seq;
86 sackoff += sizeof(*sack);
87 }
88}
89
90/* TCP SACK sequence number adjustment */
91static unsigned int nf_ct_sack_adjust(struct sk_buff *skb,
92 unsigned int protoff,
93 struct tcphdr *tcph,
94 struct nf_conn *ct,
95 enum ip_conntrack_info ctinfo)
96{
97 unsigned int dir, optoff, optend;
98 struct nf_conn_seqadj *seqadj = nfct_seqadj(ct);
99
100 optoff = protoff + sizeof(struct tcphdr);
101 optend = protoff + tcph->doff * 4;
102
103 if (!skb_make_writable(skb, optend))
104 return 0;
105
106 dir = CTINFO2DIR(ctinfo);
107
108 while (optoff < optend) {
109 /* Usually: option, length. */
110 unsigned char *op = skb->data + optoff;
111
112 switch (op[0]) {
113 case TCPOPT_EOL:
114 return 1;
115 case TCPOPT_NOP:
116 optoff++;
117 continue;
118 default:
119 /* no partial options */
120 if (optoff + 1 == optend ||
121 optoff + op[1] > optend ||
122 op[1] < 2)
123 return 0;
124 if (op[0] == TCPOPT_SACK &&
125 op[1] >= 2+TCPOLEN_SACK_PERBLOCK &&
126 ((op[1] - 2) % TCPOLEN_SACK_PERBLOCK) == 0)
127 nf_ct_sack_block_adjust(skb, tcph, optoff + 2,
128 optoff+op[1],
129 &seqadj->seq[!dir]);
130 optoff += op[1];
131 }
132 }
133 return 1;
134}
135
136/* TCP sequence number adjustment. Returns 1 on success, 0 on failure */
137int nf_ct_seq_adjust(struct sk_buff *skb,
138 struct nf_conn *ct, enum ip_conntrack_info ctinfo,
139 unsigned int protoff)
140{
141 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
142 struct tcphdr *tcph;
143 __be32 newseq, newack;
144 s32 seqoff, ackoff;
145 struct nf_conn_seqadj *seqadj = nfct_seqadj(ct);
146 struct nf_ct_seqadj *this_way, *other_way;
147 int res;
148
149 this_way = &seqadj->seq[dir];
150 other_way = &seqadj->seq[!dir];
151
152 if (!skb_make_writable(skb, protoff + sizeof(*tcph)))
153 return 0;
154
155 tcph = (void *)skb->data + protoff;
156 spin_lock_bh(&ct->lock);
157 if (after(ntohl(tcph->seq), this_way->correction_pos))
158 seqoff = this_way->offset_after;
159 else
160 seqoff = this_way->offset_before;
161
162 if (after(ntohl(tcph->ack_seq) - other_way->offset_before,
163 other_way->correction_pos))
164 ackoff = other_way->offset_after;
165 else
166 ackoff = other_way->offset_before;
167
168 newseq = htonl(ntohl(tcph->seq) + seqoff);
169 newack = htonl(ntohl(tcph->ack_seq) - ackoff);
170
171 inet_proto_csum_replace4(&tcph->check, skb, tcph->seq, newseq, 0);
172 inet_proto_csum_replace4(&tcph->check, skb, tcph->ack_seq, newack, 0);
173
174 pr_debug("Adjusting sequence number from %u->%u, ack from %u->%u\n",
175 ntohl(tcph->seq), ntohl(newseq), ntohl(tcph->ack_seq),
176 ntohl(newack));
177
178 tcph->seq = newseq;
179 tcph->ack_seq = newack;
180
181 res = nf_ct_sack_adjust(skb, protoff, tcph, ct, ctinfo);
182 spin_unlock_bh(&ct->lock);
183
184 return res;
185}
186EXPORT_SYMBOL_GPL(nf_ct_seq_adjust);
187
188s32 nf_ct_seq_offset(const struct nf_conn *ct,
189 enum ip_conntrack_dir dir,
190 u32 seq)
191{
192 struct nf_conn_seqadj *seqadj = nfct_seqadj(ct);
193 struct nf_ct_seqadj *this_way;
194
195 if (!seqadj)
196 return 0;
197
198 this_way = &seqadj->seq[dir];
199 return after(seq, this_way->correction_pos) ?
200 this_way->offset_after : this_way->offset_before;
201}
202EXPORT_SYMBOL_GPL(nf_ct_seq_offset);
203
204static struct nf_ct_ext_type nf_ct_seqadj_extend __read_mostly = {
205 .len = sizeof(struct nf_conn_seqadj),
206 .align = __alignof__(struct nf_conn_seqadj),
207 .id = NF_CT_EXT_SEQADJ,
208};
209
210int nf_conntrack_seqadj_init(void)
211{
212 return nf_ct_extend_register(&nf_ct_seqadj_extend);
213}
214
215void nf_conntrack_seqadj_fini(void)
216{
217 nf_ct_extend_unregister(&nf_ct_seqadj_extend);
218}