From cc5cc5631e9df4ef0eee9c26705208dfcf035e8c Mon Sep 17 00:00:00 2001
From: Ido Barnea <ibarnea@cisco.com>
Date: Wed, 6 Jul 2016 11:04:52 +0300
Subject: NAT seq num randomization working version - Missing some
 functionality

---
 src/bp_gtest.cpp                      |  72 ++++++---
 src/bp_sim.cpp                        | 284 ++++++++++++++++++++++------------
 src/bp_sim.h                          | 114 +++++++++-----
 src/common/Network/Packet/TcpHeader.h |   5 +
 src/latency.cpp                       |  17 +-
 src/latency.h                         |  34 ++--
 src/main_dpdk.cpp                     |   5 +-
 src/nat_check.cpp                     |  78 ++++++++--
 src/nat_check.h                       |  21 ++-
 9 files changed, 438 insertions(+), 192 deletions(-)

(limited to 'src')

diff --git a/src/bp_gtest.cpp b/src/bp_gtest.cpp
index 79ea2458..3c6b2e40 100755
--- a/src/bp_gtest.cpp
+++ b/src/bp_gtest.cpp
@@ -2416,7 +2416,7 @@ public:
                 assert(ipv4->getTimeToLive()==255);
                 /* ip option packet */
                 printf(" rx got ip option packet ! \n");
-                mg->handle_packet_ipv4(option,ipv4);
+                mg->handle_packet_ipv4(option, ipv4, true);
                 delay(10);          // delay for queue flush 
                 mg->handle_aging(); // flush the RxRing 
             }
@@ -2481,8 +2481,10 @@ protected:
       m_flow_info.Delete();
   }
 public:
-    CCapFileFlowInfo m_flow_info;
+    void load_cap_file_errors_helper(std::string cap_file, enum CCapFileFlowInfo::load_cap_file_err expect);
 
+public:
+    CCapFileFlowInfo m_flow_info;
 };
 
 TEST_F(file_flow_info, f1) {
@@ -2612,30 +2614,58 @@ TEST_F(file_flow_info, http_add_ipv6_option) {
     po->preview.set_ipv6_mode_enable(false);
 }
 
+void file_flow_info::load_cap_file_errors_helper(std::string cap_file, enum CCapFileFlowInfo::load_cap_file_err expect) {
+    enum CCapFileFlowInfo::load_cap_file_err err;
+
+    err = m_flow_info.load_cap_file(cap_file, 1, 0);
+    if (err == 0) err = m_flow_info.is_valid_template_load_time();
+    if (err != expect) {
+        printf("Error in testing file %s. Expected error to be %d, but it is %d\n", cap_file.c_str(), expect, err);
+    }
+    assert (err == expect);
+}
+
 // Test error conditions when loading cap file
 TEST_F(file_flow_info, load_cap_file_errors) {
-    enum CCapFileFlowInfo::load_cap_file_err err;
-    CParserOption * po =&CGlobalInfo::m_options;
-    po->m_learn_mode = CParserOption::LEARN_MODE_TCP_ACK;
+    CParserOption *po = &CGlobalInfo::m_options;
 
-    // file does not exist
-    err = m_flow_info.load_cap_file("/tmp/not_exist",1,0);
-    assert (err == CCapFileFlowInfo::kFileNotExist);
+    po->m_learn_mode = CParserOption::LEARN_MODE_DISABLED;
+    load_cap_file_errors_helper("/tmp/not_exist", CCapFileFlowInfo::kFileNotExist);
     // file format not supported
-    err = m_flow_info.load_cap_file("cap2/dns.yaml",1,0);
-    assert (err == CCapFileFlowInfo::kFileNotExist);
-    // udp in tcp learn mode
-    err = m_flow_info.load_cap_file("./cap2/dns.pcap",1,0);
-    assert (err == CCapFileFlowInfo::kNoTCPFromServer);
-    // First TCP packet without syn
-    err = m_flow_info.load_cap_file("./exp/tcp_no_syn.pcap",1,0);
-    assert (err == CCapFileFlowInfo::kNoSyn);
-    // TCP flags offset is too big
-    err = m_flow_info.load_cap_file("./exp/many_ip_options.pcap",1,0);
-    assert (err == CCapFileFlowInfo::kTCPOffsetTooBig);
+    load_cap_file_errors_helper("cap2/dns.yaml", CCapFileFlowInfo::kFileNotExist);
+    load_cap_file_errors_helper("cap2/dns.pcap", CCapFileFlowInfo::kOK);
+    load_cap_file_errors_helper("./exp/tcp_no_syn.pcap", CCapFileFlowInfo::kOK);
+    load_cap_file_errors_helper("./exp/many_ip_options.pcap", CCapFileFlowInfo::kOK);
     // Non IP packet
-    err = m_flow_info.load_cap_file("./exp/bad_not_ip.pcap",1,0);
-    assert (err == CCapFileFlowInfo::kPktProcessFail);
+    load_cap_file_errors_helper("./exp/bad_not_ip.pcap", CCapFileFlowInfo::kPktProcessFail);
+    load_cap_file_errors_helper("./exp/tcp_2_pkts.pcap", CCapFileFlowInfo::kOK);
+    // more than 1 flow in cap file
+    load_cap_file_errors_helper("./exp/syn_attack.pcap",  CCapFileFlowInfo::kCapFileErr);
+
+    po->m_learn_mode = CParserOption::LEARN_MODE_IP_OPTION;
+    load_cap_file_errors_helper("cap2/dns.pcap", CCapFileFlowInfo::kOK);
+    load_cap_file_errors_helper("./exp/tcp_no_syn.pcap", CCapFileFlowInfo::kOK);
+    load_cap_file_errors_helper("./exp/many_ip_options.pcap", CCapFileFlowInfo::kIPOptionNotAllowed);
+    load_cap_file_errors_helper("./exp/tcp_2_pkts.pcap", CCapFileFlowInfo::kOK);
+
+    po->m_learn_mode = CParserOption::LEARN_MODE_TCP_ACK_NO_SERVER_SEQ_RAND;
+    // udp in tcp learn mode
+    load_cap_file_errors_helper("cap2/dns.pcap", CCapFileFlowInfo::kNoTCPFromServer);
+    // no SYN in first packet
+    load_cap_file_errors_helper("./exp/tcp_no_syn.pcap", CCapFileFlowInfo::kNoSyn);
+    // TCP flags offset is too big. We don't allow IP option, so can comment this.
+    // open this if we do allow IP options in the future
+    //    load_cap_file_errors_helper("./exp/many_ip_options.pcap", CCapFileFlowInfo::kTCPOffsetTooBig);
+    load_cap_file_errors_helper("./exp/tcp_2_pkts.pcap", CCapFileFlowInfo::kOK);
+    load_cap_file_errors_helper("./exp/no_tcp_syn_ack.pcap",  CCapFileFlowInfo::kOK);
+
+    po->m_learn_mode = CParserOption::LEARN_MODE_TCP_ACK;
+    // too short. only two packets
+    load_cap_file_errors_helper("./exp/tcp_2_pkts.pcap", CCapFileFlowInfo::kTCPLearnModeBadFlow);
+    // no SYN+ACK
+    load_cap_file_errors_helper("./exp/no_tcp_syn_ack.pcap",  CCapFileFlowInfo::kNoTCPSynAck);
+    // IPG between TCP handshake packets too low
+    load_cap_file_errors_helper("./exp/tcp_low_ipg.pcap",  CCapFileFlowInfo::kTCPIpgTooLow);
 }
 
 //////////////////////////////////////////////////////////////
diff --git a/src/bp_sim.cpp b/src/bp_sim.cpp
index b93c0461..f46f2824 100755
--- a/src/bp_sim.cpp
+++ b/src/bp_sim.cpp
@@ -1938,42 +1938,114 @@ typedef CTmpFlowInfo * flow_tmp_t;
 typedef std::map<uint16_t, flow_tmp_t> flow_tmp_map_t;
 typedef flow_tmp_map_t::iterator flow_tmp_map_iter_t;
 
-
-
-bool CCapFileFlowInfo::is_valid_template_load_time(std::string & err){
-    err="";
+enum CCapFileFlowInfo::load_cap_file_err CCapFileFlowInfo::is_valid_template_load_time(){
    int i;
     for (i=0; i<Size(); i++) {
         CFlowPktInfo * lp= GetPacket((uint32_t)i);
         CPacketIndication * lpd=&lp->m_pkt_indication;
         if ( lpd->getEtherOffset() !=0 ){
-            err=" supported template Ether offset start is 0 \n";
-            return (false);
-        }
-        if ( lpd->getIpOffset() !=14 ){
-            err=" supported template ip offset is 14 \n";
-            return (false);
+            fprintf(stderr, "Error: Bad CAP file. Ether offset start is not 0 in packet %d \n", i+1);
+            return kPktNotSupp;
         }
-        if ( lpd->is_ipv6() ){
-            if ( lpd->getTcpOffset() != (14+40) ){
-                err=" supported template tcp/udp offset is 54, no ipv6 option header is supported \n";
-                return (false);
+
+        if  ( CGlobalInfo::is_learn_mode() ) {
+            // We change TCP ports. Need them to be in first 64 byte mbuf.
+            // Since we also add IP option, and rx-check feature might add another IP option, better not allow
+            // OP options in this mode. If needed this limitation can be refined a bit.
+            if ( lpd->getTcpOffset() - lpd->getIpOffset() != 20 ) {
+                fprintf(stderr, "Error: Bad CAP file. In learn (NAT) mode, no IP options allowed \n");
+                return kIPOptionNotAllowed;
             }
-        }else{
-            if ( lpd->getTcpOffset() != (14+20) ){
-                err=" supported template tcp/udp offset is 34, no ipv4 option is allowed in this version \n";
-                return (false);
+            if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP)) {
+                if (lpd->getIpProto() != IPPROTO_TCP && !lpd->m_desc.IsInitSide()) {
+                    fprintf(stderr, "Error: In the chosen learn mode, all packets from server to client in CAP file should be TCP.\n");
+                    fprintf(stderr, "       Please give different CAP file, or try different --learn-mode\n");
+                    return kNoTCPFromServer;
+                }
             }
         }
     }
 
     if  ( CGlobalInfo::is_learn_mode() ) {
-        if ( GetPacket(0)->m_pkt_indication.m_desc.IsPluginEnable() ) {
-            err="plugins are not supported with --learn mode \n";
-            return(false);
+        CPacketIndication &pkt_0_indication = GetPacket(0)->m_pkt_indication;
+
+        if ( pkt_0_indication.m_desc.IsPluginEnable() ) {
+            fprintf(stderr, "Error: plugins are not supported with --learn mode \n");
+            return kPlugInWithLearn;
+        }
+
+        if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP)) {
+            if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) {
+                if (Size() < 3) {
+                    fprintf(stderr
+                            , "Error: In the chosen learn mode, need at least the 3 TCP handshake packets.\n");
+                    fprintf(stderr
+                            , "       Please give different CAP file, or try different --learn-mode\n");
+                    return kTCPLearnModeBadFlow;
+                }
+            }
+            CPacketIndication &pkt_1_indication = GetPacket(1)->m_pkt_indication;
+
+
+            // verify first packet is TCP SYN from client
+            TCPHeader *tcp = (TCPHeader *)(pkt_0_indication.getBasePtr() + pkt_0_indication.getTcpOffset());
+            if ( (! pkt_0_indication.m_desc.IsInitSide()) || (! tcp->getSynFlag()) ) {
+                fprintf(stderr, "Error: In the chosen learn mode, first TCP packet should be SYN from client side.\n");
+                fprintf(stderr, "       In cap file, first packet side direction is %s. TCP header is:\n"
+                        , pkt_0_indication.m_desc.IsInitSide() ? "outside":"inside");
+                tcp->dump(stderr);
+                fprintf(stderr, "       Please give different CAP file, or try different --learn-mode\n");
+                return kNoSyn;
+            }
+
+            // We want at least the TCP flags to be inside first mbuf
+            if (pkt_0_indication.getTcpOffset() + 14 > FIRST_PKT_SIZE) {
+                fprintf(stderr
+                        , "Error: In the chosen learn mode, TCP flags offset should be less than %d, but it is %d.\n"
+                        , FIRST_PKT_SIZE, pkt_0_indication.getTcpOffset() + 14);
+                fprintf(stderr, "       Please give different CAP file, or try different --learn-mode\n");
+                return kTCPOffsetTooBig;
+            }
+            if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) {
+                // To support TCP seq randomization from server to client, we need second packet in flow to be the server SYN+ACK
+                bool error = false;
+                if (pkt_1_indication.getIpProto() != IPPROTO_TCP) {
+                    error = true;
+                } else {
+                    TCPHeader *tcp = (TCPHeader *)(pkt_1_indication.getBasePtr() + pkt_1_indication.getTcpOffset());
+                    if ( (! tcp->getSynFlag()) ||  (! tcp->getAckFlag()) || ( pkt_1_indication.m_desc.IsInitSide())) {
+                        error = true;
+                    }
+                }
+                if (error) {
+                    fprintf(stderr, "Error: In the chosen learn mode, second packet should be SYN+ACK from server.\n");
+                    fprintf(stderr, "       Please give different CAP file, or try different --learn-mode\n");
+                    return kNoTCPSynAck;
+                }
+
+                CPacketIndication &pkt_2_indication = GetPacket(2)->m_pkt_indication;
+                if ( (! pkt_2_indication.m_desc.IsInitSide()) ) {
+                    fprintf(stderr
+                            , "Error: Wrong third packet. In the chosen learn mode, need at least the 3 TCP handshake packets.\n");
+                    fprintf(stderr
+                            , "       Please give different CAP file, or try different --learn-mode\n");
+                    return kTCPLearnModeBadFlow;
+                }
+                if ((pkt_0_indication.m_cap_ipg < LEARN_MODE_MIN_IPG / 1000) || (pkt_1_indication.m_cap_ipg < LEARN_MODE_MIN_IPG / 1000)) {
+                    fprintf(stderr
+                            , "Error: Bad cap file timings. In the chosen learn mode");
+                    fprintf(stderr, "IPG between TCP handshake packets should be at least %d msec.\n", LEARN_MODE_MIN_IPG);
+                    fprintf(stderr, "       Current delay is %f between second and first, %f between third and second"
+                            , pkt_0_indication.m_cap_ipg, pkt_1_indication.m_cap_ipg);
+                    fprintf(stderr
+                            , "       Please give different CAP file, try different --learn-mode, or edit ipg parameters in template file\n");
+                    return kTCPIpgTooLow;
+                }
+            }
         }
     }
-    return(true);
+
+    return(kOK);
 }
 
 
@@ -2061,6 +2133,13 @@ void CCapFileFlowInfo::update_info(){
         if ( lp->m_pkt_indication.m_desc.IsBiDirectionalFlow() ){
             lp->mask_as_learn();
         }
+
+        if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) {
+            // In this mode, we need to see the SYN+ACK as well.
+            lp = GetPacket(1);
+            assert(lp);
+            lp->m_pkt_indication.setTTL(TTL_RESERVE_DUPLICATE);
+        }
     }
 
     if ( ft.empty() )
@@ -2100,7 +2179,6 @@ enum CCapFileFlowInfo::load_cap_file_err CCapFileFlowInfo::load_cap_file(std::st
     m_total_errors=0;
     CFlow * first_flow=0;
     bool first_flow_fif_is_swap=false;
-
     bool time_was_set=false;
     double last_time=0.0;
     CCapPktRaw raw_packet;
@@ -2175,35 +2253,10 @@ enum CCapFileFlowInfo::load_cap_file_err CCapFileFlowInfo::load_cap_file(std::st
                             m_total_errors++;
                         }
                     }
-
-                    if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) {
-                        // in this mode, first TCP packet must be SYN from client.
-                        if (pkt_indication.getIpProto() == IPPROTO_TCP) {
-                            TCPHeader *tcp = (TCPHeader *)(pkt_indication.getBasePtr() + pkt_indication.getTcpOffset());
-                            if ( (! pkt_indication.m_desc.IsInitSide()) || (! tcp->getSynFlag()) ) {
-                                fprintf(stderr, "Error: In the chosen learn mode, first TCP packet should be SYN from client side.\n");
-                                fprintf(stderr, "       In cap file, first packet side direction is %s. TCP header is:\n", pkt_indication.m_desc.IsInitSide() ? "outside":"inside");
-                                tcp->dump(stderr);
-                                fprintf(stderr, "       Please give different CAP file, or try different --learn-mode\n");
-
-                                return kNoSyn;
-                            }
-                            // We want at least the TCP flags to be inside first mbuf
-                            if (pkt_indication.getTcpOffset() + 14 > FIRST_PKT_SIZE) {
-                                fprintf(stderr, "Error: In the chosen learn mode, first TCP packet TCP flags offset should be less than %d, but it is %d.\n"
-                                       , FIRST_PKT_SIZE, pkt_indication.getTcpOffset() + 14);
-                                fprintf(stderr, "       Please give different CAP file, or try different --learn-mode\n");
-                                return kTCPOffsetTooBig;
-                            }
-                        }
-                    }
-
                 }else{ /* no FIF */
-
                     pkt_indication.m_desc.SetFlowId(lpflow->flow_id);
 
                     if ( multi_flow_enable ==false ){
-
                         if (lpflow == first_flow) {
                             // add to
                             bool init_side=
@@ -2225,16 +2278,6 @@ enum CCapFileFlowInfo::load_cap_file_err CCapFileFlowInfo::load_cap_file(std::st
 
                     }
                 }
-
-                if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) {
-                    // This test must be down here, after initializing init side indication
-                    if (pkt_indication.getIpProto() != IPPROTO_TCP && !pkt_indication.m_desc.IsInitSide()) {
-                        fprintf(stderr, "Error: In the chosen learn mode, all packets from server to client in CAP file should be TCP.\n");
-                        fprintf(stderr, "       Please give different CAP file, or try different --learn-mode\n");
-                        return kNoTCPFromServer;
-                    }
-                }
-
             }else{
                 fprintf(stderr, "ERROR packet %d is not supported, should be Ethernet/IP(0x0800)/(TCP|UDP) format try to convert it using Wireshark !\n",cnt);
                 return kPktNotSupp;
@@ -2245,7 +2288,6 @@ enum CCapFileFlowInfo::load_cap_file_err CCapFileFlowInfo::load_cap_file(std::st
         }
     }
 
-
     /* set the last */
     CFlowPktInfo * last_pkt =GetPacket((uint32_t)(Size()-1));
     last_pkt->m_pkt_indication.m_desc.SetIsLastPkt(true);
@@ -2261,6 +2303,7 @@ enum CCapFileFlowInfo::load_cap_file_err CCapFileFlowInfo::load_cap_file(std::st
 
 
 
+        printf("%d: IPG:%f", i, lp_prev->m_pkt_indication.m_cap_ipg); //??? remove
         if ( lp->m_pkt_indication.m_desc.IsInitSide() !=
              lp_prev->m_pkt_indication.m_desc.IsInitSide()) {
             lp_prev->m_pkt_indication.m_desc.SetRtt(true);
@@ -3122,11 +3165,8 @@ bool CFlowGeneratorRec::Create(CFlowYamlInfo * info,
     int res=m_flow_info.load_cap_file(info->m_name.c_str(),_id,m_info->m_plugin_id);
     if ( res==0 ) {
         fixup_ipg_if_needed();
-        std::string  err;
-        /* verify that template are valid */
-        bool is_valid=m_flow_info.is_valid_template_load_time(err);
-        if (!is_valid) {
-            printf("\n ERROR template file is not valid  '%s' \n",err.c_str());
+
+        if (m_flow_info.is_valid_template_load_time() != 0) {
             return (false);
         }
         m_flow_info.update_info();
@@ -3700,11 +3740,11 @@ inline int CNodeGenerator::teardown(CFlowGenListPerThread * thread,
     }
     return (0);
 }
-                                           
+
 
 
 template<int SCH_MODE>
-inline int CNodeGenerator::flush_file_realtime(dsec_t max_time, 
+inline int CNodeGenerator::flush_file_realtime(dsec_t max_time,
                                         dsec_t d_time,
                                         bool always,
                                         CFlowGenListPerThread * thread,
@@ -3723,9 +3763,9 @@ inline int CNodeGenerator::flush_file_realtime(dsec_t max_time,
 
     sch_state_t state = scINIT;
     node = m_p_queue.top();
-    n_time = node->m_time + offset; 
+    n_time = node->m_time + offset;
     cur_time = now_sec();
-    
+
     while (state!=scTERMINATE) {
 
          switch (state) {
@@ -3751,7 +3791,7 @@ inline int CNodeGenerator::flush_file_realtime(dsec_t max_time,
                          break;
                      }
                      node = m_p_queue.top();
-                     n_time = node->m_time + offset; 
+                     n_time = node->m_time + offset;
 
                      if ((n_time-cur_time)>EAT_WINDOW_DTIME) {
                        state=scINIT;
@@ -3761,7 +3801,7 @@ inline int CNodeGenerator::flush_file_realtime(dsec_t max_time,
                  break;
 
          case scWAIT:
-                do_sleep(cur_time,thread,n_time); // estimate  loop 
+                do_sleep(cur_time,thread,n_time); // estimate  loop
                 state=scWORK;
                 break;
          default:
@@ -3772,7 +3812,7 @@ inline int CNodeGenerator::flush_file_realtime(dsec_t max_time,
     return (teardown(thread,always,old_offset,offset));
 }
 
-FORCE_NO_INLINE int CNodeGenerator::flush_file_sim(dsec_t max_time, 
+FORCE_NO_INLINE int CNodeGenerator::flush_file_sim(dsec_t max_time,
                                         dsec_t d_time,
                                         bool always,
                                         CFlowGenListPerThread * thread,
@@ -3799,7 +3839,7 @@ FORCE_NO_INLINE int CNodeGenerator::flush_file_sim(dsec_t max_time,
     return (teardown(thread,always,old_offset,0));
 }
 
-int CNodeGenerator::flush_file(dsec_t max_time, 
+int CNodeGenerator::flush_file(dsec_t max_time,
                                dsec_t d_time,
                                bool always,
                                CFlowGenListPerThread * thread,
@@ -3820,7 +3860,7 @@ int CNodeGenerator::flush_file(dsec_t max_time,
 
 void CNodeGenerator::handle_flow_pkt(CGenNode *node, CFlowGenListPerThread *thread) {
 
-    /*repeat and NAT is not supported */
+    /*repeat and NAT is not supported together */
     if ( node->is_nat_first_state()  ) {
         node->set_nat_wait_state();
         flush_one_node_to_file(node);
@@ -3831,7 +3871,7 @@ void CNodeGenerator::handle_flow_pkt(CGenNode *node, CFlowGenListPerThread *thre
         if ( node->is_nat_wait_state() ) {
             if (node->is_responder_pkt()) {
                 m_p_queue.pop();
-                /* time out, need to free the flow and remove the association , we didn't get convertion yet*/
+                /* time out, need to free the flow and remove the association , we didn't get conversion yet*/
                 thread->terminate_nat_flows(node);
                 return;
 
@@ -3842,7 +3882,22 @@ void CNodeGenerator::handle_flow_pkt(CGenNode *node, CFlowGenListPerThread *thre
                 #endif
             }
         } else {
-            assert(0);
+            if ( node->is_nat_wait_ack_state() ) {
+                if (node->is_initiator_pkt()) {
+                    m_p_queue.pop();
+                    /* time out, need to free the flow and remove the association , we didn't get conversion yet*/
+                    thread->terminate_nat_flows(node);
+                    return;
+
+                } else {
+                    flush_one_node_to_file(node);
+#ifdef _DEBUG
+                    update_stats(node);
+#endif
+                }
+            } else {
+                assert(0);
+            }
         }
     }
     m_p_queue.pop();
@@ -4146,38 +4201,72 @@ void CFlowGenListPerThread::handle_latency_pkt_msg(CGenNodeLatencyPktInfo * msg)
     m_node_gen.m_v_if->send_one_pkt((pkt_dir_t)msg->m_dir,msg->m_pkt);
 }
 
-
 void CFlowGenListPerThread::handle_nat_msg(CGenNodeNatInfo * msg){
     int i;
+    bool first = true, second = true;
+
     for (i=0; i<msg->m_cnt; i++) {
+        first = true;
+        second = true;
         CNatFlowInfo * nat_msg=&msg->m_data[i];
         CGenNode * node=m_flow_id_to_node_lookup.lookup(nat_msg->m_fid);
         if (!node) {
-            /* this should be move to a notification module */
-            #ifdef NAT_TRACE_
+            /* this should be moved to a notification module */
+#ifdef NAT_TRACE_
             printf(" ERORR not valid flow_id %d probably flow was aged  \n",nat_msg->m_fid);
-            #endif
+#endif
             m_stats.m_nat_lookup_no_flow_id++;
             continue;
         }
-        #ifdef NAT_TRACE_
-        printf(" %.03f  RX :set node %p:%x  %x:%x:%x \n",now_sec() ,node,nat_msg->m_fid,nat_msg->m_external_ip,nat_msg->m_external_ip_server,nat_msg->m_external_port);
-        #endif
-        node->set_nat_ipv4_addr(nat_msg->m_external_ip);
-        node->set_nat_ipv4_port(nat_msg->m_external_port);
-        node->set_nat_ipv4_addr_server(nat_msg->m_external_ip_server);
-
-        assert(node->is_nat_wait_state());
-        if ( CGlobalInfo::is_learn_verify_mode() ){
-            if (!node->is_external_is_eq_to_internal_ip() ){
-                m_stats.m_nat_flow_learn_error++;
+
+        // Calculate diff between tcp seq of SYN packet, and TCP ack of SYN+ACK packet
+        // For supporting firewalls who do TCP seq num randomization
+        if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP)) {
+            if (node->is_nat_wait_state()) {
+                char *syn_pkt = node->m_flow_info->GetPacket(0)->m_packet->raw;
+                TCPHeader *tcp = (TCPHeader *)(syn_pkt + node->m_pkt_info->m_pkt_indication.getFastTcpOffset());
+                node->set_nat_tcp_seq_diff_client(nat_msg->m_tcp_seq - tcp->getSeqNumber());
+                if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) {
+                    node->set_nat_wait_ack_state();
+                    second = false;
+                } else {
+                    node->set_nat_learn_state();
+                }
+            } else {
+                char *syn_ack_pkt = node->m_flow_info->GetPacket(1)->m_packet->raw;
+                TCPHeader *tcp = (TCPHeader *)(syn_ack_pkt + node->m_pkt_info->m_pkt_indication.getFastTcpOffset());
+                node->set_nat_tcp_seq_diff_server(nat_msg->m_tcp_seq - tcp->getSeqNumber());
+                assert(node->is_nat_wait_ack_state());
+                node->set_nat_learn_state();
+                first = false;
             }
+        } else {
+            assert(node->is_nat_wait_state());
+            node->set_nat_learn_state();
         }
-        node->set_nat_learn_state();
-        /* remove from the hash */
-        m_flow_id_to_node_lookup.remove_no_lookup(nat_msg->m_fid);
-        m_stats.m_nat_lookup_remove_flow_id++;
 
+        if (first) {
+#ifdef NAT_TRACE_
+            printf(" %.03f  RX :set node %p:%x  %x:%x TCP diff %x\n"
+                   , now_sec(), node,nat_msg->m_fid, nat_msg->m_external_ip, nat_msg->m_external_port
+                   , node->get_nat_tcp_seq_diff_client());
+#endif
+
+            node->set_nat_ipv4_addr(nat_msg->m_external_ip);
+            node->set_nat_ipv4_port(nat_msg->m_external_port);
+
+            if ( CGlobalInfo::is_learn_verify_mode() ){
+                if (!node->is_external_is_eq_to_internal_ip() ){
+                    m_stats.m_nat_flow_learn_error++;
+                }
+            }
+        }
+
+        if (second) {
+            /* remove from the hash */
+            m_flow_id_to_node_lookup.remove_no_lookup(nat_msg->m_fid);
+            m_stats.m_nat_lookup_remove_flow_id++;
+        }
     }
 }
 
@@ -4273,7 +4362,7 @@ void CFlowGenListPerThread::start_generate_stateful(std::string erf_file_name,
         fprintf(stderr," nothing to generate no template loaded \n");
         return;
     }
-    
+
     m_preview_mode = preview;
     m_node_gen.open_file(erf_file_name,&m_preview_mode);
     dsec_t d_time_flow=get_delta_flow_is_sec();
@@ -5055,7 +5144,7 @@ void CErfIF::apply_client_config(const ClientCfg *cfg, pkt_dir_t dir) {
     /* VLAN */
     if (cfg_dir.has_vlan()) {
         add_vlan(cfg_dir.get_vlan());
-    }   
+    }
 }
 
 int CErfIF::send_node(CGenNode *node){
@@ -6422,7 +6511,7 @@ void CGenNodeBase::free_base(){
         CGenNodePCAP *p = (CGenNodePCAP *)this;
         p->destroy();
         return;
-    } 
+    }
 
     if ( m_type == COMMAND ) {
          CGenNodeCommand* p=(CGenNodeCommand*)this;
@@ -6430,4 +6519,3 @@ void CGenNodeBase::free_base(){
     }
 
 }
-
diff --git a/src/bp_sim.h b/src/bp_sim.h
index d2812688..49e0e8dc 100755
--- a/src/bp_sim.h
+++ b/src/bp_sim.h
@@ -65,8 +65,6 @@ limitations under the License.
 
 class CGenNodePCAP;
 
-#undef NAT_TRACE_
-
 #define FORCE_NO_INLINE __attribute__ ((noinline))
 #define FORCE_INLINE __attribute__((always_inline))
 
@@ -84,10 +82,6 @@ typedef struct {
  */
 #define INET_PORTSTRLEN 5
 
-
-
-
-
 /* VM commands */
 
 class CMiniVMCmdBase {
@@ -757,7 +751,10 @@ public:
     LEARN_MODE_DISABLED=0,
     LEARN_MODE_TCP_ACK=1,
     LEARN_MODE_IP_OPTION=2,
-    LEARN_MODE_MAX=LEARN_MODE_IP_OPTION
+    LEARN_MODE_TCP_ACK_NO_SERVER_SEQ_RAND=3,
+    LEARN_MODE_MAX=LEARN_MODE_TCP_ACK_NO_SERVER_SEQ_RAND,
+    // This is used to check if 1 or 3 exist
+    LEARN_MODE_TCP=100
     };
 
 public:
@@ -1246,7 +1243,11 @@ public:
     }
 
     static inline bool is_learn_mode(CParserOption::trex_learn_mode_e mode){
-        return ( (m_options.m_learn_mode == mode));
+        if (mode == CParserOption::LEARN_MODE_TCP) {
+            return ((m_options.m_learn_mode == CParserOption::LEARN_MODE_TCP_ACK_NO_SERVER_SEQ_RAND)
+                    || (m_options.m_learn_mode == CParserOption::LEARN_MODE_TCP_ACK));
+        } else
+            return (m_options.m_learn_mode == mode);
     }
 
     static inline bool is_ipv6_enable(void){
@@ -1558,11 +1559,11 @@ public:
 
     CTupleGeneratorSmart *m_tuple_gen;
     // cache line 1 - 64bytes waste of space !
-    uint32_t            m_nat_external_ipv4; /* client */
-    uint32_t            m_nat_external_ipv4_server;
-    uint16_t            m_nat_external_port;
-
-    uint16_t            m_nat_pad[3];
+    uint32_t            m_nat_external_ipv4; // NAT client IP
+    uint32_t            m_nat_tcp_seq_diff_client; // support for firewalls that do TCP seq num randomization
+    uint32_t            m_nat_tcp_seq_diff_server; // And some do seq num randomization for server->client also
+    uint16_t            m_nat_external_port; // NAT client port
+    uint16_t            m_nat_pad[1];
     const ClientCfg    *m_client_cfg;
     uint32_t            m_src_idx;
     uint32_t            m_dest_idx;
@@ -1699,6 +1700,15 @@ public:
         return (btGetMaskBit16(m_flags,4,3)==2?true:false) ;
     }
 
+    // We saw first TCP SYN. Waiting for SYN+ACK
+    inline void set_nat_wait_ack_state() {
+        btSetMaskBit16(m_flags, 4, 3, 3);
+    }
+
+    inline bool is_nat_wait_ack_state(){
+        return (btGetMaskBit16(m_flags,4,3) == 3) ? true : false;
+    }
+
     inline void set_nat_learn_state(){
         m_type=FLOW_PKT; /* normal operation .. repeat might work too */
     }
@@ -1712,14 +1722,21 @@ public:
         return (m_thread_id);
     }
 
-    inline void set_nat_ipv4_addr_server(uint32_t ip){
-        m_nat_external_ipv4_server =ip;
+    inline void set_nat_tcp_seq_diff_client(uint32_t diff) {
+        m_nat_tcp_seq_diff_client = diff;
+    }
+
+    inline uint32_t get_nat_tcp_seq_diff_client() {
+        return m_nat_tcp_seq_diff_client;
     }
 
-    inline uint32_t  get_nat_ipv4_addr_server(){
-        return ( m_nat_external_ipv4_server );
+    inline void set_nat_tcp_seq_diff_server(uint32_t diff) {
+        m_nat_tcp_seq_diff_server = diff;
     }
 
+    inline uint32_t get_nat_tcp_seq_diff_server() {
+        return m_nat_tcp_seq_diff_server;
+    }
 
     inline void set_nat_ipv4_addr(uint32_t ip){
         m_nat_external_ipv4 =ip;
@@ -1740,8 +1757,7 @@ public:
     bool is_external_is_eq_to_internal_ip(){
         /* this API is used to check TRex itself */
         if ( (get_nat_ipv4_addr() == m_src_ip ) &&
-             (get_nat_ipv4_port()==m_src_port) &&
-             ( get_nat_ipv4_addr_server() == m_dest_ip) ) {
+             (get_nat_ipv4_port()==m_src_port)) {
             return (true);
         }else{
             return (false);
@@ -3004,6 +3020,8 @@ inline void CFlowPktInfo::update_pkt_info(char *p,
     (void)et;
 
     uint16_t src_port =   node->m_src_port;
+    uint32_t tcp_seq_diff_client = 0;
+    uint32_t tcp_seq_diff_server = 0;
 
     pkt_dir_t ip_dir = node->cur_pkt_ip_addr_dir();
     pkt_dir_t port_dir = node->cur_pkt_port_addr_dir();
@@ -3024,7 +3042,6 @@ inline void CFlowPktInfo::update_pkt_info(char *p,
     }else{
 
         if ( unlikely ( CGlobalInfo::is_learn_mode()  ) ){
-
             if (m_pkt_indication.m_desc.IsLearn()) {
                 /* might be done twice */
 #ifdef NAT_TRACE_
@@ -3033,42 +3050,48 @@ inline void CFlowPktInfo::update_pkt_info(char *p,
                 ipv4->setTimeToLive(TTL_RESERVE_DUPLICATE);
 
                 /* first ipv4 option add the info in case of learn packet, usualy only the first packet */
-        if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_IP_OPTION)) {
-            CNatOption *lpNat =(CNatOption *)ipv4->getOption();
-            lpNat->set_fid(node->get_short_fid());
-            lpNat->set_thread_id(node->get_thread_id());
-        } else {
-            // This method only work on first TCP SYN
-            if (ipv4->getProtocol() == IPPROTO_TCP) {
-            TCPHeader *tcp = (TCPHeader *)(((uint8_t *)ipv4) + ipv4->getHeaderLength());
-            if (tcp->getSynFlag()) {
-                tcp->setAckNumber(CNatRxManager::calc_tcp_ack_val(node->get_short_fid(), node->get_thread_id()));
-            }
+                if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_IP_OPTION)) {
+                    CNatOption *lpNat =(CNatOption *)ipv4->getOption();
+                    lpNat->set_fid(node->get_short_fid());
+                    lpNat->set_thread_id(node->get_thread_id());
+                } else {
+                    // This method only work on first TCP SYN
+                    if (ipv4->getProtocol() == IPPROTO_TCP) {
+                        TCPHeader *tcp = (TCPHeader *)(((uint8_t *)ipv4) + ipv4->getHeaderLength());
+                        if (tcp->getSynFlag()) {
+                            tcp->setAckNumber(CNatRxManager::calc_tcp_ack_val(node->get_short_fid(), node->get_thread_id()));
+                        }
 #ifdef NAT_TRACE_
-            printf(" %.3f : flow_id: %x thread_id %x TCP ack %x\n",now_sec(), node->get_short_fid(), node->get_thread_id(), tcp->getAckNumber());
+                        printf(" %.3f : flow_id: %x thread_id %x TCP ack %x seq %x\n"
+                               ,now_sec(), node->get_short_fid(), node->get_thread_id(), tcp->getAckNumber()
+                               , tcp->getSeqNumber());
 #endif
-            }
-        }
+                    }
+                }
             }
             /* in all cases update the ip using the outside ip */
 
             if ( m_pkt_indication.m_desc.IsInitSide()  ) {
 #ifdef NAT_TRACE_
                 if (node->m_flags != CGenNode::NODE_FLAGS_LATENCY ) {
-                    printf(" %.3f : DP : i %x:%x -> %x  flow_id: %lx\n",now_sec(),node->m_src_ip,node->m_src_port,node->m_dest_ip,node->m_flow_id);
+                    printf(" %.3f : DP : i %x:%x -> %x  flow_id: %lx\n",now_sec(), node->m_src_ip
+                           , node->m_src_port, node->m_dest_ip, node->m_flow_id);
                 }
 #endif
 
+                tcp_seq_diff_server = node->get_nat_tcp_seq_diff_server();
                 ipv4->updateIpSrc(node->m_src_ip);
                 ipv4->updateIpDst(node->m_dest_ip);
-            }else{
+            } else {
 #ifdef NAT_TRACE_
                 if (node->m_flags != CGenNode::NODE_FLAGS_LATENCY ) {
-                    printf(" %.3f : r %x   -> %x:%x  flow_id: %lx \n",now_sec(),node->m_dest_ip,node->m_src_ip,node->m_src_port,node->m_flow_id);
+                    printf(" %.3f : r %x   -> %x:%x  flow_id: %lx \n", now_sec(), node->m_dest_ip
+                           , node->m_src_ip, node->m_src_port, node->m_flow_id);
                 }
 #endif
                 src_port = node->get_nat_ipv4_port();
-                ipv4->updateIpSrc(node->get_nat_ipv4_addr_server());
+                tcp_seq_diff_client = node->get_nat_tcp_seq_diff_client();
+                ipv4->updateIpSrc(node->m_dest_ip);
                 ipv4->updateIpDst(node->get_nat_ipv4_addr());
             }
 
@@ -3076,7 +3099,7 @@ inline void CFlowPktInfo::update_pkt_info(char *p,
 #ifdef NAT_TRACE_
             if (node->m_flags != CGenNode::NODE_FLAGS_LATENCY ) {
                 if ( m_pkt_indication.m_desc.IsInitSide() ==false ){
-                    printf(" %.3f : pkt ==> %x:%x %x:%x \n",now_sec(),node->get_nat_ipv4_addr(),node->get_nat_ipv4_addr_server(),
+                    printf(" %.3f : pkt ==> %x %x:%x \n",now_sec(),node->get_nat_ipv4_addr(),
                            node->get_nat_ipv4_port(),node->m_src_port);
                 }else{
                     printf(" %.3f : pkt ==> init pkt sent \n",now_sec());
@@ -3116,8 +3139,10 @@ inline void CFlowPktInfo::update_pkt_info(char *p,
         /* replace port */
         if ( port_dir ==  CLIENT_SIDE ) {
             m_tcp->setSourcePort(src_port);
+            m_tcp->setAckNumber(m_tcp->getAckNumber() + tcp_seq_diff_server);
         }else{
             m_tcp->setDestPort(src_port);
+            m_tcp->setAckNumber(m_tcp->getAckNumber() + tcp_seq_diff_client);
         }
     }else {
         if ( m_pkt_indication.m_desc.IsUdp() ){
@@ -3352,6 +3377,8 @@ public:
 
 class CCapFileFlowInfo {
 public:
+    const int LEARN_MODE_MIN_IPG = 10; // msec
+
     enum load_cap_file_err {
     kOK = 0,
     kFileNotExist,
@@ -3359,9 +3386,14 @@ public:
     kNoSyn,
     kTCPOffsetTooBig,
     kNoTCPFromServer,
+    kNoTCPSynAck,
+    kTCPLearnModeBadFlow,
     kPktNotSupp,
     kPktProcessFail,
-    kCapFileErr
+    kCapFileErr,
+    kPlugInWithLearn,
+    kIPOptionNotAllowed,
+    kTCPIpgTooLow
     };
 
     bool Create();
@@ -3378,7 +3410,7 @@ public:
     /* update flow info */
     void update_info();
 
-    bool is_valid_template_load_time(std::string & err);
+    enum CCapFileFlowInfo::load_cap_file_err is_valid_template_load_time();
 
     void save_to_erf(std::string cap_file_name,int pcap);
 
diff --git a/src/common/Network/Packet/TcpHeader.h b/src/common/Network/Packet/TcpHeader.h
index c19cd262..97575a60 100755
--- a/src/common/Network/Packet/TcpHeader.h
+++ b/src/common/Network/Packet/TcpHeader.h
@@ -23,6 +23,11 @@ class TCPHeader
 {
 
 public:
+    enum TCPHeader_enum_t
+    {
+        TCP_INVALID_PORT = 0
+    };
+
     TCPHeader(){}
 
     TCPHeader(uint16_t    argSourcePort,
diff --git a/src/latency.cpp b/src/latency.cpp
index 841913cf..b9ce2177 100644
--- a/src/latency.cpp
+++ b/src/latency.cpp
@@ -387,6 +387,12 @@ bool CCPortLatency::check_packet(rte_mbuf_t * m,CRx_check_header * & rx_p) {
 
     if ( ! is_lateancy_pkt) {
 
+#if 0
+        TCPHeader *tcp = (TCPHeader *)parser.m_l4; //????? remove
+	if (parser.m_ipv4->getProtocol() == 0x6 && tcp->getSynFlag()) {
+        tcp->dump(stdout); //???? remove
+	}
+#endif
 #ifdef NAT_TRACE_
         printf(" %.3f RX : got packet !!! \n",now_sec() );
 #endif
@@ -436,7 +442,7 @@ bool CCPortLatency::check_packet(rte_mbuf_t * m,CRx_check_header * & rx_p) {
                         m_no_ipv4_option++;
                         return (false);
                     }
-                    m_parent->get_nat_manager()->handle_packet_ipv4(lp,parser.m_ipv4);
+                    m_parent->get_nat_manager()->handle_packet_ipv4(lp, parser.m_ipv4, true);
                     opt_len -= CNatOption::noOPTION_LEN;
                     opt_ptr += CNatOption::noOPTION_LEN;
                     break;
@@ -445,10 +451,11 @@ bool CCPortLatency::check_packet(rte_mbuf_t * m,CRx_check_header * & rx_p) {
                     return (false);
             } // End of switch
         } // End of while
-	if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)
-	    && parser.IsNatInfoPkt()) {
-		m_parent->get_nat_manager()->handle_packet_ipv4(NULL, parser.m_ipv4);
-	}
+
+        bool first;
+        if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP) && parser.IsNatInfoPkt(first)) {
+            m_parent->get_nat_manager()->handle_packet_ipv4(NULL, parser.m_ipv4, first);
+        }
 
         return (true);
     } // End of check for non-latency packet
diff --git a/src/latency.h b/src/latency.h
index 724621f0..552b3999 100644
--- a/src/latency.h
+++ b/src/latency.h
@@ -107,19 +107,27 @@ public:
     }
 
     // Check if this packet contains NAT info in TCP ack
-    inline bool IsNatInfoPkt() {
-	if (!m_ipv4 || (m_protocol != IPPROTO_TCP)) {
-	    return false;
-	}
-	if (! m_l4 || (m_l4 - rte_pktmbuf_mtod(m_m, uint8_t*) + TCP_HEADER_LEN) > m_m->data_len) {
-	    return false;
-	}
-	// If we are here, relevant fields from tcp header are guaranteed to be in first mbuf 
-	TCPHeader *tcp = (TCPHeader *)m_l4;
-	if (!tcp->getSynFlag() || (tcp->getAckNumber() == 0)) {
-	    return false;
-	}
-	return true;
+    // first - set to true if this is the first packet of the flow. false otherwise.
+    //         relevant only if return value is true
+    inline bool IsNatInfoPkt(bool &first) {
+        if (!m_ipv4 || (m_protocol != IPPROTO_TCP)) {
+            return false;
+        }
+        if (! m_l4 || (m_l4 - rte_pktmbuf_mtod(m_m, uint8_t*) + TCP_HEADER_LEN) > m_m->data_len) {
+            return false;
+        }
+        // If we are here, relevant fields from tcp header are guaranteed to be in first mbuf
+        // We want to handle SYN and SYN+ACK packets
+        TCPHeader *tcp = (TCPHeader *)m_l4;
+        if (! tcp->getSynFlag())
+            return false;
+
+        if (! tcp->getAckFlag()) {
+            first = true;
+        } else {
+            first = false;
+        }
+        return true;
     }
 
 public:
diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp
index a2bfefe3..d5e7c9b5 100644
--- a/src/main_dpdk.cpp
+++ b/src/main_dpdk.cpp
@@ -687,9 +687,10 @@ static int usage(){
 
     printf(" --ipv6                     : work in ipv6 mode\n");
     printf(" --learn (deprecated). Replaced by --learn-mode. To get older behaviour, use --learn-mode 2\n");
-    printf(" --learn-mode [1-2]         : Work in NAT environments, learn the dynamic NAT translation and ALG  \n");
-    printf("      1    Use TCP ACK in first SYN to pass NAT translation information. Will work only for TCP streams. Initial SYN packet must be present in stream.\n");
+    printf(" --learn-mode [1-3]         : Work in NAT environments, learn the dynamic NAT translation and ALG  \n");
+    printf("      1    Use TCP ACK in first SYN to pass NAT translation information. Will work only for TCP streams. Initial SYN packet must be first packet in stream.\n");
     printf("      2    Add special IP option to pass NAT translation information. Will not work on certain firewalls if they drop packets with IP options\n");
+    printf("      3    Like 1, but without support for sequence number randomization in server->clien direction. Performance (flow/second) better than 1\n");
     printf(" --learn-verify             : Learn the translation, but intended for verification of the mechanism in cases that NAT does not exist \n");
     printf("  \n");
     printf(" --l-pkt-mode [0-3]         : Set mode for sending latency packets.\n");
diff --git a/src/nat_check.cpp b/src/nat_check.cpp
index 7e224430..c7262e50 100755
--- a/src/nat_check.cpp
+++ b/src/nat_check.cpp
@@ -41,7 +41,8 @@ void CGenNodeNatInfo::dump(FILE *fd){
     int i;
     for (i=0; i<m_cnt; i++) {
         CNatFlowInfo * lp=&m_data[i];
-        fprintf (fd," id:%d , external ip:%08x:%x , ex_port: %04x , fid: %d \n",i,lp->m_external_ip,lp->m_external_ip_server,lp->m_external_port,lp->m_fid);
+        fprintf (fd," id:%d , external ip:%08x , ex_port: %04x , TCP seq:%x fid: %d \n"
+                 , i, lp->m_external_ip, lp->m_external_port, lp->m_tcp_seq, lp->m_fid);
     }
 }
 
@@ -51,6 +52,28 @@ void CGenNodeNatInfo::init(){
     m_cnt=0;
 }
 
+bool CNatCheckFlowTableMap::find(uint64_t key, uint32 &val) {
+    nat_check_flow_map_t::iterator iter;
+    iter = m_map.find(key);
+    if (iter != m_map.end() ) {
+        val = (*iter).second;
+        return true;
+    }else{
+        return false;
+    }
+}
+
+void CNatCheckFlowTableMap::dump(FILE *fd) {
+    nat_check_flow_map_iter_t it;
+    uint32_t val;
+    uint64_t key;
+
+    for (it = m_map.begin(); it != m_map.end(); it++) {
+        val = it->second;
+        key = it->first;
+        fprintf(fd, "%lx->%x\n", key, val);
+    }
+}
 
 void CNatStats::reset(){
     m_total_rx=0;
@@ -147,14 +170,17 @@ void CNatRxManager::get_info_from_tcp_ack(uint32_t tcp_ack, uint32_t &fid, uint8
  *   option - pointer to our proprietary NAT info IP option.
  *      If it is NULL, the NAT info is in the TCP ACK number
  *   ipv4 - pointer to ipv4 header to extract info from.
+ *   is_first - Is this the first packet of the flow or second. To handle firewalls that do
+ *              TCP seq randomization on the server->client side, we also look at the second
+ *              packet of the flow (SYN+ACK), and extract its seq num.
  */
-void CNatRxManager::handle_packet_ipv4(CNatOption *option, IPHeader *ipv4) {
+void CNatRxManager::handle_packet_ipv4(CNatOption *option, IPHeader *ipv4, bool is_first) {
     CNatPerThreadInfo * thread_info;
     uint32_t fid=0;
+    uint32_t tcp_seq;
 
     /* Extract info from the packet ! */
     uint32_t ext_ip   = ipv4->getSourceIp();
-    uint32_t ext_ip_server = ipv4->getDestIp();
     uint8_t proto     = ipv4->getProtocol();
     /* must be TCP/UDP this is the only supported proto */
     if (!( (proto==6) || (proto==17) )){
@@ -165,21 +191,49 @@ void CNatRxManager::handle_packet_ipv4(CNatOption *option, IPHeader *ipv4) {
     TCPHeader *tcp = (TCPHeader *) (((char *)ipv4)+ ipv4->getHeaderLength());
     uint16_t ext_port = tcp->getSourcePort();
 
+    tcp_seq = tcp->getSeqNumber();
+
     if (option) {
 	thread_info = get_thread_info(option->get_thread_id());
 	fid = option->get_fid();
     } else {
-	uint8_t thread_id;
-	get_info_from_tcp_ack(tcp->getAckNumber(), fid, thread_id);
-	thread_info = get_thread_info(thread_id);
+        uint8_t thread_id;
+
+        if (is_first) {
+            uint32_t tcp_ack = tcp->getAckNumber();
+            get_info_from_tcp_ack(tcp_ack, fid, thread_id);
+            thread_info = get_thread_info(thread_id);
+            if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) {
+                uint32_t dst_ip = ipv4->getDestIp();
+                uint16_t dst_port = tcp->getDestPort();
+                uint64_t map_key = (dst_ip << 16) + dst_port;
+                m_fm.insert(map_key, tcp_ack);
+            }
+        } else {
+            uint32_t val;
+            // server->client packet. IP/port reversed in regard to first SYN packet
+            uint64_t map_key = (ext_ip << 16) + ext_port;
+
+            if (m_fm.find(map_key, val)) {
+                get_info_from_tcp_ack(val, fid, thread_id);
+                thread_info = get_thread_info(thread_id);
+                m_fm.erase(map_key);
+            } else {
+                thread_info = 0;
+                // ??? Handle error
+                // ??? handle aging of flow info
+            }
+        }
     }
 
+
     if (unlikely(!thread_info)) {
         return;
     }
 
 #ifdef NAT_TRACE_
-    printf("rx msg ext ip : %08x:%08x ext port : %04x  flow_id : %d \n",ext_ip,ext_ip_server,ext_port, fid);
+    printf("rx msg ext ip: %08x ext port: %04x TCP Seq: %08x flow_id : %d (%s) \n", ext_ip, ext_port, tcp_seq, fid
+           , is_first ? "first":"second");
 #endif
 
     CGenNodeNatInfo * node=thread_info->m_cur_nat_msg;
@@ -194,9 +248,13 @@ void CNatRxManager::handle_packet_ipv4(CNatOption *option, IPHeader *ipv4) {
     CNatFlowInfo * msg=node->get_next_msg();
 
     /* fill the message */
-    msg->m_external_ip   = ext_ip;
-    msg->m_external_ip_server = ext_ip_server;
-    msg->m_external_port = ext_port;
+    if (is_first) {
+        msg->m_external_ip   = ext_ip;
+        msg->m_external_port = ext_port;
+    } else {
+        msg->m_external_port = TCPHeader::TCP_INVALID_PORT;
+    }
+    msg->m_tcp_seq = tcp_seq;
     msg->m_fid           = fid;
     msg->m_pad           = 0xee; 
 
diff --git a/src/nat_check.h b/src/nat_check.h
index 133501ae..3b526c0b 100755
--- a/src/nat_check.h
+++ b/src/nat_check.h
@@ -21,6 +21,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
+#include <map>
 #include "msg_manager.h"
 #include <common/Network/Packet/TcpHeader.h>
 #include <common/Network/Packet/UdpHeader.h>
@@ -121,7 +122,7 @@ private:
 
 struct CNatFlowInfo {
     uint32_t m_external_ip;
-    uint32_t m_external_ip_server;
+    uint32_t m_tcp_seq;
     uint32_t m_fid;
     uint16_t m_external_port;
     uint16_t m_pad;
@@ -210,13 +211,28 @@ public:
     void Dump(FILE *fd);
 };
 
+typedef std::map<uint64_t, uint32_t, std::less<uint64_t> > nat_check_flow_map_t;
+typedef nat_check_flow_map_t::iterator nat_check_flow_map_iter_t;
+
+class CNatCheckFlowTableMap   {
+public:
+    void erase(uint64_t key) {m_map.erase(key);}
+    bool find(uint64_t fid, uint32_t &val);
+    void insert(uint64_t key, uint32_t val) {m_map.insert(std::pair<uint64_t, uint32_t>(key, val));}
+    void clear(void) {m_map.clear();}
+    void dump(FILE *fd);
+    uint64_t size(void) {return m_map.size();}
+
+public:
+    nat_check_flow_map_t m_map;
+};
 
 class CNatRxManager {
 
 public:
     bool Create();
     void Delete();
-    void handle_packet_ipv4(CNatOption * option, IPHeader * ipv4);
+    void handle_packet_ipv4(CNatOption * option, IPHeader * ipv4, bool is_first);
     void handle_aging();
     void Dump(FILE *fd);
     void DumpShort(FILE *fd);
@@ -232,6 +248,7 @@ private:
     uint8_t               m_max_threads;
     CNatPerThreadInfo   * m_per_thread;
     CNatStats             m_stats;
+    CNatCheckFlowTableMap  m_fm;
 };
 
 
-- 
cgit