linkset: Make it possible to create multiple links for a linkset

We now create multiple links and will start all of them. This
still requires good balancing between these links.
diff --git a/include/bsc_data.h b/include/bsc_data.h
index ad45982..9e4be92 100644
--- a/include/bsc_data.h
+++ b/include/bsc_data.h
@@ -103,7 +103,6 @@
 	int pcap_fd;
 	int udp_reset_timeout;
 	struct llist_head links;
-	struct link_data first_link;
 
 	const char *token;
 
@@ -180,4 +179,7 @@
 /* another callback for SCCP data from the linkset */
 void linkset_forward_sccp(struct bsc_data *bsc, struct msgb *msg, int sls);
 
+/* find the link */
+struct link_data *linkset_find_link(struct bsc_data *bsc, int link_index);
+
 #endif
diff --git a/src/link_udp.c b/src/link_udp.c
index d33f85e..ea23e24 100644
--- a/src/link_udp.c
+++ b/src/link_udp.c
@@ -38,17 +38,6 @@
 
 #define OSMO_CB_LI(msg) msg->cb[0]
 
-static struct link_data *find_link(struct bsc_data *bsc, int link_index)
-{
-	struct link_data *link;
-
-	llist_for_each_entry(link, &bsc->links, entry)
-		if (link->link_index == link_index)
-			return link;
-
-	return NULL;
-}
-
 static int udp_write_cb(struct bsc_fd *fd, struct msgb *msg)
 {
 	struct bsc_data *bsc;
@@ -57,7 +46,7 @@
 
 	bsc = (struct bsc_data *) fd->data;
 
-	link = find_link(bsc, OSMO_CB_LI(msg));
+	link = linkset_find_link(bsc, OSMO_CB_LI(msg));
 	if (!link) {
 		LOGP(DINP, LOGL_ERROR, "No link_data for %lu\n", OSMO_CB_LI(msg));
 		return -1;
@@ -104,7 +93,7 @@
 
 	hdr = (struct udp_data_hdr *) msgb_put(msg, sizeof(*hdr));
 
-	link = find_link(bsc, ntohs(hdr->data_link_index));
+	link = linkset_find_link(bsc, ntohs(hdr->data_link_index));
 	if (!link) {
 		LOGP(DINP, LOGL_ERROR, "Failed to find a link.\n");
 		rc = -1;
diff --git a/src/links.c b/src/links.c
index d8fa9a6..24a1738 100644
--- a/src/links.c
+++ b/src/links.c
@@ -24,6 +24,17 @@
 #include <cellmgr_debug.h>
 #include <snmp_mtp.h>
 
+struct link_data *linkset_find_link(struct bsc_data *bsc, int link_index)
+{
+	struct link_data *link;
+
+	llist_for_each_entry(link, &bsc->links, entry)
+		if (link->link_index == link_index)
+			return link;
+
+	return NULL;
+}
+
 void link_stop_all(struct bsc_data *bsc)
 {
 	struct link_data *link;
@@ -87,49 +98,70 @@
 
 int link_setup_start(struct bsc_data *bsc)
 {
-	bsc->first_link.the_link = mtp_link_alloc();
-	bsc->first_link.the_link->data = &bsc->first_link;
-	bsc->first_link.the_link->dpc = bsc->dpc;
-	bsc->first_link.the_link->opc = bsc->opc;
-	bsc->first_link.the_link->sccp_opc = bsc->sccp_opc > -1 ? bsc->sccp_opc : bsc->opc;
-	bsc->first_link.the_link->link = 0;
-	bsc->first_link.the_link->sltm_once = bsc->once;
-	bsc->first_link.the_link->ni = bsc->ni_ni;
-	bsc->first_link.the_link->spare = bsc->ni_spare;
-	bsc->first_link.bsc = bsc;
-	bsc->first_link.pcap_fd = bsc->pcap_fd;
-	bsc->first_link.udp.reset_timeout = bsc->udp_reset_timeout;
-	bsc->first_link.link_index = 1;
+	struct link_data *link;
 
-	llist_add(&bsc->first_link.entry, &bsc->links);
-
-	if (!bsc->first_link.udp.udp_ip) {
-		LOGP(DINP, LOGL_ERROR, "Need to set a UDP IP.\n");
+	if (llist_empty(&bsc->links)) {
+		LOGP(DINP, LOGL_ERROR, "No links defined.\n");
 		return -1;
 	}
 
-	LOGP(DINP, LOGL_NOTICE, "Using UDP MTP mode.\n");
-
-	/* setup SNMP first, it is blocking */
-	bsc->first_link.udp.session = snmp_mtp_session_create(bsc->first_link.udp.udp_ip);
-	if (!bsc->first_link.udp.session)
-		return -1;
-
 	if (link_udp_network_init(bsc) != 0)
 		return -1;
 
-	/* now connect to the transport */
-	if (link_udp_init(&bsc->first_link, bsc->first_link.udp.udp_ip, bsc->first_link.udp.udp_port) != 0)
-		return -1;
+	llist_for_each_entry(link, &bsc->links, entry) {
+		link->the_link = mtp_link_alloc();
+		link->the_link->data = link;
+		link->the_link->dpc = bsc->dpc;
+		link->the_link->opc = bsc->opc;
+		link->the_link->sccp_opc = bsc->sccp_opc > -1 ? bsc->sccp_opc : bsc->opc;
+		link->the_link->link = 0;
+		link->the_link->sltm_once = bsc->once;
+		link->the_link->ni = bsc->ni_ni;
+		link->the_link->spare = bsc->ni_spare;
+		link->bsc = bsc;
+		link->pcap_fd = bsc->pcap_fd;
+		link->udp.reset_timeout = bsc->udp_reset_timeout;
 
-	/*
-	 * We will ask the MTP link to be taken down for two
-	 * timeouts of the BSC to make sure we are missing the
-	 * SLTM and it begins a reset. Then we will take it up
-	 * again and do the usual business.
-	 */
-	snmp_mtp_deactivate(bsc->first_link.udp.session,
-			    bsc->first_link.link_index);
+		if (!link->udp.udp_ip) {
+			LOGP(DINP, LOGL_ERROR, "Need to set a UDP IP on %d.\n",
+			     link->link_index);
+			return -1;
+		}
+
+		if (!link->udp.udp_port) {
+			LOGP(DINP, LOGL_ERROR, "Need to set a UDP Port on %d.\n",
+			     link->link_index);
+			return -1;
+		}
+	}
+
+	LOGP(DINP, LOGL_NOTICE, "Using UDP MTP mode.\n");
+
+	llist_for_each_entry(link, &bsc->links, entry) {
+		/* setup SNMP first, it is blocking */
+		link->udp.session = snmp_mtp_session_create(link->udp.udp_ip);
+		if (!link->udp.session) {
+			LOGP(DINP, LOGL_ERROR,
+			     "Failed to initialize SNMP on %d\n", link->link_index);
+			return -1;
+		}
+
+		/* now connect to the transport */
+		if (link_udp_init(link, link->udp.udp_ip, link->udp.udp_port) != 0) {
+			LOGP(DINP, LOGL_ERROR,
+			     "Failed to init the link on %d\n", link->link_index);
+			return -1;
+		}
+
+		/*
+		 * We will ask the MTP link to be taken down for two
+		 * timeouts of the BSC to make sure we are missing the
+		 * SLTM and it begins a reset. Then we will take it up
+		 * again and do the usual business.
+		 */
+		snmp_mtp_deactivate(link->udp.session, link->link_index);
+	}
+
 	bsc->start_timer.cb = start_rest;
 	bsc->start_timer.data = bsc;
 	bsc_schedule_timer(&bsc->start_timer, bsc->udp_reset_timeout, 0);
diff --git a/src/main.c b/src/main.c
index 95736c8..8b9ae1a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -636,8 +636,6 @@
 	bsc.dpc = 1;
 	bsc.opc = 0;
 	bsc.sccp_opc = -1;
-	bsc.first_link.udp.udp_port = 3456;
-	bsc.first_link.udp.udp_ip = NULL;
 	bsc.src_port = 1313;
 	bsc.ni_ni = MTP_NI_NATION_NET;
 	bsc.ni_spare = 0;
diff --git a/src/main_udt.c b/src/main_udt.c
index 3327178..f4be646 100644
--- a/src/main_udt.c
+++ b/src/main_udt.c
@@ -187,8 +187,6 @@
 	bsc.dpc = 1;
 	bsc.opc = 0;
 	bsc.sccp_opc = -1;
-	bsc.first_link.udp.udp_port = 3456;
-	bsc.first_link.udp.udp_ip = NULL;
 	bsc.src_port = 1313;
 	bsc.ni_ni = MTP_NI_NATION_NET;
 	bsc.ni_spare = 0;
diff --git a/src/vty_interface.c b/src/vty_interface.c
index ac982ea..33fae34 100644
--- a/src/vty_interface.c
+++ b/src/vty_interface.c
@@ -21,6 +21,7 @@
  */
 
 #include <bsc_data.h>
+#include <cellmgr_debug.h>
 
 #include <osmocore/talloc.h>
 #include <osmocore/gsm48.h>
@@ -59,6 +60,8 @@
 
 static int config_write_cell(struct vty *vty)
 {
+	struct link_data *link;
+
 	vty_out(vty, "cellmgr%s", VTY_NEWLINE);
 	vty_out(vty, " mtp dpc %d%s", bsc.dpc, VTY_NEWLINE);
 	vty_out(vty, " mtp opc %d%s", bsc.opc, VTY_NEWLINE);
@@ -69,9 +72,14 @@
 	vty_out(vty, " country-code %d%s", bsc.mcc, VTY_NEWLINE);
 	vty_out(vty, " network-code %d%s", bsc.mnc, VTY_NEWLINE);
 	vty_out(vty, " location-area-code %d%s", bsc.lac, VTY_NEWLINE);
-	if (bsc.first_link.udp.udp_ip)
-		vty_out(vty, " udp dest ip %s%s", bsc.first_link.udp.udp_ip, VTY_NEWLINE);
-	vty_out(vty, " udp dest port %d%s", bsc.first_link.udp.udp_port, VTY_NEWLINE);
+
+	llist_for_each_entry(link, &bsc.links, entry) {
+		vty_out(vty, " udp dest-ip %d %s%s",
+			link->link_index, link->udp.udp_ip, VTY_NEWLINE);
+		vty_out(vty, " udp dest-port %d %d%s",
+			link->link_index, link->udp.udp_port, VTY_NEWLINE);
+	}
+
 	vty_out(vty, " udp src port %d%s", bsc.src_port, VTY_NEWLINE);
 	vty_out(vty, " udp reset %d%s", bsc.udp_reset_timeout, VTY_NEWLINE);
 	vty_out(vty, " msc ip %s%s", bsc.msc_address, VTY_NEWLINE);
@@ -128,30 +136,98 @@
 	return CMD_SUCCESS;
 }
 
+static struct link_data *find_or_create(struct bsc_data *bsc, int index)
+{
+	struct link_data *link = linkset_find_link(bsc, index);
+	if (link)
+		return link;
 
-DEFUN(cfg_udp_dst_ip, cfg_udp_dst_ip_cmd,
-      "udp dest ip IP",
-      "Set the IP when UDP mode is supposed to be used.")
+	link = talloc_zero(NULL, struct link_data);
+	if (!link) {
+		LOGP(DINP, LOGL_ERROR, "Failed to allocate link.\n");
+		return NULL;
+	}
+
+	link->link_index = index;
+	llist_add(&link->entry, &bsc->links);
+	return link;
+}
+
+static int set_dest_ip(struct vty *vty, struct link_data *link, const char *name)
 {
 	struct hostent *hosts;
 	struct in_addr *addr;
 
-	hosts = gethostbyname(argv[0]);
+	hosts = gethostbyname(name);
 	if (!hosts || hosts->h_length < 1 || hosts->h_addrtype != AF_INET) {
-		vty_out(vty, "Failed to resolve '%s'%s", argv[0], VTY_NEWLINE);
+		vty_out(vty, "Failed to resolve '%s'%s", name, VTY_NEWLINE);
 		return CMD_WARNING;
 	}
 
 	addr = (struct in_addr *) hosts->h_addr_list[0];
-	bsc.first_link.udp.udp_ip = talloc_strdup(NULL, inet_ntoa(*addr));
+	link->udp.udp_ip = talloc_strdup(NULL, inet_ntoa(*addr));
 	return CMD_SUCCESS;
 }
 
-DEFUN(cfg_udp_dst_port, cfg_udp_dst_port_cmd,
+DEFUN_DEPRECATED(cfg_udp_dst_ip, cfg_udp_dst_ip_cmd,
+      "udp dest ip IP",
+      "Set the IP when UDP mode is supposed to be used.")
+{
+	struct link_data *link;
+
+	link = find_or_create(&bsc, 1);
+	if (!link) {
+		vty_out(vty, "Failed to create a link.\n");
+		return CMD_WARNING;
+	}
+
+	return set_dest_ip(vty, link, argv[0]);
+}
+
+DEFUN_DEPRECATED(cfg_udp_dst_port, cfg_udp_dst_port_cmd,
       "udp dest port PORT_NR",
       "If UDP mode is used specify the UDP dest port")
 {
-	bsc.first_link.udp.udp_port = atoi(argv[0]);
+	struct link_data *link;
+
+	link = find_or_create(&bsc, 1);
+	if (!link) {
+		vty_out(vty, "Failed to create a link.\n");
+		return CMD_WARNING;
+	}
+
+	link->udp.udp_port = atoi(argv[0]);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_link_dest_ip, cfg_link_dest_ip_cmd,
+      "udp dest-ip <0-64> IP",
+      "Set the dest-ip for a link.\n" "The link index\n" "IP or hostname\n")
+{
+	struct link_data *link;
+
+	link = find_or_create(&bsc, atoi(argv[0]));
+	if (!link) {
+		vty_out(vty, "Failed to create a link.\n");
+		return CMD_WARNING;
+	}
+
+	return set_dest_ip(vty, link, argv[1]);
+}
+
+DEFUN(cfg_link_port_ip, cfg_link_port_ip_cmd,
+      "udp dest-port <0-64> PORT_NR",
+      "Set the dest-port for a link.\n" "The link index\n" "port number\n")
+{
+	struct link_data *link;
+
+	link = find_or_create(&bsc, atoi(argv[0]));
+	if (!link) {
+		vty_out(vty, "Failed to create a link.\n");
+		return CMD_WARNING;
+	}
+
+	link->udp.udp_port = atoi(argv[1]);
 	return CMD_SUCCESS;
 }
 
@@ -305,6 +381,9 @@
 	install_element(CELLMGR_NODE, &cfg_mcc_cmd);
 	install_element(CELLMGR_NODE, &cfg_mnc_cmd);
 	install_element(CELLMGR_NODE, &cfg_lac_cmd);
+
+	install_element(CELLMGR_NODE, &cfg_link_dest_ip_cmd);
+	install_element(CELLMGR_NODE, &cfg_link_port_ip_cmd);
 }
 
 const char *openbsc_copyright = "";