]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - net/ipv4/netfilter/nf_nat_sip.c
netfilter: nf_nat_sip: add TCP support
[net-next-2.6.git] / net / ipv4 / netfilter / nf_nat_sip.c
index 07d61a57613c6a0a21685874b497559c2a005619..11b538deaaec1f996505d387d549b3de6dd4bb37 100644 (file)
@@ -1,4 +1,4 @@
-/* SIP extension for UDP NAT alteration.
+/* SIP extension for NAT alteration.
  *
  * (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar>
  * based on RR's ip_nat_ftp.c and other modules.
@@ -15,6 +15,7 @@
 #include <linux/ip.h>
 #include <net/ip.h>
 #include <linux/udp.h>
+#include <linux/tcp.h>
 
 #include <net/netfilter/nf_nat.h>
 #include <net/netfilter/nf_nat_helper.h>
@@ -29,25 +30,42 @@ MODULE_DESCRIPTION("SIP NAT helper");
 MODULE_ALIAS("ip_nat_sip");
 
 
-static unsigned int mangle_packet(struct sk_buff *skb,
+static unsigned int mangle_packet(struct sk_buff *skb, unsigned int dataoff,
                                  const char **dptr, unsigned int *datalen,
                                  unsigned int matchoff, unsigned int matchlen,
                                  const char *buffer, unsigned int buflen)
 {
        enum ip_conntrack_info ctinfo;
        struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
-
-       if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, matchoff, matchlen,
-                                     buffer, buflen))
-               return 0;
+       struct tcphdr *th;
+       unsigned int baseoff;
+
+       if (nf_ct_protonum(ct) == IPPROTO_TCP) {
+               th = (struct tcphdr *)(skb->data + ip_hdrlen(skb));
+               baseoff = ip_hdrlen(skb) + th->doff * 4;
+               matchoff += dataoff - baseoff;
+
+               if (!__nf_nat_mangle_tcp_packet(skb, ct, ctinfo,
+                                               matchoff, matchlen,
+                                               buffer, buflen, false))
+                       return 0;
+       } else {
+               baseoff = ip_hdrlen(skb) + sizeof(struct udphdr);
+               matchoff += dataoff - baseoff;
+
+               if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo,
+                                             matchoff, matchlen,
+                                             buffer, buflen))
+                       return 0;
+       }
 
        /* Reload data pointer and adjust datalen value */
-       *dptr = skb->data + ip_hdrlen(skb) + sizeof(struct udphdr);
+       *dptr = skb->data + dataoff;
        *datalen += buflen - matchlen;
        return 1;
 }
 
-static int map_addr(struct sk_buff *skb,
+static int map_addr(struct sk_buff *skb, unsigned int dataoff,
                    const char **dptr, unsigned int *datalen,
                    unsigned int matchoff, unsigned int matchlen,
                    union nf_inet_addr *addr, __be16 port)
@@ -76,11 +94,11 @@ static int map_addr(struct sk_buff *skb,
 
        buflen = sprintf(buffer, "%pI4:%u", &newaddr, ntohs(newport));
 
-       return mangle_packet(skb, dptr, datalen, matchoff, matchlen,
+       return mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen,
                             buffer, buflen);
 }
 
-static int map_sip_addr(struct sk_buff *skb,
+static int map_sip_addr(struct sk_buff *skb, unsigned int dataoff,
                        const char **dptr, unsigned int *datalen,
                        enum sip_header_types type)
 {
@@ -93,16 +111,18 @@ static int map_sip_addr(struct sk_buff *skb,
        if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, type, NULL,
                                    &matchoff, &matchlen, &addr, &port) <= 0)
                return 1;
-       return map_addr(skb, dptr, datalen, matchoff, matchlen, &addr, port);
+       return map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen,
+                       &addr, port);
 }
 
-static unsigned int ip_nat_sip(struct sk_buff *skb,
+static unsigned int ip_nat_sip(struct sk_buff *skb, unsigned int dataoff,
                               const char **dptr, unsigned int *datalen)
 {
        enum ip_conntrack_info ctinfo;
        struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
        enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
-       unsigned int dataoff, matchoff, matchlen;
+       unsigned int coff, matchoff, matchlen;
+       enum sip_header_types hdr;
        union nf_inet_addr addr;
        __be16 port;
        int request, in_header;
@@ -112,16 +132,21 @@ static unsigned int ip_nat_sip(struct sk_buff *skb,
                if (ct_sip_parse_request(ct, *dptr, *datalen,
                                         &matchoff, &matchlen,
                                         &addr, &port) > 0 &&
-                   !map_addr(skb, dptr, datalen, matchoff, matchlen,
+                   !map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen,
                              &addr, port))
                        return NF_DROP;
                request = 1;
        } else
                request = 0;
 
+       if (nf_ct_protonum(ct) == IPPROTO_TCP)
+               hdr = SIP_HDR_VIA_TCP;
+       else
+               hdr = SIP_HDR_VIA_UDP;
+
        /* Translate topmost Via header and parameters */
        if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen,
-                                   SIP_HDR_VIA, NULL, &matchoff, &matchlen,
+                                   hdr, NULL, &matchoff, &matchlen,
                                    &addr, &port) > 0) {
                unsigned int matchend, poff, plen, buflen, n;
                char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
@@ -138,7 +163,7 @@ static unsigned int ip_nat_sip(struct sk_buff *skb,
                                goto next;
                }
 
-               if (!map_addr(skb, dptr, datalen, matchoff, matchlen,
+               if (!map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen,
                              &addr, port))
                        return NF_DROP;
 
@@ -153,8 +178,8 @@ static unsigned int ip_nat_sip(struct sk_buff *skb,
                    addr.ip != ct->tuplehash[!dir].tuple.dst.u3.ip) {
                        buflen = sprintf(buffer, "%pI4",
                                        &ct->tuplehash[!dir].tuple.dst.u3.ip);
-                       if (!mangle_packet(skb, dptr, datalen, poff, plen,
-                                          buffer, buflen))
+                       if (!mangle_packet(skb, dataoff, dptr, datalen,
+                                          poff, plen, buffer, buflen))
                                return NF_DROP;
                }
 
@@ -167,8 +192,8 @@ static unsigned int ip_nat_sip(struct sk_buff *skb,
                    addr.ip != ct->tuplehash[!dir].tuple.src.u3.ip) {
                        buflen = sprintf(buffer, "%pI4",
                                        &ct->tuplehash[!dir].tuple.src.u3.ip);
-                       if (!mangle_packet(skb, dptr, datalen, poff, plen,
-                                          buffer, buflen))
+                       if (!mangle_packet(skb, dataoff, dptr, datalen,
+                                          poff, plen, buffer, buflen))
                                return NF_DROP;
                }
 
@@ -181,31 +206,45 @@ static unsigned int ip_nat_sip(struct sk_buff *skb,
                    htons(n) != ct->tuplehash[!dir].tuple.src.u.udp.port) {
                        __be16 p = ct->tuplehash[!dir].tuple.src.u.udp.port;
                        buflen = sprintf(buffer, "%u", ntohs(p));
-                       if (!mangle_packet(skb, dptr, datalen, poff, plen,
-                                          buffer, buflen))
+                       if (!mangle_packet(skb, dataoff, dptr, datalen,
+                                          poff, plen, buffer, buflen))
                                return NF_DROP;
                }
        }
 
 next:
        /* Translate Contact headers */
-       dataoff = 0;
+       coff = 0;
        in_header = 0;
-       while (ct_sip_parse_header_uri(ct, *dptr, &dataoff, *datalen,
+       while (ct_sip_parse_header_uri(ct, *dptr, &coff, *datalen,
                                       SIP_HDR_CONTACT, &in_header,
                                       &matchoff, &matchlen,
                                       &addr, &port) > 0) {
-               if (!map_addr(skb, dptr, datalen, matchoff, matchlen,
+               if (!map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen,
                              &addr, port))
                        return NF_DROP;
        }
 
-       if (!map_sip_addr(skb, dptr, datalen, SIP_HDR_FROM) ||
-           !map_sip_addr(skb, dptr, datalen, SIP_HDR_TO))
+       if (!map_sip_addr(skb, dataoff, dptr, datalen, SIP_HDR_FROM) ||
+           !map_sip_addr(skb, dataoff, dptr, datalen, SIP_HDR_TO))
                return NF_DROP;
+
        return NF_ACCEPT;
 }
 
+static void ip_nat_sip_seq_adjust(struct sk_buff *skb, s16 off)
+{
+       enum ip_conntrack_info ctinfo;
+       struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
+       const struct tcphdr *th;
+
+       if (nf_ct_protonum(ct) != IPPROTO_TCP || off == 0)
+               return;
+
+       th = (struct tcphdr *)(skb->data + ip_hdrlen(skb));
+       nf_nat_set_seq_adjust(ct, ctinfo, th->seq, off);
+}
+
 /* Handles expected signalling connections and media streams */
 static void ip_nat_sip_expected(struct nf_conn *ct,
                                struct nf_conntrack_expect *exp)
@@ -232,7 +271,7 @@ static void ip_nat_sip_expected(struct nf_conn *ct,
        }
 }
 
-static unsigned int ip_nat_sip_expect(struct sk_buff *skb,
+static unsigned int ip_nat_sip_expect(struct sk_buff *skb, unsigned int dataoff,
                                      const char **dptr, unsigned int *datalen,
                                      struct nf_conntrack_expect *exp,
                                      unsigned int matchoff,
@@ -279,8 +318,8 @@ static unsigned int ip_nat_sip_expect(struct sk_buff *skb,
        if (exp->tuple.dst.u3.ip != exp->saved_ip ||
            exp->tuple.dst.u.udp.port != exp->saved_proto.udp.port) {
                buflen = sprintf(buffer, "%pI4:%u", &newip, port);
-               if (!mangle_packet(skb, dptr, datalen, matchoff, matchlen,
-                                  buffer, buflen))
+               if (!mangle_packet(skb, dataoff, dptr, datalen,
+                                  matchoff, matchlen, buffer, buflen))
                        goto err;
        }
        return NF_ACCEPT;
@@ -290,7 +329,7 @@ err:
        return NF_DROP;
 }
 
-static int mangle_content_len(struct sk_buff *skb,
+static int mangle_content_len(struct sk_buff *skb, unsigned int dataoff,
                              const char **dptr, unsigned int *datalen)
 {
        enum ip_conntrack_info ctinfo;
@@ -312,12 +351,13 @@ static int mangle_content_len(struct sk_buff *skb,
                return 0;
 
        buflen = sprintf(buffer, "%u", c_len);
-       return mangle_packet(skb, dptr, datalen, matchoff, matchlen,
+       return mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen,
                             buffer, buflen);
 }
 
-static int mangle_sdp_packet(struct sk_buff *skb, const char **dptr,
-                            unsigned int dataoff, unsigned int *datalen,
+static int mangle_sdp_packet(struct sk_buff *skb, unsigned int dataoff,
+                            const char **dptr, unsigned int *datalen,
+                            unsigned int sdpoff,
                             enum sdp_header_types type,
                             enum sdp_header_types term,
                             char *buffer, int buflen)
@@ -326,16 +366,16 @@ static int mangle_sdp_packet(struct sk_buff *skb, const char **dptr,
        struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
        unsigned int matchlen, matchoff;
 
-       if (ct_sip_get_sdp_header(ct, *dptr, dataoff, *datalen, type, term,
+       if (ct_sip_get_sdp_header(ct, *dptr, sdpoff, *datalen, type, term,
                                  &matchoff, &matchlen) <= 0)
                return -ENOENT;
-       return mangle_packet(skb, dptr, datalen, matchoff, matchlen,
+       return mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen,
                             buffer, buflen) ? 0 : -EINVAL;
 }
 
-static unsigned int ip_nat_sdp_addr(struct sk_buff *skb, const char **dptr,
-                                   unsigned int dataoff,
-                                   unsigned int *datalen,
+static unsigned int ip_nat_sdp_addr(struct sk_buff *skb, unsigned int dataoff,
+                                   const char **dptr, unsigned int *datalen,
+                                   unsigned int sdpoff,
                                    enum sdp_header_types type,
                                    enum sdp_header_types term,
                                    const union nf_inet_addr *addr)
@@ -344,16 +384,15 @@ static unsigned int ip_nat_sdp_addr(struct sk_buff *skb, const char **dptr,
        unsigned int buflen;
 
        buflen = sprintf(buffer, "%pI4", &addr->ip);
-       if (mangle_sdp_packet(skb, dptr, dataoff, datalen, type, term,
+       if (mangle_sdp_packet(skb, dataoff, dptr, datalen, sdpoff, type, term,
                              buffer, buflen))
                return 0;
 
-       return mangle_content_len(skb, dptr, datalen);
+       return mangle_content_len(skb, dataoff, dptr, datalen);
 }
 
-static unsigned int ip_nat_sdp_port(struct sk_buff *skb,
-                                   const char **dptr,
-                                   unsigned int *datalen,
+static unsigned int ip_nat_sdp_port(struct sk_buff *skb, unsigned int dataoff,
+                                   const char **dptr, unsigned int *datalen,
                                    unsigned int matchoff,
                                    unsigned int matchlen,
                                    u_int16_t port)
@@ -362,16 +401,16 @@ static unsigned int ip_nat_sdp_port(struct sk_buff *skb,
        unsigned int buflen;
 
        buflen = sprintf(buffer, "%u", port);
-       if (!mangle_packet(skb, dptr, datalen, matchoff, matchlen,
+       if (!mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen,
                           buffer, buflen))
                return 0;
 
-       return mangle_content_len(skb, dptr, datalen);
+       return mangle_content_len(skb, dataoff, dptr, datalen);
 }
 
-static unsigned int ip_nat_sdp_session(struct sk_buff *skb, const char **dptr,
-                                      unsigned int dataoff,
-                                      unsigned int *datalen,
+static unsigned int ip_nat_sdp_session(struct sk_buff *skb, unsigned int dataoff,
+                                      const char **dptr, unsigned int *datalen,
+                                      unsigned int sdpoff,
                                       const union nf_inet_addr *addr)
 {
        char buffer[sizeof("nnn.nnn.nnn.nnn")];
@@ -379,12 +418,12 @@ static unsigned int ip_nat_sdp_session(struct sk_buff *skb, const char **dptr,
 
        /* Mangle session description owner and contact addresses */
        buflen = sprintf(buffer, "%pI4", &addr->ip);
-       if (mangle_sdp_packet(skb, dptr, dataoff, datalen,
+       if (mangle_sdp_packet(skb, dataoff, dptr, datalen, sdpoff,
                               SDP_HDR_OWNER_IP4, SDP_HDR_MEDIA,
                               buffer, buflen))
                return 0;
 
-       switch (mangle_sdp_packet(skb, dptr, dataoff, datalen,
+       switch (mangle_sdp_packet(skb, dataoff, dptr, datalen, sdpoff,
                                  SDP_HDR_CONNECTION_IP4, SDP_HDR_MEDIA,
                                  buffer, buflen)) {
        case 0:
@@ -401,14 +440,13 @@ static unsigned int ip_nat_sdp_session(struct sk_buff *skb, const char **dptr,
                return 0;
        }
 
-       return mangle_content_len(skb, dptr, datalen);
+       return mangle_content_len(skb, dataoff, dptr, datalen);
 }
 
 /* So, this packet has hit the connection tracking matching code.
    Mangle it, and change the expectation to match the new version. */
-static unsigned int ip_nat_sdp_media(struct sk_buff *skb,
-                                    const char **dptr,
-                                    unsigned int *datalen,
+static unsigned int ip_nat_sdp_media(struct sk_buff *skb, unsigned int dataoff,
+                                    const char **dptr, unsigned int *datalen,
                                     struct nf_conntrack_expect *rtp_exp,
                                     struct nf_conntrack_expect *rtcp_exp,
                                     unsigned int mediaoff,
@@ -456,7 +494,8 @@ static unsigned int ip_nat_sdp_media(struct sk_buff *skb,
 
        /* Update media port. */
        if (rtp_exp->tuple.dst.u.udp.port != rtp_exp->saved_proto.udp.port &&
-           !ip_nat_sdp_port(skb, dptr, datalen, mediaoff, medialen, port))
+           !ip_nat_sdp_port(skb, dataoff, dptr, datalen,
+                            mediaoff, medialen, port))
                goto err2;
 
        return NF_ACCEPT;
@@ -471,6 +510,7 @@ err1:
 static void __exit nf_nat_sip_fini(void)
 {
        rcu_assign_pointer(nf_nat_sip_hook, NULL);
+       rcu_assign_pointer(nf_nat_sip_seq_adjust_hook, NULL);
        rcu_assign_pointer(nf_nat_sip_expect_hook, NULL);
        rcu_assign_pointer(nf_nat_sdp_addr_hook, NULL);
        rcu_assign_pointer(nf_nat_sdp_port_hook, NULL);
@@ -482,12 +522,14 @@ static void __exit nf_nat_sip_fini(void)
 static int __init nf_nat_sip_init(void)
 {
        BUG_ON(nf_nat_sip_hook != NULL);
+       BUG_ON(nf_nat_sip_seq_adjust_hook != NULL);
        BUG_ON(nf_nat_sip_expect_hook != NULL);
        BUG_ON(nf_nat_sdp_addr_hook != NULL);
        BUG_ON(nf_nat_sdp_port_hook != NULL);
        BUG_ON(nf_nat_sdp_session_hook != NULL);
        BUG_ON(nf_nat_sdp_media_hook != NULL);
        rcu_assign_pointer(nf_nat_sip_hook, ip_nat_sip);
+       rcu_assign_pointer(nf_nat_sip_seq_adjust_hook, ip_nat_sip_seq_adjust);
        rcu_assign_pointer(nf_nat_sip_expect_hook, ip_nat_sip_expect);
        rcu_assign_pointer(nf_nat_sdp_addr_hook, ip_nat_sdp_addr);
        rcu_assign_pointer(nf_nat_sdp_port_hook, ip_nat_sdp_port);