Index: extensions/CMakeLists.txt
===================================================================
--- extensions/CMakeLists.txt	(revision 1020)
+++ extensions/CMakeLists.txt	(revision 1040)
@@ -72,4 +72,6 @@
 FD_EXTENSION_SUBDIR(rt_busypeers "Handling of Diameter TOO_BUSY messages and relay timeouts"	ON)
 FD_EXTENSION_SUBDIR(rt_ereg      "Configurable routing based on regexp matching of AVP values" OFF)
+FD_EXTENSION_SUBDIR(rt_ignore_dh "Stow Destination-Host in Proxy-Info, restore to Origin-Host for answers"	ON)
+FD_EXTENSION_SUBDIR(rt_load_balance "Balance load over multiple equal hosts, based on outstanding requests"	ON)
 
 
Index: extensions/rt_busypeers/CMakeLists.txt
===================================================================
--- extensions/rt_busypeers/CMakeLists.txt	(revision 1020)
+++ extensions/rt_busypeers/CMakeLists.txt	(revision 1040)
@@ -1,3 +1,3 @@
-# The rt_ereg extension
+# The rt_busypeer extension
 PROJECT("Handling of TOO_BUSY messages and relay timeout capability routing extension" C)
 
Index: extensions/rt_ignore_dh/CMakeLists.txt
===================================================================
--- extensions/rt_ignore_dh/CMakeLists.txt	(revision 1041)
+++ extensions/rt_ignore_dh/CMakeLists.txt	(revision 1041)
@@ -0,0 +1,20 @@
+# The rt_ignore_dh extension
+PROJECT("Routing extension that removes Destination-Host from messages and restores from Proxy-Info for answers" C)
+
+# List of source files
+SET(RT_IGNORE_DH_SRC
+	rt_ignore_dh.c
+)
+
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
+
+# Compile these files as a freeDiameter extension
+FD_ADD_EXTENSION(rt_ignore_dh ${RT_IGNORE_DH_SRC})
+
+####
+## INSTALL section ##
+
+# We install with the daemon component because it is a base feature.
+INSTALL(TARGETS rt_ignore_dh
+	LIBRARY DESTINATION ${INSTALL_EXTENSIONS_SUFFIX}
+	COMPONENT freeDiameter-daemon)
Index: extensions/rt_ignore_dh/rt_ignore_dh.c
===================================================================
--- extensions/rt_ignore_dh/rt_ignore_dh.c	(revision 1043)
+++ extensions/rt_ignore_dh/rt_ignore_dh.c	(revision 1043)
@@ -0,0 +1,232 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Thomas Klausner <tk@giga.or.at>                                                                *
+*                                                                                                        *
+* Copyright (c) 2013, Thomas Klausner                                                                    *
+* All rights reserved.                                                                                   *
+*                                                                                                        *
+* Written under contract by nfotex IT GmbH, http://nfotex.com/                                           *
+*                                                                                                        *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:                                              *
+*                                                                                                        *
+* * Redistributions of source code must retain the above                                                 *
+*   copyright notice, this list of conditions and the                                                    *
+*   following disclaimer.                                                                                *
+*                                                                                                        *
+* * Redistributions in binary form must reproduce the above                                              *
+*   copyright notice, this list of conditions and the                                                    *
+*   following disclaimer in the documentation and/or other                                               *
+*   materials provided with the distribution.                                                            *
+*                                                                                                        *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT     *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS    *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                                                             *
+*********************************************************************************************************/
+
+#include <freeDiameter/extension.h>
+
+/* 
+ * Remove Destination-Hosts, putting it into Proxy-Info, and restore it to
+ * Origin-Host for answers.
+ */
+
+struct dict_object * dh_avp_do; /* cache the Destination-Host dictionary object */
+struct dict_object * oh_avp_do; /* cache the Origin-Host dictionary object */
+struct dict_object * ph_avp_do; /* cache the Proxy-Host dictionary object */
+struct dict_object * pi_avp_do; /* cache the Proxy-Info dictionary object */
+struct dict_object * ps_avp_do; /* cache the Proxy-State dictionary object */
+
+static void *memdup(void *data, size_t len)
+{
+	void *mem;
+	if ((mem=malloc(len)) == NULL)
+		return NULL;
+	memcpy(mem, data, len);
+	return mem;
+}
+
+static int restore_origin_host(struct msg **msg) {
+	struct avp *avp, *child;
+	struct avp *oh_avp = NULL;
+	struct avp *pi_avp = NULL;
+	int match;
+	void *ps, *new_oh;
+	size_t ps_len, new_oh_len = 0;
+	union avp_value val;
+
+	ps = new_oh = NULL;
+
+	CHECK_FCT(fd_msg_browse(*msg, MSG_BRW_FIRST_CHILD, &avp, NULL));
+	/* look for Origin-Host and Proxy-Info matching this host */
+	while (avp && (!oh_avp || !pi_avp)) {
+		struct avp_hdr * ahdr;
+		
+		CHECK_FCT(fd_msg_avp_hdr(avp, &ahdr));
+		if (!(ahdr->avp_flags & AVP_FLAG_VENDOR)) {
+			switch (ahdr->avp_code) {
+			case AC_ORIGIN_HOST:
+				oh_avp = avp;
+				CHECK_FCT(fd_msg_parse_dict(oh_avp, fd_g_config->cnf_dict, NULL));
+				break;
+			case AC_PROXY_INFO:
+				ps = NULL;
+				match = ps_len = 0;
+				CHECK_FCT(fd_msg_parse_dict(avp, fd_g_config->cnf_dict, NULL));
+				CHECK_FCT(fd_msg_browse(avp, MSG_BRW_FIRST_CHILD, &child, NULL));
+				int match = 0;
+				while (child && (!match || !ps)) {
+					struct avp_hdr *chdr;
+					CHECK_FCT(fd_msg_avp_hdr(child, &chdr));
+					if (!(chdr->avp_flags & AVP_FLAG_VENDOR)) {
+						switch (chdr->avp_code) {
+						case AC_PROXY_HOST:
+							if (fd_os_cmp(chdr->avp_value->os.data, chdr->avp_value->os.len, 
+								      fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len) == 0) {
+								match = 1;
+							}
+							break;
+						case AC_PROXY_STATE:
+							ps_len = chdr->avp_value->os.len;
+							ps = memdup(chdr->avp_value->os.data, ps_len);
+							if (!ps) {
+								TRACE_ERROR("malloc failure");
+								return 0;
+							}
+							break;
+						default:
+							break;
+						}
+					}
+					CHECK_FCT(fd_msg_browse(child, MSG_BRW_NEXT, &child, NULL));
+				}
+				if (match && ps) {
+					new_oh = ps;
+					new_oh_len = ps_len;
+					pi_avp = avp;
+				} else
+					free(ps);
+				break;
+			default:
+				break;
+			}
+		}
+		CHECK_FCT(fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL));
+	}
+	if (!pi_avp)
+		return 0;
+
+	memset(&val, 0, sizeof(val));
+	val.os.data = new_oh;
+	val.os.len = new_oh_len;
+	if (!oh_avp) {
+		TRACE_ERROR("Message contained no Origin-Host");
+	} else {
+		CHECK_FCT(fd_msg_avp_setvalue(oh_avp, &val));
+	}
+	fd_msg_free((msg_or_avp*)pi_avp);
+
+	fd_log_debug("Restored Origin-Host '%.*s' from Proxy-Info", (int)new_oh_len, (char *)new_oh);
+	return 0;
+}
+
+static int stow_destination_host(struct msg **msg) {
+	struct avp * avp = NULL;
+	struct avp * ph_avp = NULL;
+	struct avp * pi_avp = NULL;
+	struct avp * ps_avp = NULL;
+
+	/* Look for the Destination-Host AVP in the message */
+	CHECK_FCT(fd_msg_search_avp(*msg, dh_avp_do, &avp));
+	if (avp != NULL) {
+		struct avp_hdr * ahdr = NULL;
+		union avp_value val;
+
+		CHECK_FCT(fd_msg_avp_hdr(avp, &ahdr));
+		if (ahdr->avp_value != NULL) {
+			/* add Proxy-Info->{Proxy-Host, Proxy-State} using Destination-Host information */
+			CHECK_FCT(fd_msg_avp_new(ph_avp_do, 0, &ph_avp));
+			memset(&val, 0, sizeof(val));
+                        val.os.data = memdup(fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len);
+			val.os.len = fd_g_config->cnf_diamid_len;
+			CHECK_FCT(fd_msg_avp_setvalue(ph_avp, &val));
+
+			CHECK_FCT(fd_msg_avp_new(ps_avp_do, 0, &ps_avp));
+			memset(&val, 0, sizeof(val));
+			val.os.data = memdup(ahdr->avp_value->os.data, ahdr->avp_value->os.len);
+			val.os.len = ahdr->avp_value->os.len;
+			CHECK_FCT(fd_msg_avp_setvalue(ps_avp, &val));
+
+			CHECK_FCT(fd_msg_avp_new(pi_avp_do, 0, &pi_avp));
+			CHECK_FCT(fd_msg_avp_add(pi_avp, MSG_BRW_LAST_CHILD, ph_avp));
+			CHECK_FCT(fd_msg_avp_add(pi_avp, MSG_BRW_LAST_CHILD, ps_avp));
+
+			/* remove Destination-Host from message */
+			fd_msg_free((msg_or_avp*)avp);
+			/* add Proxy-Info */
+			CHECK_FCT(fd_msg_avp_add(*msg, MSG_BRW_LAST_CHILD, pi_avp));
+			fd_log_debug("Stowed Destination-Host '%.*s' into Proxy-Info", (int)val.os.len, (const char *)val.os.data);
+		}
+	}
+
+	return 0;
+}
+
+/* The callback for putting Destination-Host into Proxy-Info and restoring it on the way back */
+static int rt_ignore_destination_host(void * cbdata, struct msg **msg) {
+	struct msg_hdr * hdr;
+	int ret;
+
+	TRACE_ENTRY("%p %p", cbdata, msg);
+
+	CHECK_PARAMS(msg && *msg);
+
+	/* Read the message header */
+	CHECK_FCT(fd_msg_hdr(*msg, &hdr));
+	if (hdr->msg_flags & CMD_FLAG_REQUEST) {
+		ret = stow_destination_host(msg);
+	} else {
+		ret = restore_origin_host(msg);
+	}
+
+	return ret;
+}
+
+/* handler */
+static struct fd_rt_fwd_hdl * rt_ignore_destination_host_hdl = NULL;
+
+/* entry point */
+static int rt_ignore_destination_host_entry(char * conffile)
+{
+	CHECK_FCT_DO(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Host", &dh_avp_do, ENOENT),
+		     { TRACE_ERROR("Unable to find 'Destination-Host' AVP in the loaded dictionaries."); });
+	CHECK_FCT_DO(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &oh_avp_do, ENOENT),
+		     { TRACE_ERROR("Unable to find 'Origin-Host' AVP in the loaded dictionaries."); });
+	CHECK_FCT_DO(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Proxy-Host", &ph_avp_do, ENOENT),
+		     { TRACE_ERROR("Unable to find 'Proxy-Host' AVP in the loaded dictionaries."); });
+	CHECK_FCT_DO(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Proxy-Info", &pi_avp_do, ENOENT),
+		     { TRACE_ERROR("Unable to find 'Proxy-Info' AVP in the loaded dictionaries."); });
+	CHECK_FCT_DO(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Proxy-State", &ps_avp_do, ENOENT),
+		     { TRACE_ERROR("Unable to find 'Proxy-State' AVP in the loaded dictionaries."); });
+
+	/* Register the callback */
+	CHECK_FCT(fd_rt_fwd_register(rt_ignore_destination_host, NULL, RT_FWD_ALL, &rt_ignore_destination_host_hdl));
+
+	TRACE_DEBUG(INFO, "Extension 'Ignore Destination Host' initialized");
+	return 0;
+}
+
+/* Unload */
+void fd_ext_fini(void)
+{
+	/* Unregister the callbacks */
+	CHECK_FCT_DO(fd_rt_fwd_unregister(rt_ignore_destination_host_hdl, NULL), /* continue */);
+	return ;
+}
+
+EXTENSION_ENTRY("rt_ignore_destination_host", rt_ignore_destination_host_entry);
Index: extensions/rt_load_balance/CMakeLists.txt
===================================================================
--- extensions/rt_load_balance/CMakeLists.txt	(revision 1042)
+++ extensions/rt_load_balance/CMakeLists.txt	(revision 1042)
@@ -0,0 +1,20 @@
+# The rt_load_balance extension
+PROJECT("Routing extension splits requests evenly over multiple hosts, using current load as routing indicator" C)
+
+# List of source files
+SET(RT_IGNORE_DH_SRC
+	rt_load_balance.c
+)
+
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
+
+# Compile these files as a freeDiameter extension
+FD_ADD_EXTENSION(rt_load_balance ${RT_IGNORE_DH_SRC})
+
+####
+## INSTALL section ##
+
+# We install with the daemon component because it is a base feature.
+INSTALL(TARGETS rt_load_balance
+	LIBRARY DESTINATION ${INSTALL_EXTENSIONS_SUFFIX}
+	COMPONENT freeDiameter-daemon)
Index: extensions/rt_load_balance/rt_load_balance.c
===================================================================
--- extensions/rt_load_balance/rt_load_balance.c	(revision 1043)
+++ extensions/rt_load_balance/rt_load_balance.c	(revision 1043)
@@ -0,0 +1,92 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Thomas Klausner <tk@giga.or.at>                                                                *
+*                                                                                                        *
+* Copyright (c) 2013, Thomas Klausner                                                                    *
+* All rights reserved.                                                                                   *
+*                                                                                                        *
+* Written under contract by nfotex IT GmbH, http://nfotex.com/                                           *
+*                                                                                                        *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:                                              *
+*                                                                                                        *
+* * Redistributions of source code must retain the above                                                 *
+*   copyright notice, this list of conditions and the                                                    *
+*   following disclaimer.                                                                                *
+*                                                                                                        *
+* * Redistributions in binary form must reproduce the above                                              *
+*   copyright notice, this list of conditions and the                                                    *
+*   following disclaimer in the documentation and/or other                                               *
+*   materials provided with the distribution.                                                            *
+*                                                                                                        *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT     *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS    *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                                                             *
+*********************************************************************************************************/
+
+#include <freeDiameter/extension.h>
+
+/*
+ * Load balancing extension. Send request to least-loaded node.
+ */
+
+/* The callback for load balancing the requests across the peers */
+static int rt_load_balancing(void * cbdata, struct msg * msg, struct fd_list * candidates)
+{
+	struct fd_list *lic;
+	
+	TRACE_ENTRY("%p %p %p", cbdata, msg, candidates);
+	
+	CHECK_PARAMS(msg && candidates);
+	
+	/* Check if it is worth processing the message */
+	if (FD_IS_LIST_EMPTY(candidates))
+		return 0;
+
+	/* load balancing */
+	for (lic = candidates->next; lic != candidates; lic = lic->next) {
+		struct rtd_candidate * cand = (struct rtd_candidate *) lic;
+		struct peer_hdr *peer;
+		long to_receive, to_send, load;
+		int score;
+		CHECK_FCT(fd_peer_getbyid(cand->diamid, cand->diamidlen, 0, &peer));
+		CHECK_FCT(fd_peer_get_load_pending(peer, &to_receive, &to_send));
+                load = to_receive + to_send;
+		score = cand->score;
+		if (load > 5)
+			cand->score -= 5;
+		else
+			cand->score -= load;
+		TRACE_DEBUG(INFO, "evaluated peer `%.*s', score was %d, now %d", (int)cand->diamidlen, cand->diamid, score, cand->score);
+	}
+
+	return 0;
+}
+
+/* handler */
+static struct fd_rt_out_hdl * rt_load_balancing_hdl = NULL;
+
+/* entry point */
+static int rt_load_balance_entry(char * conffile)
+{
+	/* Register the callback */
+	CHECK_FCT(fd_rt_out_register(rt_load_balancing, NULL, 10, &rt_load_balancing_hdl));
+
+	TRACE_DEBUG(INFO, "Extension 'Load Balancing' initialized");
+	return 0;
+}
+
+/* Unload */
+void fd_ext_fini(void)
+{
+	/* Unregister the callbacks */
+	CHECK_FCT_DO(fd_rt_out_unregister(rt_load_balancing_hdl, NULL), /* continue */);
+	return ;
+}
+
+EXTENSION_ENTRY("rt_load_balance", rt_load_balance_entry);
