diff --git a/README.txt b/README.txt
index a6a7322..a3e2fbb 100644
--- a/README.txt
+++ b/README.txt
@@ -58,4 +58,39 @@ Thanks,
   ojacques@users.sourceforge.net
   richard_gayraud@users.sourceforge.net
 
+ADDITIONS
+*********
+
+This section describes some additions to IMS Bench that are not reflected
+in the online documentation (yet).
+
+1.  Changes to code to support gcc-4.4 C++ stdlib changes, which changes
+    the prototypes for many of the str*() family of functions.
+
+2.  UDP KeepAlive support for NAT pinhole tunneling. SIP users may
+    be configured to periodically send small 4-byte UDP packets to their
+    remote (typically a proxy). This serves to keep any NAT pinholes
+    open, so that the SIP server can contact the client later thru the
+    NAT box. To enable:
+
+    A. 	Use command line argument to SIPp "-keep_alive_period <secs>" where
+	<secs> is the period between keep alive messages. Typically ~20sec.
+    B.  Add scenario action int_cmd="keep_alive_on". Typically this would
+   	occur at the end of the registration scenario.
+    C.  Add scdenario action int_cmd="keep_alive_off", typically at end
+  	of de-registration scenario (At point where it is ok for pinhole
+	to close.
+    Keep alives are implemented on a per-user basis: the int_cmd changes
+    the state for each user.  (e.g., the keep alive state is remembered
+    into the next call for that user. The keep_alive_period defaults to
+    zero, which disable sending and keep alives.
+
+    As currently implemented, enabling keep alives, esp. for many users,
+    may have a negative effect on latency measurements. Sending keep alives
+    requires walking thru the entire user list. This occurs every 2 secs.
+    Note that this overhead is entirely avoided when the keep alive period
+    is set to zero. Thus there is no performance impact when this feature
+    is not enabled.
+    
+
 ******************************************************************
diff --git a/actions.cpp b/actions.cpp
index dc97b09..e19aab0 100644
--- a/actions.cpp
+++ b/actions.cpp
@@ -49,6 +49,10 @@ static const char* strIntCmd(CIntCmdAction::T_IntCmdType type)
     return "set_start";
   case CIntCmdAction::E_INTCMD_SET_TARGET_IP:
     return "set_target_ip";
+  case CIntCmdAction::E_INTCMD_KEEP_ALIVE_OFF:
+    return "keep_alive_off";
+  case CIntCmdAction::E_INTCMD_KEEP_ALIVE_ON:
+    return "keep_alive_on";
   default:
   case CIntCmdAction::E_INTCMD_INVALID:
     return "invalid";
@@ -110,6 +114,10 @@ CIntCmdAction::CIntCmdAction(const char * cmd) :
     M_IntCmd = CIntCmdAction::E_INTCMD_SET_START;
   } else if (!strcmp(cmd, "set_target_ip")) {
     M_IntCmd = CIntCmdAction::E_INTCMD_SET_TARGET_IP;
+  } else if (!strcmp(cmd, "keep_alive_off")) {
+    M_IntCmd = CIntCmdAction::E_INTCMD_KEEP_ALIVE_OFF;
+  } else if (!strcmp(cmd, "keep_alive_on")) {
+    M_IntCmd = CIntCmdAction::E_INTCMD_KEEP_ALIVE_ON;
   }
   //DBGINFO_ACT((" CIntCmdAction(cmd=%d)", M_IntCmd));
 }
diff --git a/actions.hpp b/actions.hpp
index 1dc7a5f..0d3e6f9 100644
--- a/actions.hpp
+++ b/actions.hpp
@@ -173,7 +173,9 @@ public:
       E_INTCMD_STOP_ALL,
       E_INTCMD_STOP_NOW,
       E_INTCMD_SET_START,
-      E_INTCMD_SET_TARGET_IP
+      E_INTCMD_SET_TARGET_IP,
+      E_INTCMD_KEEP_ALIVE_OFF,
+      E_INTCMD_KEEP_ALIVE_ON
     };
 
 public:
diff --git a/call.cpp b/call.cpp
index 0f72146..36d8671 100644
--- a/call.cpp
+++ b/call.cpp
@@ -3726,6 +3726,15 @@ call::T_ActionResult call::executeAction(char * msg, int scenarioIndex, RmtParmL
                 DBGINFO_ACT(("IntCmdAction SET_START"));
                 start_time = GetMicroTime();  // Consider the call really starts now
                 break;
+              case CIntCmdAction::E_INTCMD_KEEP_ALIVE_OFF:
+                if (m_User)
+                    m_User->setKeepAlive(0);
+		break;
+              case CIntCmdAction::E_INTCMD_KEEP_ALIVE_ON:
+                if (m_User)
+                    m_User->setKeepAlive(1);
+		break;
+		
               case CIntCmdAction::E_INTCMD_SET_TARGET_IP:
                 DBGINFO_ACT(("IntCmdAction SET_TARGET_IP"));
                 {
diff --git a/sipp.cpp b/sipp.cpp
index f18768d..857c116 100644
--- a/sipp.cpp
+++ b/sipp.cpp
@@ -301,6 +301,7 @@ struct sipp_option options_table[] = {
         {"ip_field", "Set which field from the injection file contains the IP address from which the client will send its messages.\n"
                      "If this option is omitted and the '-t ui' option is present, then field 0 is assumed.\n"
                      "Use this option together with '-t ui'", SIPP_OPTION_INT, &peripfield},
+	{"keep_alive_period", "Period (secs) at which to send UDP keep alive messages in order to keep NAT holes open. Use with action int_cmd=keep_alive_on.", SIPP_OPTION_INT, &keep_alive_period},
         {"l", "Set the maximum number of simultaneous calls. Once this limit is reached, traffic is decreased until the number of open calls goes down. Default:\n"
               "  (3 * call_duration (s) * rate).", SIPP_OPTION_LIMIT, NULL},
         {"lost", "Set the number of packets to lose by default (scenario specifications override this value).", SIPP_OPTION_FLOAT, &global_lost},
@@ -2563,6 +2564,14 @@ Temp One Shot for now....
         }
       }
     }
+
+    // Every second, see if any keep alives ready to go
+    // keep_alive_period is secs, while ticks are milli, thus scale
+    if (keep_alive_period >0 && (clock_tick-last_keep_alive_time)>1000 ) { 
+	try_users_keep_alive(clock_tick, keep_alive_period*1000,
+		&remote_sockaddr);
+	last_keep_alive_time = clock_tick;
+    }
   }
   }
   catch (SIPpException& e) {
diff --git a/sipp.hpp b/sipp.hpp
index 64b4007..ae94758 100644
--- a/sipp.hpp
+++ b/sipp.hpp
@@ -337,6 +337,8 @@ extern unsigned long last_dump_time               _DEFVAL(1);
 extern unsigned long clock_tick                   _DEFVAL(1);
 extern unsigned long scheduling_loops             _DEFVAL(0);
 extern unsigned long last_timer_cycle             _DEFVAL(0);
+extern unsigned long last_keep_alive_time	  _DEFVAL(0);
+extern int	     keep_alive_period		  _DEFVAL(0); 
 
 #ifndef GET_TIME
 #define GET_TIME(clock)       \
diff --git a/user.cpp b/user.cpp
index 429a3b5..1e6cf5a 100644
--- a/user.cpp
+++ b/user.cpp
@@ -29,12 +29,20 @@
 #endif
 
 
+#if 0
+class CUserPool {
+  public:
+    user_list	m_users;
+};
+static CUserPool S_user_pool[MAX_USER_POOL];
+#else
 static user_list S_user_pool[MAX_USER_POOL];
+#endif
 
 
 CUser::CUser(const char* a_Fields, int a_PoolId) :
   m_PoolId(-1), m_PoolIndex(0),
-  m_pSocket(NULL)
+  m_pSocket(NULL), m_DoKeepAlive(0), m_LastKeepAliveTime(0)
 {
   // Read user constant fields
   std::string line(a_Fields);
@@ -393,6 +401,29 @@ void init_user_variables()
   }
 }
 
+void try_users_keep_alive(unsigned long curTime, unsigned short period, 
+	SockAddrStorage *addrData)
+{
+  unsigned long trigTime = curTime - period;
+  const char *msgData = "\r\n\r\n";
+  int msgLen = strlen(msgData);
+  bool unused = 0;
+  socklen_t addrLen = SOCK_ADDR_SIZE(addrData);
+
+  for (int p=0; p<MAX_USER_POOL; p++) {
+    user_list& rPool = S_user_pool[p];
+    for (user_list::iterator it = rPool.begin(); it != rPool.end(); ++ it) {
+      CUser *u = *it;
+      CSocket *tsock;
+      if (u->getKeepAlive() && u->getLastKeepAliveTime() < trigTime
+         && (tsock=u->getSocket())!=NULL ) {
+	    u->setLastKeepAliveTime(curTime);
+	    tsock->SendTo( (unsigned char*)msgData, msgLen, addrData, addrLen, unused);
+      }
+    }
+  }
+}
+
 #ifdef MULTI_IP_SUPPORT
 void load_users_ip(const char* fname, const char *ip_file)
 {
diff --git a/user.hpp b/user.hpp
index acaa67c..914d720 100644
--- a/user.hpp
+++ b/user.hpp
@@ -63,6 +63,13 @@ public:
   CSocket* getSocket() {return m_pSocket; };
   int getPort(bool bClient);
   char *getFullIp();
+  
+  bool getKeepAlive() { return m_DoKeepAlive; }
+  void setKeepAlive(bool doIt) { m_DoKeepAlive = doIt; }
+
+  unsigned long getLastKeepAliveTime() { return m_LastKeepAliveTime; } 
+  void setLastKeepAliveTime(unsigned long lastTime) {
+	m_LastKeepAliveTime = lastTime; }
 
 private:
   unsigned int m_PoolIndex;
@@ -71,6 +78,8 @@ private:
   CCallVariable*  m_VariableTable[SCEN_VARIABLE_SIZE];
   vector<string*> m_FieldVect;
   CSocket*        m_pSocket;
+  bool			m_DoKeepAlive;
+  unsigned long		m_LastKeepAliveTime;
 };
 
 
@@ -86,5 +95,8 @@ unsigned int get_total_users();
 void init_user_variables();
 void seed_user_rand(unsigned long a_Seed);
 
+void try_users_keep_alive(unsigned long curTime, unsigned short period,
+	SockAddrStorage *addrData);
+
 #endif  /* USER */
 
