0001
0002 #include <linux/types.h>
0003 #include <linux/netfilter.h>
0004 #include <net/tcp.h>
0005
0006 #include <net/netfilter/nf_conntrack.h>
0007 #include <net/netfilter/nf_conntrack_extend.h>
0008 #include <net/netfilter/nf_conntrack_seqadj.h>
0009
0010 int nf_ct_seqadj_init(struct nf_conn *ct, enum ip_conntrack_info ctinfo,
0011 s32 off)
0012 {
0013 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
0014 struct nf_conn_seqadj *seqadj;
0015 struct nf_ct_seqadj *this_way;
0016
0017 if (off == 0)
0018 return 0;
0019
0020 set_bit(IPS_SEQ_ADJUST_BIT, &ct->status);
0021
0022 seqadj = nfct_seqadj(ct);
0023 this_way = &seqadj->seq[dir];
0024 this_way->offset_before = off;
0025 this_way->offset_after = off;
0026 return 0;
0027 }
0028 EXPORT_SYMBOL_GPL(nf_ct_seqadj_init);
0029
0030 int nf_ct_seqadj_set(struct nf_conn *ct, enum ip_conntrack_info ctinfo,
0031 __be32 seq, s32 off)
0032 {
0033 struct nf_conn_seqadj *seqadj = nfct_seqadj(ct);
0034 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
0035 struct nf_ct_seqadj *this_way;
0036
0037 if (off == 0)
0038 return 0;
0039
0040 if (unlikely(!seqadj)) {
0041 WARN_ONCE(1, "Missing nfct_seqadj_ext_add() setup call\n");
0042 return 0;
0043 }
0044
0045 set_bit(IPS_SEQ_ADJUST_BIT, &ct->status);
0046
0047 spin_lock_bh(&ct->lock);
0048 this_way = &seqadj->seq[dir];
0049 if (this_way->offset_before == this_way->offset_after ||
0050 before(this_way->correction_pos, ntohl(seq))) {
0051 this_way->correction_pos = ntohl(seq);
0052 this_way->offset_before = this_way->offset_after;
0053 this_way->offset_after += off;
0054 }
0055 spin_unlock_bh(&ct->lock);
0056 return 0;
0057 }
0058 EXPORT_SYMBOL_GPL(nf_ct_seqadj_set);
0059
0060 void nf_ct_tcp_seqadj_set(struct sk_buff *skb,
0061 struct nf_conn *ct, enum ip_conntrack_info ctinfo,
0062 s32 off)
0063 {
0064 const struct tcphdr *th;
0065
0066 if (nf_ct_protonum(ct) != IPPROTO_TCP)
0067 return;
0068
0069 th = (struct tcphdr *)(skb_network_header(skb) + ip_hdrlen(skb));
0070 nf_ct_seqadj_set(ct, ctinfo, th->seq, off);
0071 }
0072 EXPORT_SYMBOL_GPL(nf_ct_tcp_seqadj_set);
0073
0074
0075 static void nf_ct_sack_block_adjust(struct sk_buff *skb,
0076 struct tcphdr *tcph,
0077 unsigned int sackoff,
0078 unsigned int sackend,
0079 struct nf_ct_seqadj *seq)
0080 {
0081 while (sackoff < sackend) {
0082 struct tcp_sack_block_wire *sack;
0083 __be32 new_start_seq, new_end_seq;
0084
0085 sack = (void *)skb->data + sackoff;
0086 if (after(ntohl(sack->start_seq) - seq->offset_before,
0087 seq->correction_pos))
0088 new_start_seq = htonl(ntohl(sack->start_seq) -
0089 seq->offset_after);
0090 else
0091 new_start_seq = htonl(ntohl(sack->start_seq) -
0092 seq->offset_before);
0093
0094 if (after(ntohl(sack->end_seq) - seq->offset_before,
0095 seq->correction_pos))
0096 new_end_seq = htonl(ntohl(sack->end_seq) -
0097 seq->offset_after);
0098 else
0099 new_end_seq = htonl(ntohl(sack->end_seq) -
0100 seq->offset_before);
0101
0102 pr_debug("sack_adjust: start_seq: %u->%u, end_seq: %u->%u\n",
0103 ntohl(sack->start_seq), ntohl(new_start_seq),
0104 ntohl(sack->end_seq), ntohl(new_end_seq));
0105
0106 inet_proto_csum_replace4(&tcph->check, skb,
0107 sack->start_seq, new_start_seq, false);
0108 inet_proto_csum_replace4(&tcph->check, skb,
0109 sack->end_seq, new_end_seq, false);
0110 sack->start_seq = new_start_seq;
0111 sack->end_seq = new_end_seq;
0112 sackoff += sizeof(*sack);
0113 }
0114 }
0115
0116
0117 static unsigned int nf_ct_sack_adjust(struct sk_buff *skb,
0118 unsigned int protoff,
0119 struct nf_conn *ct,
0120 enum ip_conntrack_info ctinfo)
0121 {
0122 struct tcphdr *tcph = (void *)skb->data + protoff;
0123 struct nf_conn_seqadj *seqadj = nfct_seqadj(ct);
0124 unsigned int dir, optoff, optend;
0125
0126 optoff = protoff + sizeof(struct tcphdr);
0127 optend = protoff + tcph->doff * 4;
0128
0129 if (skb_ensure_writable(skb, optend))
0130 return 0;
0131
0132 tcph = (void *)skb->data + protoff;
0133 dir = CTINFO2DIR(ctinfo);
0134
0135 while (optoff < optend) {
0136
0137 unsigned char *op = skb->data + optoff;
0138
0139 switch (op[0]) {
0140 case TCPOPT_EOL:
0141 return 1;
0142 case TCPOPT_NOP:
0143 optoff++;
0144 continue;
0145 default:
0146
0147 if (optoff + 1 == optend ||
0148 optoff + op[1] > optend ||
0149 op[1] < 2)
0150 return 0;
0151 if (op[0] == TCPOPT_SACK &&
0152 op[1] >= 2+TCPOLEN_SACK_PERBLOCK &&
0153 ((op[1] - 2) % TCPOLEN_SACK_PERBLOCK) == 0)
0154 nf_ct_sack_block_adjust(skb, tcph, optoff + 2,
0155 optoff+op[1],
0156 &seqadj->seq[!dir]);
0157 optoff += op[1];
0158 }
0159 }
0160 return 1;
0161 }
0162
0163
0164 int nf_ct_seq_adjust(struct sk_buff *skb,
0165 struct nf_conn *ct, enum ip_conntrack_info ctinfo,
0166 unsigned int protoff)
0167 {
0168 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
0169 struct tcphdr *tcph;
0170 __be32 newseq, newack;
0171 s32 seqoff, ackoff;
0172 struct nf_conn_seqadj *seqadj = nfct_seqadj(ct);
0173 struct nf_ct_seqadj *this_way, *other_way;
0174 int res = 1;
0175
0176 this_way = &seqadj->seq[dir];
0177 other_way = &seqadj->seq[!dir];
0178
0179 if (skb_ensure_writable(skb, protoff + sizeof(*tcph)))
0180 return 0;
0181
0182 tcph = (void *)skb->data + protoff;
0183 spin_lock_bh(&ct->lock);
0184 if (after(ntohl(tcph->seq), this_way->correction_pos))
0185 seqoff = this_way->offset_after;
0186 else
0187 seqoff = this_way->offset_before;
0188
0189 newseq = htonl(ntohl(tcph->seq) + seqoff);
0190 inet_proto_csum_replace4(&tcph->check, skb, tcph->seq, newseq, false);
0191 pr_debug("Adjusting sequence number from %u->%u\n",
0192 ntohl(tcph->seq), ntohl(newseq));
0193 tcph->seq = newseq;
0194
0195 if (!tcph->ack)
0196 goto out;
0197
0198 if (after(ntohl(tcph->ack_seq) - other_way->offset_before,
0199 other_way->correction_pos))
0200 ackoff = other_way->offset_after;
0201 else
0202 ackoff = other_way->offset_before;
0203
0204 newack = htonl(ntohl(tcph->ack_seq) - ackoff);
0205 inet_proto_csum_replace4(&tcph->check, skb, tcph->ack_seq, newack,
0206 false);
0207 pr_debug("Adjusting ack number from %u->%u, ack from %u->%u\n",
0208 ntohl(tcph->seq), ntohl(newseq), ntohl(tcph->ack_seq),
0209 ntohl(newack));
0210 tcph->ack_seq = newack;
0211
0212 res = nf_ct_sack_adjust(skb, protoff, ct, ctinfo);
0213 out:
0214 spin_unlock_bh(&ct->lock);
0215
0216 return res;
0217 }
0218 EXPORT_SYMBOL_GPL(nf_ct_seq_adjust);
0219
0220 s32 nf_ct_seq_offset(const struct nf_conn *ct,
0221 enum ip_conntrack_dir dir,
0222 u32 seq)
0223 {
0224 struct nf_conn_seqadj *seqadj = nfct_seqadj(ct);
0225 struct nf_ct_seqadj *this_way;
0226
0227 if (!seqadj)
0228 return 0;
0229
0230 this_way = &seqadj->seq[dir];
0231 return after(seq, this_way->correction_pos) ?
0232 this_way->offset_after : this_way->offset_before;
0233 }
0234 EXPORT_SYMBOL_GPL(nf_ct_seq_offset);