gtphub: implement sgsn_use_sender for NAT.

If an SGSN is behind NAT, we cannot rely on the default ports. Specifically,
if a GGSN sends a message, the forwarding to the SGSN should go to whichever
port the SGSN last sent from (whether sequence nr is known or not).

Add sgsn_use_sender config and VTY command, and store the sender instead
of the GSN Address IE and default port if set.

Sponsored-by: On-Waves ehi
diff --git a/openbsc/src/gprs/gtphub.c b/openbsc/src/gprs/gtphub.c
index e96088c..6762825 100644
--- a/openbsc/src/gprs/gtphub.c
+++ b/openbsc/src/gprs/gtphub.c
@@ -1502,9 +1502,27 @@
 			tei_from_ie = ntoh32(p->ie[ie_idx]->tv4.v);
 
 		/* Make sure an entry for this peer address with default port
-		 * exists. */
+		 * exists.
+		 *
+		 * Exception: if sgsn_use_sender is set, instead use the
+		 * sender's address and port for Ctrl -- the User port is not
+		 * known until the first User packet arrives.
+		 *
+		 * Note: doing this here is just an optimization, because
+		 * gtphub_handle_buf() has code to replace the tunnel
+		 * endpoints' addresses with the sender (needed for User
+		 * plane). We could just ignore sgsn_use_sender here. But if we
+		 * set up a default port here and replace it in
+		 * gtphub_handle_buf(), we'd be creating a peer port just to
+		 * expire it right away. */
+		if (hub->sgsn_use_sender && (side_idx == GTPH_SIDE_SGSN)) {
+			gsn_addr_from_sockaddr(&use_addr, &use_port, &from_ctrl->sa);
+		} else {
+			use_port = gtphub_plane_idx_default_port[plane_idx];
+
+		}
+
 		struct gtphub_peer_port *peer_from_ie;
-		use_port = gtphub_plane_idx_default_port[plane_idx];
 		peer_from_ie = gtphub_port_have(hub,
 						&hub->to_gsns[side_idx][plane_idx],
 						&use_addr, use_port);
@@ -2417,6 +2435,7 @@
 	gtphub_init(hub);
 
 	hub->restart_counter = restart_counter;
+	hub->sgsn_use_sender = cfg->sgsn_use_sender? 1 : 0;
 
 	/* If a Ctrl plane proxy is configured, ares will never be used. */
 	if (!cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].addr_str) {
diff --git a/openbsc/src/gprs/gtphub_vty.c b/openbsc/src/gprs/gtphub_vty.c
index 204d1de..324d155 100644
--- a/openbsc/src/gprs/gtphub_vty.c
+++ b/openbsc/src/gprs/gtphub_vty.c
@@ -87,6 +87,10 @@
 		    &g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].bind,
 		    &g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_USER].bind);
 
+	if (g_cfg->sgsn_use_sender) {
+		vty_out(vty, "sgsn-use-sender%s", VTY_NEWLINE);
+	}
+
 	if (g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].addr_str) {
 		write_addrs(vty, "sgsn-proxy",
 			    &g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL],
@@ -237,6 +241,28 @@
 }
 
 
+#define SGSN_USE_SENDER_STR \
+	"Ignore SGSN's Address IEs, use sender address and port (useful over NAT)\n"
+
+DEFUN(cfg_gtphub_sgsn_use_sender,
+      cfg_gtphub_sgsn_use_sender_cmd,
+      "sgsn-use-sender",
+      SGSN_USE_SENDER_STR)
+{
+	g_cfg->sgsn_use_sender = 1;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_gtphub_no_sgsn_use_sender,
+      cfg_gtphub_no_sgsn_use_sender_cmd,
+      "no sgsn-use-sender",
+      NO_STR SGSN_USE_SENDER_STR)
+{
+	g_cfg->sgsn_use_sender = 0;
+	return CMD_SUCCESS;
+}
+
+
 /* Copied from sgsn_vty.h */
 DEFUN(cfg_grx_ggsn, cfg_grx_ggsn_cmd,
 	"grx-dns-add A.B.C.D",
@@ -425,6 +451,8 @@
 	install_element(GTPHUB_NODE, &cfg_gtphub_ggsn_proxy_cmd);
 	install_element(GTPHUB_NODE, &cfg_gtphub_sgsn_proxy_short_cmd);
 	install_element(GTPHUB_NODE, &cfg_gtphub_sgsn_proxy_cmd);
+	install_element(GTPHUB_NODE, &cfg_gtphub_sgsn_use_sender_cmd);
+	install_element(GTPHUB_NODE, &cfg_gtphub_no_sgsn_use_sender_cmd);
 	install_element(GTPHUB_NODE, &cfg_grx_ggsn_cmd);
 
 	return 0;