From a6fa31c2c42a5162a25c90f58cce2247d6f3b464 Mon Sep 17 00:00:00 2001
From: Marius Tomaschewski <mt@suse.de>
Date: Mon, 14 Mar 2016 12:28:54 +0100
Subject: [PATCH 1/4] imudp: Use IP_FREEBIND option for UDP sockets

Rather a proof of concept patch (originally by Marius) that allows
rsyslog to bind UDP ports even w/out specific interface being up at the
moment.
---
 runtime/net.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/runtime/net.c b/runtime/net.c
index 01129d6..f65f4d9 100644
--- a/runtime/net.c
+++ b/runtime/net.c
@@ -1348,6 +1348,9 @@ int *create_udp_socket(uchar *hostname, uchar *pszPort, int bIsServer, int rcvbu
 		}
 
 		if(bIsServer) {
+			if (setsockopt(*s, IPPROTO_IP, IP_FREEBIND, &on, sizeof(on)) < 0)
+				dbgprintf("unable to enable free IP bind socket option");
+
 			/* rgerhards, 2007-06-22: if we run on a kernel that does not support
 			 * the IPV6_V6ONLY socket option, we need to use a work-around. On such
 			 * systems the IPv6 socket does also accept IPv4 sockets. So an IPv4
-- 
2.6.2

From 5f12c9b57e208eb1d6271aecf8dee8af310762db Mon Sep 17 00:00:00 2001
From: Nirmoy Das <ndas@suse.de>
Date: Tue, 22 Mar 2016 15:42:53 +0100
Subject: [PATCH 2/4] imudp: add config option for IP_FREEBIND socket option

ipfreebind config option by default enables IP_FREEBIND socket
option with warn message on success.
---
 plugins/imudp/imudp.c           | 10 +++++++++-
 plugins/omudpspoof/omudpspoof.c |  2 +-
 runtime/net.c                   | 17 +++++++++++++----
 runtime/net.h                   |  2 +-
 tools/omfwd.c                   |  2 +-
 5 files changed, 25 insertions(+), 8 deletions(-)

diff --git a/plugins/imudp/imudp.c b/plugins/imudp/imudp.c
index 82a4a34..5507787 100644
--- a/plugins/imudp/imudp.c
+++ b/plugins/imudp/imudp.c
@@ -116,6 +116,10 @@ struct instanceConf_s {
 	int ratelimitInterval;
 	int ratelimitBurst;
 	int rcvbuf;			/* 0 means: do not set, keep OS default */
+	/*  0 means:  IP_FREEBIND is disabled
+	1 means:  IP_FREEBIND enabled + warning disabled
+	1+ means: IP+FREEBIND enabled + warning enabled */
+	int ipfreebind;
 	struct instanceConf_s *next;
 	sbool bAppendPortToInpname;
 };
@@ -179,6 +183,7 @@ static struct cnfparamdescr inppdescr[] = {
 	{ "ratelimit.interval", eCmdHdlrInt, 0 },
 	{ "ratelimit.burst", eCmdHdlrInt, 0 },
 	{ "rcvbufsize", eCmdHdlrSize, 0 },
+	{ "ipfreebind", eCmdHdlrInt, 0 },
 	{ "ruleset", eCmdHdlrString, 0 }
 };
 static struct cnfparamblk inppblk =
@@ -209,6 +214,7 @@ createInstance(instanceConf_t **pinst)
 	inst->ratelimitBurst = 10000; /* arbitrary high limit */
 	inst->ratelimitInterval = 0; /* off */
 	inst->rcvbuf = 0;
+	inst->ipfreebind = 2; /* IP_FREEBIND is enabled with warning message */
 	inst->dfltTZ = NULL;
 
 	/* node created, let's add to config */
@@ -285,7 +291,7 @@ addListner(instanceConf_t *inst)
 
 	DBGPRINTF("Trying to open syslog UDP ports at %s:%s.\n", bindName, inst->pszBindPort);
 
-	newSocks = net.create_udp_socket(bindAddr, port, 1, inst->rcvbuf);
+	newSocks = net.create_udp_socket(bindAddr, port, 1, inst->rcvbuf, inst->ipfreebind);
 	if(newSocks != NULL) {
 		/* we now need to add the new sockets to the existing set */
 		/* ready to copy */
@@ -933,6 +939,8 @@ createListner(es_str_t *port, struct cnfparamvals *pvals)
 			inst->ratelimitInterval = (int) pvals[i].val.d.n;
 		} else if(!strcmp(inppblk.descr[i].name, "rcvbufsize")) {
 			inst->rcvbuf = (int) pvals[i].val.d.n;
+		} else if(!strcmp(inppblk.descr[i].name, "ipfreebind")) {
+			inst->ipfreebind = (int) pvals[i].val.d.n;
 		} else {
 			dbgprintf("imudp: program error, non-handled "
 			  "param '%s'\n", inppblk.descr[i].name);
diff --git a/plugins/omudpspoof/omudpspoof.c b/plugins/omudpspoof/omudpspoof.c
index 5bcd91c..4176cf0 100644
--- a/plugins/omudpspoof/omudpspoof.c
+++ b/plugins/omudpspoof/omudpspoof.c
@@ -573,7 +573,7 @@ static rsRetVal doTryResume(wrkrInstanceData_t *pWrkrData)
 	}
 	DBGPRINTF("%s found, resuming.\n", pData->host);
 	pWrkrData->f_addr = res;
-	pWrkrData->pSockArray = net.create_udp_socket((uchar*)pData->host, NULL, 0, 0);
+	pWrkrData->pSockArray = net.create_udp_socket((uchar*)pData->host, NULL, 0, 0, 0);
 
 finalize_it:
 	if(iRet != RS_RET_OK) {
diff --git a/runtime/net.c b/runtime/net.c
index f65f4d9..e773a7a 100644
--- a/runtime/net.c
+++ b/runtime/net.c
@@ -1208,7 +1208,7 @@ void closeUDPListenSockets(int *pSockArr)
  * 1 - server, 0 - client
  * param rcvbuf indicates desired rcvbuf size; 0 means OS default
  */
-int *create_udp_socket(uchar *hostname, uchar *pszPort, int bIsServer, int rcvbuf)
+int *create_udp_socket(uchar *hostname, uchar *pszPort, int bIsServer, int rcvbuf, int ipfreebind)
 {
         struct addrinfo hints, *res, *r;
         int error, maxs, *s, *socks, on = 1;
@@ -1348,8 +1348,6 @@ int *create_udp_socket(uchar *hostname, uchar *pszPort, int bIsServer, int rcvbu
 		}
 
 		if(bIsServer) {
-			if (setsockopt(*s, IPPROTO_IP, IP_FREEBIND, &on, sizeof(on)) < 0)
-				dbgprintf("unable to enable free IP bind socket option");
 
 			/* rgerhards, 2007-06-22: if we run on a kernel that does not support
 			 * the IPV6_V6ONLY socket option, we need to use a work-around. On such
@@ -1363,7 +1361,18 @@ int *create_udp_socket(uchar *hostname, uchar *pszPort, int bIsServer, int rcvbu
 			     && (errno != EADDRINUSE)
 	#		endif
 			   ) {
-				errmsg.LogError(errno, NO_ERRCODE, "bind");
+				if (errno == EADDRNOTAVAIL && ipfreebind) {
+					if (setsockopt(*s, IPPROTO_IP, IP_FREEBIND, &on, sizeof(on)) < 0) {
+						errmsg.LogError(errno, NO_ERRCODE, "setsockopt(IP_FREEBIND)");
+					}
+					else if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) {
+						errmsg.LogError(errno, NO_ERRCODE, "bind with IP_FREEBIND");
+					} else {
+						if (ipfreebind > 1)
+							errmsg.LogMsg(0, RS_RET_OK_WARN, LOG_WARNING, "bound address %s IP free", hostname);
+						continue;
+					}
+				}
 				close(*s);
 				*s = -1;
 				continue;
diff --git a/runtime/net.h b/runtime/net.h
index d7a7b51..b664fb4 100644
--- a/runtime/net.h
+++ b/runtime/net.h
@@ -137,7 +137,7 @@ BEGINinterface(net) /* name must also be changed in ENDinterface macro! */
 	void (*PrintAllowedSenders)(int iListToPrint);
 	void (*clearAllowedSenders)(uchar*);
 	void (*debugListenInfo)(int fd, char *type);
-	int *(*create_udp_socket)(uchar *hostname, uchar *LogPort, int bIsServer, int rcvbuf);
+	int *(*create_udp_socket)(uchar *hostname, uchar *LogPort, int bIsServer, int rcvbuf, int ipfreebind);
 	void (*closeUDPListenSockets)(int *finet);
 	int (*isAllowedSender)(uchar *pszType, struct sockaddr *pFrom, const char *pszFromHost); /* deprecated! */
 	rsRetVal (*getLocalHostname)(uchar**);
diff --git a/tools/omfwd.c b/tools/omfwd.c
index f2a721c..29d7f14 100644
--- a/tools/omfwd.c
+++ b/tools/omfwd.c
@@ -760,7 +760,7 @@ static rsRetVal doTryResume(wrkrInstanceData_t *pWrkrData)
 		pWrkrData->f_addr = res;
 		pWrkrData->bIsConnected = 1;
 		if(pWrkrData->pSockArray == NULL) {
-			pWrkrData->pSockArray = net.create_udp_socket((uchar*)pData->target, NULL, 0, 0);
+			pWrkrData->pSockArray = net.create_udp_socket((uchar*)pData->target, NULL, 0, 0, 0);
 		}
 	} else {
 		CHKiRet(TCPSendInit((void*)pWrkrData));
-- 
2.6.2

From 3e3bd32f10e17dd66b64c2667ea2e49d65afca6f Mon Sep 17 00:00:00 2001
From: Nirmoy Das <ndas@suse.de>
Date: Wed, 23 Mar 2016 10:46:55 +0100
Subject: [PATCH 3/4] cleanup/beautify, removed numerical with defines

---
 plugins/imudp/imudp.c | 2 +-
 runtime/net.c         | 4 ++--
 runtime/net.h         | 5 +++++
 3 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/plugins/imudp/imudp.c b/plugins/imudp/imudp.c
index 5507787..37d8aad 100644
--- a/plugins/imudp/imudp.c
+++ b/plugins/imudp/imudp.c
@@ -214,7 +214,7 @@ createInstance(instanceConf_t **pinst)
 	inst->ratelimitBurst = 10000; /* arbitrary high limit */
 	inst->ratelimitInterval = 0; /* off */
 	inst->rcvbuf = 0;
-	inst->ipfreebind = 2; /* IP_FREEBIND is enabled with warning message */
+	inst->ipfreebind = IPFREEBIND_ENABLED_WITH_LOG;
 	inst->dfltTZ = NULL;
 
 	/* node created, let's add to config */
diff --git a/runtime/net.c b/runtime/net.c
index e773a7a..4cbedba 100644
--- a/runtime/net.c
+++ b/runtime/net.c
@@ -1361,14 +1361,14 @@ int *create_udp_socket(uchar *hostname, uchar *pszPort, int bIsServer, int rcvbu
 			     && (errno != EADDRINUSE)
 	#		endif
 			   ) {
-				if (errno == EADDRNOTAVAIL && ipfreebind) {
+				if (errno == EADDRNOTAVAIL && ipfreebind != IPFREEBIND_DISABLED) {
 					if (setsockopt(*s, IPPROTO_IP, IP_FREEBIND, &on, sizeof(on)) < 0) {
 						errmsg.LogError(errno, NO_ERRCODE, "setsockopt(IP_FREEBIND)");
 					}
 					else if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) {
 						errmsg.LogError(errno, NO_ERRCODE, "bind with IP_FREEBIND");
 					} else {
-						if (ipfreebind > 1)
+						if (ipfreebind >= IPFREEBIND_ENABLED_WITH_LOG)
 							errmsg.LogMsg(0, RS_RET_OK_WARN, LOG_WARNING, "bound address %s IP free", hostname);
 						continue;
 					}
diff --git a/runtime/net.h b/runtime/net.h
index b664fb4..b2f49e3 100644
--- a/runtime/net.h
+++ b/runtime/net.h
@@ -39,6 +39,11 @@ typedef enum _TCPFRAMINGMODE {
 #define ADDR_NAME 0x01 /* address is hostname wildcard) */
 #define ADDR_PRI6 0x02 /* use IPv6 address prior to IPv4 when resolving */
 
+/* defines for IP_FREEBIND, currently being used in imudp */
+#define IPFREEBIND_DISABLED 0x00 /* don't enable IP_FREEBIND in  sock option */
+#define IPFREEBIND_ENABLED_NO_LOG 0x01 /* enable IP_FREEBIND but no warn on success */
+#define IPFREEBIND_ENABLED_WITH_LOG 0x02 /* enable IP_FREEBIND and warn on success */
+
 #ifdef OS_BSD
 #	ifndef _KERNEL
 #		define s6_addr32 __u6_addr.__u6_addr32
-- 
2.6.2

From 66ce7eb11351eb9ece917e50b4218e4c55ca15e9 Mon Sep 17 00:00:00 2001
From: Nirmoy Das <ndas@suse.de>
Date: Tue, 29 Mar 2016 17:16:01 +0200
Subject: [PATCH 4/4] portability: define IP_FREEBIND if it is not already
 defined.

---
 runtime/net.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/runtime/net.h b/runtime/net.h
index b2f49e3..6809570 100644
--- a/runtime/net.h
+++ b/runtime/net.h
@@ -39,6 +39,10 @@ typedef enum _TCPFRAMINGMODE {
 #define ADDR_NAME 0x01 /* address is hostname wildcard) */
 #define ADDR_PRI6 0x02 /* use IPv6 address prior to IPv4 when resolving */
 
+/* portability: incase IP_FREEBIND is not defined */
+#ifndef IP_FREEBIND
+#define IP_FREEBIND 0
+#endif
 /* defines for IP_FREEBIND, currently being used in imudp */
 #define IPFREEBIND_DISABLED 0x00 /* don't enable IP_FREEBIND in  sock option */
 #define IPFREEBIND_ENABLED_NO_LOG 0x01 /* enable IP_FREEBIND but no warn on success */
-- 
2.6.2

