From b6c4f05aa9ff0912163778d05f2be725e4aeadc7 Mon Sep 17 00:00:00 2001
From: Lionel Gauthier <lionel.gauthier@eurecom.fr>
Date: Thu, 18 Dec 2014 18:25:31 +0000
Subject: [PATCH] Yes, it seems to work with UDP, ICMP, TCP.

git-svn-id: http://svn.eurecom.fr/openair4G/trunk@6289 818b1a75-f10b-46b9-bf7c-635c3b92a50f
---
 openair-cn/GTPV1-U/GTPUAH/xt_GTPUAH.c | 251 ++++++++++++++++++--------
 1 file changed, 177 insertions(+), 74 deletions(-)

diff --git a/openair-cn/GTPV1-U/GTPUAH/xt_GTPUAH.c b/openair-cn/GTPV1-U/GTPUAH/xt_GTPUAH.c
index dd62419d67..471860c264 100755
--- a/openair-cn/GTPV1-U/GTPUAH/xt_GTPUAH.c
+++ b/openair-cn/GTPV1-U/GTPUAH/xt_GTPUAH.c
@@ -11,10 +11,12 @@
  */
 
 #include <linux/module.h>
+#include <linux/kallsyms.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/skbuff.h>
 #include <linux/ip.h>
+#include <linux/if_ether.h>
 #include <linux/route.h> 
 #include <linux/time.h>
 #include <net/checksum.h>
@@ -36,14 +38,16 @@
 #endif
 #include "xt_GTPUAH.h"
 #if !(defined KVERSION)
-#error "Kernel version is not defined!!!! Exiting."
+#    error "Kernel version is not defined!!!! Exiting."
 #endif
+#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0444)
 //-----------------------------------------------------------------------------
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Pradip Biswas <pradip_biswas@polarisnetworks.net>");
 MODULE_DESCRIPTION("GTPu Data Path extension on netfilter");
 //-----------------------------------------------------------------------------
 static char*        _gtpuah_nf_inet_hook_2_string(int nf_inet_hookP);
+static void         _gtpuah_print_hex_octets(unsigned char* data_pP, unsigned short sizeP);
 static void         _gtpuah_tg4_add(struct sk_buff *old_skb_pP, const struct xt_action_param *par_pP);
 static unsigned int gtpuah_tg4(struct sk_buff *skb_pP, const struct xt_action_param *par_pP);
 static unsigned int gtpuah_tg6(struct sk_buff *skb_pP, const struct xt_action_param *par_pP);
@@ -56,7 +60,7 @@ static struct xt_target gtpuah_tg_reg[] __read_mostly = {
                                 .revision       = 0,
                                 .family         = NFPROTO_IPV4,
                                 .hooks          = (1 << NF_INET_POST_ROUTING) |
-                                                  (1 << NF_INET_LOCAL_IN)     ,
+                                                  (1 << NF_INET_LOCAL_IN),
                                 .table          = "mangle",
                                 .target         = gtpuah_tg4,
                                 .targetsize     = sizeof(struct xt_gtpuah_target_info),
@@ -68,7 +72,7 @@ static struct xt_target gtpuah_tg_reg[] __read_mostly = {
                                 .revision       = 0,
                                 .family         = NFPROTO_IPV6,
                                 .hooks          = (1 << NF_INET_POST_ROUTING) |
-                                                  (1 << NF_INET_LOCAL_IN)     ,
+                                                  (1 << NF_INET_LOCAL_IN),
                                 .table          = "mangle",
                                 .target         = gtpuah_tg6,
                                 .targetsize     = sizeof(struct xt_gtpuah_target_info),
@@ -77,15 +81,14 @@ static struct xt_target gtpuah_tg_reg[] __read_mostly = {
 #endif
 };
 
-struct gtpuhdr
-{
-    char flags;
-    char msgtype;
+struct gtpuhdr {
+    char      flags;
+    char      msgtype;
     u_int16_t length;
     u_int32_t tunid;
 };
+
 //-----------------------------------------------------------------------------
-#define MTU            1564
 #define GTPU_HDR_PNBIT 1
 #define GTPU_HDR_SBIT 1 << 1
 #define GTPU_HDR_EBIT 1 << 2
@@ -93,7 +96,7 @@ struct gtpuhdr
 
 #define GTPU_FAILURE 1
 #define GTPU_SUCCESS !GTPU_FAILURE
-#define GTPU_PORT 2152
+#define GTPUAH_2_PRINT_BUFFER_LEN 8192
 
 #define IP_MORE_FRAGMENTS 0x2000
 #define NIPADDR(addr) \
@@ -102,6 +105,15 @@ struct gtpuhdr
         (uint8_t)((addr & 0x00FF0000) >> 16), \
         (uint8_t)((addr & 0xFF000000) >> 24)
 //-----------------------------------------------------------------------------
+static void (*ip_local_deliver_fn_ptr)(struct sk_buff *skb);
+static char _gtpuah_print_buffer[GTPUAH_2_PRINT_BUFFER_LEN];
+INT_MODULE_PARM(tunnel_local, 0);
+MODULE_PARM_DESC(tunnel_local, "Act as a boolean, tels if the S1U tunnel(s) are both start/end local");
+INT_MODULE_PARM(gtpu_port, 2152);
+MODULE_PARM_DESC(gtpu_port, "UDP port number for S1U interface (eNB and S-GW sides)");
+INT_MODULE_PARM(mtu, 1564);
+MODULE_PARM_DESC(mtu, "MTU of the S1U IP interface");
+//-----------------------------------------------------------------------------
 static char*
 _gtpuah_nf_inet_hook_2_string(int nf_inet_hookP) {
     //-----------------------------------------------------------------------------
@@ -114,23 +126,90 @@ _gtpuah_nf_inet_hook_2_string(int nf_inet_hookP) {
         default: return "NF_INET_UNKNOWN";
     }
 }
+//-----------------------------------------------------------------------------
+void
+_gtpuah_print_hex_octets(unsigned char* data_pP, unsigned short sizeP) {
+//-----------------------------------------------------------------------------
+
+  unsigned long octet_index = 0;
+  unsigned long buffer_marker = 0;
+  unsigned char aindex;
+  struct timeval tv;
+  char timeofday[64];
+  unsigned int h,m,s;
+
+  if (data_pP == NULL) {
+    return;
+  }
+  if (sizeP > 2000) {
+      return;
+  }
+
+  do_gettimeofday(&tv);
+  h = (tv.tv_sec/3600) % 24;
+  m = (tv.tv_sec / 60) % 60;
+  s = tv.tv_sec % 60;
+  snprintf(timeofday, 64, "%02d:%02d:%02d.%06ld", h,m,s,tv.tv_usec);
+
+  buffer_marker+=snprintf(&_gtpuah_print_buffer[buffer_marker], GTPUAH_2_PRINT_BUFFER_LEN - buffer_marker,"%s------+-------------------------------------------------+\n",timeofday);
+  buffer_marker+=snprintf(&_gtpuah_print_buffer[buffer_marker], GTPUAH_2_PRINT_BUFFER_LEN - buffer_marker,"%s      |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |\n",timeofday);
+  buffer_marker+=snprintf(&_gtpuah_print_buffer[buffer_marker], GTPUAH_2_PRINT_BUFFER_LEN - buffer_marker,"%s------+-------------------------------------------------+\n",timeofday);
+  pr_info("%s",_gtpuah_print_buffer);buffer_marker = 0;
+  for (octet_index = 0; octet_index < sizeP; octet_index++) {
+    if ((octet_index % 16) == 0){
+      if (octet_index != 0) {
+          buffer_marker+=snprintf(&_gtpuah_print_buffer[buffer_marker], GTPUAH_2_PRINT_BUFFER_LEN - buffer_marker, " |\n");
+          pr_info("%s",_gtpuah_print_buffer);buffer_marker = 0;
+      }
+      buffer_marker+=snprintf(&_gtpuah_print_buffer[buffer_marker], GTPUAH_2_PRINT_BUFFER_LEN - buffer_marker, "%s %04ld |",timeofday, octet_index);
+    }
+    /*
+     * Print every single octet in hexadecimal form
+     */
+    buffer_marker+=snprintf(&_gtpuah_print_buffer[buffer_marker], GTPUAH_2_PRINT_BUFFER_LEN - buffer_marker, " %02x", data_pP[octet_index]);
+    /*
+     * Align newline and pipes according to the octets in groups of 2
+     */
+  }
+
+  /*
+   * Append enough spaces and put final pipe
+   */
+  for (aindex = octet_index; aindex < 16; ++aindex)
+    buffer_marker+=snprintf(&_gtpuah_print_buffer[buffer_marker], GTPUAH_2_PRINT_BUFFER_LEN - buffer_marker, "   ");
+    //SGI_IF_DEBUG("   ");
+  buffer_marker+=snprintf(&_gtpuah_print_buffer[buffer_marker], GTPUAH_2_PRINT_BUFFER_LEN - buffer_marker, " |\n");
+  pr_info("%s",_gtpuah_print_buffer);
+}
+
 //-----------------------------------------------------------------------------
 static void
 _gtpuah_tg4_add(struct sk_buff *old_skb_pP, const struct xt_action_param *par_pP) {
 //-----------------------------------------------------------------------------
     struct rtable  *rt              = NULL;
+    struct ethhdr  *ethhdr_p        = NULL;
     struct iphdr   *old_iph_p       = ip_hdr(old_skb_pP);
     struct iphdr   *new_iph_p       = NULL;
+    struct iphdr   *tmp_iph_p       = NULL;
     struct udphdr  *udph_p          = NULL;
     struct gtpuhdr *gtpuh_p         = NULL;
     struct sk_buff *new_skb_p       = NULL;
-    uint16_t        headroom_reqd   =  LL_MAX_HEADER + sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct gtpuhdr);
+    uint16_t        headroom_reqd   =  ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct gtpuhdr);
     uint16_t        orig_iplen = 0, udp_len = 0, ip_len = 0;
     int             flags = 0, offset = 0;
     unsigned int    addr_type       = RTN_UNSPEC;
 
+
+    if (ip_is_fragment(ip_hdr(old_skb_pP))) {
+        pr_info("GTPUAH: IP fragment, dropped\n");
+        return;
+    }
+    if (skb_linearize(old_skb_pP) < 0) {
+        pr_info("GTPUAH: skb no linearize\n");
+        return;
+    }
     if (old_skb_pP->mark == 0) {
-        pr_info("GTPUAH: _gtpuah_target_add force info_pP mark %u to skb_pP mark %u",
+        pr_info("GTPUAH: _gtpuah_target_add force info_pP mark %u to skb_pP mark %u\n",
                 old_skb_pP->mark,
                 ((const struct xt_gtpuah_target_info *)(par_pP->targinfo))->rtun);
         old_skb_pP->mark = ((const struct xt_gtpuah_target_info *)(par_pP->targinfo))->rtun;
@@ -142,19 +221,20 @@ _gtpuah_tg4_add(struct sk_buff *old_skb_pP, const struct xt_action_param *par_pP
 
 
     /* Create a new copy of the original skb...can't avoid :-( */
-    if ((orig_iplen + headroom_reqd) <= MTU) {
+    if ((orig_iplen + headroom_reqd) <= mtu) {
 
-        new_skb_p = skb_copy_expand(old_skb_pP, headroom_reqd + skb_headroom(old_skb_pP), skb_tailroom(old_skb_pP), GFP_ATOMIC);
+        new_skb_p = alloc_skb(headroom_reqd + orig_iplen, GFP_ATOMIC);
         if (new_skb_p == NULL) {
-            pr_info("GTPUAH: skb_copy_expand returned NULL");
+            pr_info("GTPUAH: skb_copy_expand returned NULL\n");
             return;
         }
-        nf_reset(new_skb_p);
-        //skb_nfmark(new_skb_p) = 0;
-        skb_init_secmark(new_skb_p);
-        skb_shinfo(new_skb_p)->gso_size = 0;
-        skb_shinfo(new_skb_p)->gso_segs = 0;
-        skb_shinfo(new_skb_p)->gso_type = 0;
+        if (skb_linearize(new_skb_p) < 0) {
+            pr_info("GTPUAH: skb no linearize\n");
+            goto free_new_skb;
+        }
+        skb_reserve(new_skb_p, headroom_reqd + orig_iplen);
+        tmp_iph_p = (void *)skb_push(new_skb_p, orig_iplen);
+        memcpy(tmp_iph_p, old_iph_p, orig_iplen);
 
         /* Add GTPu header */
         gtpuh_p          = (struct gtpuhdr*)skb_push(new_skb_p, sizeof(struct gtpuhdr));
@@ -166,8 +246,8 @@ _gtpuah_tg4_add(struct sk_buff *old_skb_pP, const struct xt_action_param *par_pP
         /* Add UDP header */
         udp_len        = sizeof(struct udphdr) + sizeof(struct gtpuhdr) + orig_iplen;
         udph_p         = (struct udphdr*)skb_push(new_skb_p, sizeof(struct udphdr));
-        udph_p->source = htons(GTPU_PORT);
-        udph_p->dest   = htons(GTPU_PORT);
+        udph_p->source = htons(gtpu_port);
+        udph_p->dest   = htons(gtpu_port);
         udph_p->len    = htons(udp_len);
         udph_p->check  = 0;
         udph_p->check  = csum_tcpudp_magic(((const struct xt_gtpuah_target_info *)(par_pP->targinfo))->laddr,
@@ -189,31 +269,24 @@ _gtpuah_tg4_add(struct sk_buff *old_skb_pP, const struct xt_action_param *par_pP
         new_iph_p->ttl      = 64;
         new_iph_p->protocol = IPPROTO_UDP;
         new_iph_p->saddr    = ((const struct xt_gtpuah_target_info *)(par_pP->targinfo))->laddr;
+        // !!!!!!!! LG TEST !!!!!!!!!!
+        //new_iph_p->saddr    = old_iph_p->saddr;
         new_iph_p->daddr    = ((const struct xt_gtpuah_target_info *)(par_pP->targinfo))->raddr;
         new_iph_p->check    = 0;
         new_iph_p->check    = ip_fast_csum((unsigned char *)new_iph_p, new_iph_p->ihl);
         skb_set_network_header(new_skb_p, 0);
 
+        skb_set_inner_network_header(new_skb_p, -ETH_HLEN);
+        skb_set_inner_transport_header(new_skb_p, -ETH_HLEN);
+
 
-        new_skb_p->ip_summed = CHECKSUM_COMPLETE;
-        new_skb_p->csum      = new_iph_p->check;
+
+        // CHECKSUM_NONE, CHECKSUM_UNNECESSARY, CHECKSUM_COMPLETE, CHECKSUM_PARTIAL
+        new_skb_p->ip_summed = CHECKSUM_NONE;
         new_skb_p->mark      = old_skb_pP->mark;
 
         switch (par_pP->hooknum) {
             case NF_INET_POST_ROUTING: {
-                struct flowi   fl    = {
-                    .u = {
-                        .ip4 = {
-                            .daddr        = new_iph_p->daddr,
-                            .flowi4_tos   = RT_TOS(new_iph_p->tos),
-                            .flowi4_scope = RT_SCOPE_UNIVERSE,
-                        }
-                    }
-                };
-                pr_info("GTPUAH: PACKET -> NF_HOOK NF_INET_POST_ROUTING/%s encapsulated src: %u.%u.%u.%u dst: %u.%u.%u.%u",
-                        gtpuah_tg_reg[0].table,
-                        NIPADDR(old_iph_p->saddr),
-                        NIPADDR(old_iph_p->daddr));
                 new_skb_p->pkt_type = PACKET_OTHERHOST; // PACKET_OUTGOING
 #ifdef CONFIG_BRIDGE_NETFILTER
                 if (new_skb_p->nf_bridge != NULL && new_skb_p->nf_bridge->mask & BRNF_BRIDGED) {
@@ -221,50 +294,80 @@ _gtpuah_tg4_add(struct sk_buff *old_skb_pP, const struct xt_action_param *par_pP
                     new_skb_p->pkt_type =PACKET_HOST;
                 }
 #endif
-                rt = ip_route_output_key(&init_net, &fl.u.ip4);
-                if (rt == NULL) {
-                    pr_info("GTPURH: Failed to route packet to dst 0x%x.", fl.u.ip4.daddr);
-                    goto free_new_skb;
-                }
-                new_skb_p->priority = rt_tos2priority(new_iph_p->tos);
-                skb_dst_drop(new_skb_p);
-                if (rt->dst.dev) {
-                    pr_info("GTPURH: dst dev name %s\n", rt->dst.dev->name);
-                    skb_dst_set(new_skb_p, dst_clone(&rt->dst));
-                    new_skb_p->dev      = skb_dst(new_skb_p)->dev;
-                    if (new_skb_p->len > dst_mtu(skb_dst(new_skb_p))) {
+                if (tunnel_local == 0) {
+                    struct flowi   fl    = {
+                        .u = {
+                            .ip4 = {
+                                .daddr        = new_iph_p->daddr,
+                                .flowi4_tos   = RT_TOS(new_iph_p->tos),
+                                .flowi4_scope = RT_SCOPE_UNIVERSE,
+                            }
+                        }
+                    };
+                    pr_info("GTPUAH: PACKET -> NF_HOOK NF_INET_POST_ROUTING/%s encapsulated src: %u.%u.%u.%u dst: %u.%u.%u.%u\n",
+                            gtpuah_tg_reg[0].table,
+                            NIPADDR(old_iph_p->saddr),
+                            NIPADDR(old_iph_p->daddr));
+
+
+                    rt = ip_route_output_key(&init_net, &fl.u.ip4);
+                    if (rt == NULL) {
+                        pr_info("GTPURH: Failed to route packet to dst 0x%x.\n", fl.u.ip4.daddr);
+                        goto free_new_skb;
+                    }
+                    new_skb_p->priority = rt_tos2priority(new_iph_p->tos);
+                    skb_dst_drop(new_skb_p);
+                    if (rt->dst.dev) {
+                        pr_info("GTPURH: dst dev name %s\n", rt->dst.dev->name);
+                        skb_dst_set(new_skb_p, dst_clone(&rt->dst));
+                        new_skb_p->dev      = skb_dst(new_skb_p)->dev;
+                        if (new_skb_p->len > dst_mtu(skb_dst(new_skb_p))) {
+                            goto free_new_skb;
+                        }
+                        nf_ct_attach(new_skb_p, old_skb_pP);
+                        ip_local_out(new_skb_p);
+                    } else {
+                        pr_info("GTPURH: rt->dst.dev == NULL\n");
                         goto free_new_skb;
                     }
-                    nf_ct_attach(new_skb_p, old_skb_pP);
+                } else { // (tunnel_local)
 
-                    ip_local_out(new_skb_p);
-                } else {
-                    pr_info("GTPURH: rt->dst.dev == NULL\n");
-                    goto free_new_skb;
-                }
+                    new_skb_p->pkt_type = PACKET_HOST;
+                    new_skb_p->priority = rt_tos2priority(new_iph_p->tos);
+                    new_skb_p->protocol = htons(ETH_P_IP);
+
+                    // fake mac header
+                    ethhdr_p = (struct ethhdr*)skb_push(new_skb_p, ETH_HLEN);
+                    skb_set_mac_header(new_skb_p, 0);
+                    memset(ethhdr_p, 0, ETH_HLEN);
+                    ethhdr_p->h_proto = ntohs(ETH_P_IP);
+
+                    //_gtpuah_print_hex_octets(new_iph_p, ip_len);
+                    //ip_local_deliver_fn_ptr(new_skb_p);
 
+                    new_skb_p->ip_summed = CHECKSUM_NONE;
+                    skb_dst_drop(new_skb_p);
+                    nf_reset(new_skb_p);
+                    /*pr_info("GTPUAH(tun): PACKET -> NF_HOOK NF_INET_POST_ROUTING/%s encapsulated src: %u.%u.%u.%u dst: %u.%u.%u.%u\n",
+                            gtpuah_tg_reg[0].table,
+                            NIPADDR(old_iph_p->saddr),
+                            NIPADDR(old_iph_p->daddr));*/
+                    if ( dev_forward_skb(old_skb_pP->dev, new_skb_p) != NET_RX_SUCCESS) {
+                        pr_info("GTPUAH(tun): dev_forward_skb failed!!!\n");
+                    }
+                    return;
+                }
             }
             break;
 
-            case  NF_INET_LOCAL_IN:
-                addr_type = RTN_LOCAL;
-                pr_info("GTPUAH: PACKET -> NF_HOOK NF_INET_LOCAL_IN/%s src: %u.%u.%u.%u dst: %u.%u.%u.%u NOT PROCESSED",
-                    gtpuah_tg_reg[0].table,
-                    NIPADDR(old_iph_p->saddr),
-                    NIPADDR(old_iph_p->daddr));
-                //NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN, new_skb_p, NULL,
-                //        skb_dst(new_skb_p)->dev, ip_local_deliver_finish);
-                goto free_new_skb;
-                break;
-
             default:
-                pr_info("GTPUAH: NF_HOOK %u not processed", par_pP->hooknum);
+                pr_info("GTPUAH: NF_HOOK %u not processed\n", par_pP->hooknum);
                 goto free_new_skb;
         }
         return;
-}
+    }
 free_new_skb:
-    pr_info("GTPUAH: PACKET DROPPED");
+    pr_info("GTPUAH: PACKET DROPPED\n");
     kfree_skb(new_skb_p);
     return ;
 }
@@ -299,11 +402,7 @@ gtpuah_tg4(struct sk_buff *skb_pP, const struct xt_action_param *par_pP) {
     }
 
     if (tgi_p->action == PARAM_GTPUAH_ACTION_ADD) {
-        pr_info("GTPUAH: PACKET -> NF_HOOK %s/%s src: %u.%u.%u.%u dst: %u.%u.%u.%u NOT PROCESSED",
-            _gtpuah_nf_inet_hook_2_string(par_pP->hooknum),
-            NIPADDR(iph_p->saddr),
-            NIPADDR(iph_p->daddr));
-        //_gtpuah_tg4_add(skb_pP, par_pP);
+        _gtpuah_tg4_add(skb_pP, par_pP);
         return NF_DROP;
     }
     return NF_ACCEPT;
@@ -313,6 +412,8 @@ gtpuah_tg4(struct sk_buff *skb_pP, const struct xt_action_param *par_pP) {
 static int
 __init gtpuah_tg_init(void) {
 //-----------------------------------------------------------------------------
+    char          *name = "ip_local_deliver";
+    unsigned long  addr = 0L;
 
     pr_info("GTPUAH: Initializing module (KVersion: %d)\n", KVERSION);
     pr_info("GTPUAH: Copyright Polaris Networks 2010-2011\n");
@@ -322,6 +423,8 @@ __init gtpuah_tg_init(void) {
 #else
     pr_info("GTPURH: IPv4 only enabled\n");
 #endif
+    addr = kallsyms_lookup_name(name);
+    ip_local_deliver_fn_ptr = addr;
     return xt_register_targets(gtpuah_tg_reg, ARRAY_SIZE(gtpuah_tg_reg));
 }
 
-- 
GitLab