gtphub: fix handling of sender from nonstandard port.

Allow a peer sending from an unknown port but a known address, and just
create the port (and unmap the seq nr back to this port later to return
the response to the sender).

Only an SGSN on the Ctrl plane is allowed to make the very first contact
from an unknown address.

Sponsored-by: On-Waves ehi
diff --git a/openbsc/src/gprs/gtphub.c b/openbsc/src/gprs/gtphub.c
index 9a29cc0..d6dbb4d 100644
--- a/openbsc/src/gprs/gtphub.c
+++ b/openbsc/src/gprs/gtphub.c
@@ -1365,6 +1365,9 @@
 	return sizeof(echo_response_data);
 }
 
+struct gtphub_peer_port *gtphub_known_addr_have_port(const struct gtphub_bind *bind,
+						     const struct osmo_sockaddr *addr);
+
 /* Parse buffer as GTP packet, replace elements in-place and return the ofd and
  * address to forward to. Return a pointer to the osmo_fd, but copy the
  * sockaddr to *to_addr. The reason for this is that the sockaddr may expire at
@@ -1419,7 +1422,10 @@
 	}
 
 	if (!ggsn) {
-		ggsn = gtphub_port_find_sa(&hub->to_ggsns[plane_idx], from_addr);
+		/* Find a GGSN peer with a matching address. The sender's port
+		 * may in fact differ. */
+		ggsn = gtphub_known_addr_have_port(&hub->to_ggsns[plane_idx],
+						   from_addr);
 	}
 
 	/* If any PDP context has been created, we already have an entry for
@@ -1560,8 +1566,9 @@
 
 	if (!sgsn) {
 		/* If any contact has been made before, we already have an
-		 * entry for this SGSN. */
-		sgsn = gtphub_port_find_sa(&hub->to_sgsns[plane_idx], from_addr);
+		 * entry for this SGSN. The port may differ. */
+		sgsn = gtphub_known_addr_have_port(&hub->to_sgsns[plane_idx],
+						   from_addr);
 	}
 
 	if (!sgsn) {
@@ -2041,6 +2048,31 @@
 	return gtphub_addr_add_port(a, port);
 }
 
+/* Find a GGSN peer with a matching address. If the address is known but the
+ * port not, create a new port for that peer address. */
+struct gtphub_peer_port *gtphub_known_addr_have_port(const struct gtphub_bind *bind,
+						     const struct osmo_sockaddr *addr)
+{
+	struct gtphub_peer_addr *pa;
+	struct gtphub_peer_port *pp;
+
+	struct gsn_addr gsna;
+	uint16_t port;
+	gsn_addr_from_sockaddr(&gsna, &port, addr);
+
+	pa = gtphub_addr_find(bind, &gsna);
+	if (!pa)
+		return NULL;
+
+	pp = gtphub_addr_find_port(pa, port);
+
+	if (!pp)
+		pp = gtphub_addr_add_port(pa, port);
+
+	return pp;
+}
+
+
 /* Return 0 if the message in p is not applicable for GGSN resolution, -1 if
  * resolution should be possible but failed, and 1 if resolution was
  * successful. *pp will be set to NULL if <1 is returned. */