Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
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 /* Adjust one found SACK option including checksum correction */
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 /* TCP SACK sequence number adjustment */
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         /* Usually: option, length. */
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             /* no partial options */
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 /* TCP sequence number adjustment.  Returns 1 on success, 0 on failure */
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);