]> Pileus Git - ~andy/linux/blobdiff - net/netfilter/nf_nat_helper.c
Linux 3.14
[~andy/linux] / net / netfilter / nf_nat_helper.c
index 85e20a9190816f348a845937b8a4fe470a917744..2840abb5bb99a7590b228f13e5769a62902f3cf3 100644 (file)
 #include <net/netfilter/nf_conntrack_helper.h>
 #include <net/netfilter/nf_conntrack_ecache.h>
 #include <net/netfilter/nf_conntrack_expect.h>
+#include <net/netfilter/nf_conntrack_seqadj.h>
 #include <net/netfilter/nf_nat.h>
 #include <net/netfilter/nf_nat_l3proto.h>
 #include <net/netfilter/nf_nat_l4proto.h>
 #include <net/netfilter/nf_nat_core.h>
 #include <net/netfilter/nf_nat_helper.h>
 
-#define DUMP_OFFSET(x) \
-       pr_debug("offset_before=%d, offset_after=%d, correction_pos=%u\n", \
-                x->offset_before, x->offset_after, x->correction_pos);
-
-static DEFINE_SPINLOCK(nf_nat_seqofs_lock);
-
-/* Setup TCP sequence correction given this change at this sequence */
-static inline void
-adjust_tcp_sequence(u32 seq,
-                   int sizediff,
-                   struct nf_conn *ct,
-                   enum ip_conntrack_info ctinfo)
-{
-       enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
-       struct nf_conn_nat *nat = nfct_nat(ct);
-       struct nf_nat_seq *this_way = &nat->seq[dir];
-
-       pr_debug("adjust_tcp_sequence: seq = %u, sizediff = %d\n",
-                seq, sizediff);
-
-       pr_debug("adjust_tcp_sequence: Seq_offset before: ");
-       DUMP_OFFSET(this_way);
-
-       spin_lock_bh(&nf_nat_seqofs_lock);
-
-       /* SYN adjust. If it's uninitialized, or this is after last
-        * correction, record it: we don't handle more than one
-        * adjustment in the window, but do deal with common case of a
-        * retransmit */
-       if (this_way->offset_before == this_way->offset_after ||
-           before(this_way->correction_pos, seq)) {
-               this_way->correction_pos = seq;
-               this_way->offset_before = this_way->offset_after;
-               this_way->offset_after += sizediff;
-       }
-       spin_unlock_bh(&nf_nat_seqofs_lock);
-
-       pr_debug("adjust_tcp_sequence: Seq_offset after: ");
-       DUMP_OFFSET(this_way);
-}
-
-/* Get the offset value, for conntrack */
-s16 nf_nat_get_offset(const struct nf_conn *ct,
-                     enum ip_conntrack_dir dir,
-                     u32 seq)
-{
-       struct nf_conn_nat *nat = nfct_nat(ct);
-       struct nf_nat_seq *this_way;
-       s16 offset;
-
-       if (!nat)
-               return 0;
-
-       this_way = &nat->seq[dir];
-       spin_lock_bh(&nf_nat_seqofs_lock);
-       offset = after(seq, this_way->correction_pos)
-                ? this_way->offset_after : this_way->offset_before;
-       spin_unlock_bh(&nf_nat_seqofs_lock);
-
-       return offset;
-}
-
 /* Frobs data inside this packet, which is linear. */
 static void mangle_contents(struct sk_buff *skb,
                            unsigned int dataoff,
@@ -142,30 +81,6 @@ static int enlarge_skb(struct sk_buff *skb, unsigned int extra)
        return 1;
 }
 
-void nf_nat_set_seq_adjust(struct nf_conn *ct, enum ip_conntrack_info ctinfo,
-                          __be32 seq, s16 off)
-{
-       if (!off)
-               return;
-       set_bit(IPS_SEQ_ADJUST_BIT, &ct->status);
-       adjust_tcp_sequence(ntohl(seq), off, ct, ctinfo);
-       nf_conntrack_event_cache(IPCT_NATSEQADJ, ct);
-}
-EXPORT_SYMBOL_GPL(nf_nat_set_seq_adjust);
-
-void nf_nat_tcp_seq_adjust(struct sk_buff *skb, struct nf_conn *ct,
-                          u32 ctinfo, int off)
-{
-       const struct tcphdr *th;
-
-       if (nf_ct_protonum(ct) != IPPROTO_TCP)
-               return;
-
-       th = (struct tcphdr *)(skb_network_header(skb)+ ip_hdrlen(skb));
-       nf_nat_set_seq_adjust(ct, ctinfo, th->seq, off);
-}
-EXPORT_SYMBOL_GPL(nf_nat_tcp_seq_adjust);
-
 /* Generic function for mangling variable-length address changes inside
  * NATed TCP connections (like the PORT XXX,XXX,XXX,XXX,XXX,XXX
  * command in FTP).
@@ -210,8 +125,8 @@ int __nf_nat_mangle_tcp_packet(struct sk_buff *skb,
                             datalen, oldlen);
 
        if (adjust && rep_len != match_len)
-               nf_nat_set_seq_adjust(ct, ctinfo, tcph->seq,
-                                     (int)rep_len - (int)match_len);
+               nf_ct_seqadj_set(ct, ctinfo, tcph->seq,
+                                (int)rep_len - (int)match_len);
 
        return 1;
 }
@@ -271,145 +186,6 @@ nf_nat_mangle_udp_packet(struct sk_buff *skb,
 }
 EXPORT_SYMBOL(nf_nat_mangle_udp_packet);
 
-/* Adjust one found SACK option including checksum correction */
-static void
-sack_adjust(struct sk_buff *skb,
-           struct tcphdr *tcph,
-           unsigned int sackoff,
-           unsigned int sackend,
-           struct nf_nat_seq *natseq)
-{
-       while (sackoff < sackend) {
-               struct tcp_sack_block_wire *sack;
-               __be32 new_start_seq, new_end_seq;
-
-               sack = (void *)skb->data + sackoff;
-               if (after(ntohl(sack->start_seq) - natseq->offset_before,
-                         natseq->correction_pos))
-                       new_start_seq = htonl(ntohl(sack->start_seq)
-                                       - natseq->offset_after);
-               else
-                       new_start_seq = htonl(ntohl(sack->start_seq)
-                                       - natseq->offset_before);
-
-               if (after(ntohl(sack->end_seq) - natseq->offset_before,
-                         natseq->correction_pos))
-                       new_end_seq = htonl(ntohl(sack->end_seq)
-                                     - natseq->offset_after);
-               else
-                       new_end_seq = htonl(ntohl(sack->end_seq)
-                                     - natseq->offset_before);
-
-               pr_debug("sack_adjust: start_seq: %d->%d, end_seq: %d->%d\n",
-                        ntohl(sack->start_seq), new_start_seq,
-                        ntohl(sack->end_seq), new_end_seq);
-
-               inet_proto_csum_replace4(&tcph->check, skb,
-                                        sack->start_seq, new_start_seq, 0);
-               inet_proto_csum_replace4(&tcph->check, skb,
-                                        sack->end_seq, new_end_seq, 0);
-               sack->start_seq = new_start_seq;
-               sack->end_seq = new_end_seq;
-               sackoff += sizeof(*sack);
-       }
-}
-
-/* TCP SACK sequence number adjustment */
-static inline unsigned int
-nf_nat_sack_adjust(struct sk_buff *skb,
-                  unsigned int protoff,
-                  struct tcphdr *tcph,
-                  struct nf_conn *ct,
-                  enum ip_conntrack_info ctinfo)
-{
-       unsigned int dir, optoff, optend;
-       struct nf_conn_nat *nat = nfct_nat(ct);
-
-       optoff = protoff + sizeof(struct tcphdr);
-       optend = protoff + tcph->doff * 4;
-
-       if (!skb_make_writable(skb, optend))
-               return 0;
-
-       dir = CTINFO2DIR(ctinfo);
-
-       while (optoff < optend) {
-               /* Usually: option, length. */
-               unsigned char *op = skb->data + optoff;
-
-               switch (op[0]) {
-               case TCPOPT_EOL:
-                       return 1;
-               case TCPOPT_NOP:
-                       optoff++;
-                       continue;
-               default:
-                       /* no partial options */
-                       if (optoff + 1 == optend ||
-                           optoff + op[1] > optend ||
-                           op[1] < 2)
-                               return 0;
-                       if (op[0] == TCPOPT_SACK &&
-                           op[1] >= 2+TCPOLEN_SACK_PERBLOCK &&
-                           ((op[1] - 2) % TCPOLEN_SACK_PERBLOCK) == 0)
-                               sack_adjust(skb, tcph, optoff+2,
-                                           optoff+op[1], &nat->seq[!dir]);
-                       optoff += op[1];
-               }
-       }
-       return 1;
-}
-
-/* TCP sequence number adjustment.  Returns 1 on success, 0 on failure */
-int
-nf_nat_seq_adjust(struct sk_buff *skb,
-                 struct nf_conn *ct,
-                 enum ip_conntrack_info ctinfo,
-                 unsigned int protoff)
-{
-       struct tcphdr *tcph;
-       int dir;
-       __be32 newseq, newack;
-       s16 seqoff, ackoff;
-       struct nf_conn_nat *nat = nfct_nat(ct);
-       struct nf_nat_seq *this_way, *other_way;
-
-       dir = CTINFO2DIR(ctinfo);
-
-       this_way = &nat->seq[dir];
-       other_way = &nat->seq[!dir];
-
-       if (!skb_make_writable(skb, protoff + sizeof(*tcph)))
-               return 0;
-
-       tcph = (void *)skb->data + protoff;
-       if (after(ntohl(tcph->seq), this_way->correction_pos))
-               seqoff = this_way->offset_after;
-       else
-               seqoff = this_way->offset_before;
-
-       if (after(ntohl(tcph->ack_seq) - other_way->offset_before,
-                 other_way->correction_pos))
-               ackoff = other_way->offset_after;
-       else
-               ackoff = other_way->offset_before;
-
-       newseq = htonl(ntohl(tcph->seq) + seqoff);
-       newack = htonl(ntohl(tcph->ack_seq) - ackoff);
-
-       inet_proto_csum_replace4(&tcph->check, skb, tcph->seq, newseq, 0);
-       inet_proto_csum_replace4(&tcph->check, skb, tcph->ack_seq, newack, 0);
-
-       pr_debug("Adjusting sequence number from %u->%u, ack from %u->%u\n",
-                ntohl(tcph->seq), ntohl(newseq), ntohl(tcph->ack_seq),
-                ntohl(newack));
-
-       tcph->seq = newseq;
-       tcph->ack_seq = newack;
-
-       return nf_nat_sack_adjust(skb, protoff, tcph, ct, ctinfo);
-}
-
 /* Setup NAT on this expected conntrack so it follows master. */
 /* If we fail to get a free NAT slot, we'll get dropped on confirm */
 void nf_nat_follow_master(struct nf_conn *ct,