root/net/netfilter/nf_conntrack_seqadj.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. nf_ct_seqadj_init
  2. nf_ct_seqadj_set
  3. nf_ct_tcp_seqadj_set
  4. nf_ct_sack_block_adjust
  5. nf_ct_sack_adjust
  6. nf_ct_seq_adjust
  7. nf_ct_seq_offset
  8. nf_conntrack_seqadj_init
  9. nf_conntrack_seqadj_fini

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 #include <linux/types.h>
   3 #include <linux/netfilter.h>
   4 #include <net/tcp.h>
   5 
   6 #include <net/netfilter/nf_conntrack.h>
   7 #include <net/netfilter/nf_conntrack_extend.h>
   8 #include <net/netfilter/nf_conntrack_seqadj.h>
   9 
  10 int nf_ct_seqadj_init(struct nf_conn *ct, enum ip_conntrack_info ctinfo,
  11                       s32 off)
  12 {
  13         enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
  14         struct nf_conn_seqadj *seqadj;
  15         struct nf_ct_seqadj *this_way;
  16 
  17         if (off == 0)
  18                 return 0;
  19 
  20         set_bit(IPS_SEQ_ADJUST_BIT, &ct->status);
  21 
  22         seqadj = nfct_seqadj(ct);
  23         this_way = &seqadj->seq[dir];
  24         this_way->offset_before  = off;
  25         this_way->offset_after   = off;
  26         return 0;
  27 }
  28 EXPORT_SYMBOL_GPL(nf_ct_seqadj_init);
  29 
  30 int nf_ct_seqadj_set(struct nf_conn *ct, enum ip_conntrack_info ctinfo,
  31                      __be32 seq, s32 off)
  32 {
  33         struct nf_conn_seqadj *seqadj = nfct_seqadj(ct);
  34         enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
  35         struct nf_ct_seqadj *this_way;
  36 
  37         if (off == 0)
  38                 return 0;
  39 
  40         if (unlikely(!seqadj)) {
  41                 WARN_ONCE(1, "Missing nfct_seqadj_ext_add() setup call\n");
  42                 return 0;
  43         }
  44 
  45         set_bit(IPS_SEQ_ADJUST_BIT, &ct->status);
  46 
  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;
  54         }
  55         spin_unlock_bh(&ct->lock);
  56         return 0;
  57 }
  58 EXPORT_SYMBOL_GPL(nf_ct_seqadj_set);
  59 
  60 void nf_ct_tcp_seqadj_set(struct sk_buff *skb,
  61                           struct nf_conn *ct, enum ip_conntrack_info ctinfo,
  62                           s32 off)
  63 {
  64         const struct tcphdr *th;
  65 
  66         if (nf_ct_protonum(ct) != IPPROTO_TCP)
  67                 return;
  68 
  69         th = (struct tcphdr *)(skb_network_header(skb) + ip_hdrlen(skb));
  70         nf_ct_seqadj_set(ct, ctinfo, th->seq, off);
  71 }
  72 EXPORT_SYMBOL_GPL(nf_ct_tcp_seqadj_set);
  73 
  74 /* Adjust one found SACK option including checksum correction */
  75 static void nf_ct_sack_block_adjust(struct sk_buff *skb,
  76                                     struct tcphdr *tcph,
  77                                     unsigned int sackoff,
  78                                     unsigned int sackend,
  79                                     struct nf_ct_seqadj *seq)
  80 {
  81         while (sackoff < sackend) {
  82                 struct tcp_sack_block_wire *sack;
  83                 __be32 new_start_seq, new_end_seq;
  84 
  85                 sack = (void *)skb->data + sackoff;
  86                 if (after(ntohl(sack->start_seq) - seq->offset_before,
  87                           seq->correction_pos))
  88                         new_start_seq = htonl(ntohl(sack->start_seq) -
  89                                         seq->offset_after);
  90                 else
  91                         new_start_seq = htonl(ntohl(sack->start_seq) -
  92                                         seq->offset_before);
  93 
  94                 if (after(ntohl(sack->end_seq) - seq->offset_before,
  95                           seq->correction_pos))
  96                         new_end_seq = htonl(ntohl(sack->end_seq) -
  97                                       seq->offset_after);
  98                 else
  99                         new_end_seq = htonl(ntohl(sack->end_seq) -
 100                                       seq->offset_before);
 101 
 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));
 105 
 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);
 113         }
 114 }
 115 
 116 /* TCP SACK sequence number adjustment */
 117 static unsigned int nf_ct_sack_adjust(struct sk_buff *skb,
 118                                       unsigned int protoff,
 119                                       struct nf_conn *ct,
 120                                       enum ip_conntrack_info ctinfo)
 121 {
 122         struct tcphdr *tcph = (void *)skb->data + protoff;
 123         struct nf_conn_seqadj *seqadj = nfct_seqadj(ct);
 124         unsigned int dir, optoff, optend;
 125 
 126         optoff = protoff + sizeof(struct tcphdr);
 127         optend = protoff + tcph->doff * 4;
 128 
 129         if (skb_ensure_writable(skb, optend))
 130                 return 0;
 131 
 132         tcph = (void *)skb->data + protoff;
 133         dir = CTINFO2DIR(ctinfo);
 134 
 135         while (optoff < optend) {
 136                 /* Usually: option, length. */
 137                 unsigned char *op = skb->data + optoff;
 138 
 139                 switch (op[0]) {
 140                 case TCPOPT_EOL:
 141                         return 1;
 142                 case TCPOPT_NOP:
 143                         optoff++;
 144                         continue;
 145                 default:
 146                         /* no partial options */
 147                         if (optoff + 1 == optend ||
 148                             optoff + op[1] > optend ||
 149                             op[1] < 2)
 150                                 return 0;
 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,
 155                                                         optoff+op[1],
 156                                                         &seqadj->seq[!dir]);
 157                         optoff += op[1];
 158                 }
 159         }
 160         return 1;
 161 }
 162 
 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)
 167 {
 168         enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
 169         struct tcphdr *tcph;
 170         __be32 newseq, newack;
 171         s32 seqoff, ackoff;
 172         struct nf_conn_seqadj *seqadj = nfct_seqadj(ct);
 173         struct nf_ct_seqadj *this_way, *other_way;
 174         int res = 1;
 175 
 176         this_way  = &seqadj->seq[dir];
 177         other_way = &seqadj->seq[!dir];
 178 
 179         if (skb_ensure_writable(skb, protoff + sizeof(*tcph)))
 180                 return 0;
 181 
 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;
 186         else
 187                 seqoff = this_way->offset_before;
 188 
 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));
 193         tcph->seq = newseq;
 194 
 195         if (!tcph->ack)
 196                 goto out;
 197 
 198         if (after(ntohl(tcph->ack_seq) - other_way->offset_before,
 199                   other_way->correction_pos))
 200                 ackoff = other_way->offset_after;
 201         else
 202                 ackoff = other_way->offset_before;
 203 
 204         newack = htonl(ntohl(tcph->ack_seq) - ackoff);
 205         inet_proto_csum_replace4(&tcph->check, skb, tcph->ack_seq, newack,
 206                                  false);
 207         pr_debug("Adjusting ack number from %u->%u, ack from %u->%u\n",
 208                  ntohl(tcph->seq), ntohl(newseq), ntohl(tcph->ack_seq),
 209                  ntohl(newack));
 210         tcph->ack_seq = newack;
 211 
 212         res = nf_ct_sack_adjust(skb, protoff, ct, ctinfo);
 213 out:
 214         spin_unlock_bh(&ct->lock);
 215 
 216         return res;
 217 }
 218 EXPORT_SYMBOL_GPL(nf_ct_seq_adjust);
 219 
 220 s32 nf_ct_seq_offset(const struct nf_conn *ct,
 221                      enum ip_conntrack_dir dir,
 222                      u32 seq)
 223 {
 224         struct nf_conn_seqadj *seqadj = nfct_seqadj(ct);
 225         struct nf_ct_seqadj *this_way;
 226 
 227         if (!seqadj)
 228                 return 0;
 229 
 230         this_way = &seqadj->seq[dir];
 231         return after(seq, this_way->correction_pos) ?
 232                  this_way->offset_after : this_way->offset_before;
 233 }
 234 EXPORT_SYMBOL_GPL(nf_ct_seq_offset);
 235 
 236 static const struct nf_ct_ext_type nf_ct_seqadj_extend = {
 237         .len    = sizeof(struct nf_conn_seqadj),
 238         .align  = __alignof__(struct nf_conn_seqadj),
 239         .id     = NF_CT_EXT_SEQADJ,
 240 };
 241 
 242 int nf_conntrack_seqadj_init(void)
 243 {
 244         return nf_ct_extend_register(&nf_ct_seqadj_extend);
 245 }
 246 
 247 void nf_conntrack_seqadj_fini(void)
 248 {
 249         nf_ct_extend_unregister(&nf_ct_seqadj_extend);
 250 }

/* [<][>][^][v][top][bottom][index][help] */