figuring out how to do this is navigating a twisty maze of helper
functions, all different ;-)
there are a few things that look odd here if I am reading the code
properly.
rsyslog makes one call to the UDPSend routine for all forwarded messages,
this means that they must all send the same format.
the UDPSend routine then walks through all destinations attempting to send
the message to them. and considers the message successfully sent if any of
them can be sent. I would have expected that it would only be considered
successful if all of them succeded.
UDPSend attempts to use the sockets that were created to listening for
messages to send it's message out. in a multi-homed machine the local IP
address of those sockets may not be appropriate for the relay. also, what
would happen if a system recieves via TCP and forwards via UDP and so
doesn't have any UDP listener sockets.
so now, my suggestion
On Linux in /etc/sysctl.conf add the line
net.ipv4.ip_nonlocal_bind=1
this tells the kernel not to fail bind requests to IP addresses that don't
exist on the box
then in omfwd.c
in the routine that calls UDPSend, modify it to pass the source IP address
of the message to UDPSend
create a new filehandle array to use for outbound messages (one filehandle
for each destination since they may end up with different source IPs)
in UDPSend
three options.
1. default
use the appropriate filehandle for each destination and send the
message. this is similar to what currently takes place, but instead of
walking through each open socket for each destination, there should only
be one sendto per destination. when opening the socket it should not be
nessasary to do a bind, just issue the socket call.
if there is anything in the syslog standard specifying the source port
(I don't think there is, although many implementations use port 514
becouse they do the same thing that rsyslog is doing and re-use listening
sockets to send messages)
2. randomize source ports to support external load balancers
same as default, except that every X (configurable) messages close and
re-open the sockets.
the reason for this is that each time a message is first sent the OS
picks a random source port that will be used for all messages sent through
that socket. by closing the socket once in a while, load balancers that
balance based on source IP + source port + destination IP + destination
port will be able to function (a similar feature for TCP based transports
would be useful for the same reason)
3. forge the source IP/port
for each message that is sent, open a new socket, then issue a 'bind'
command for that socket (filehandle) that sets the local IP address to the
IP address that the message initially came from, then issue the sendto,
then close the filehandle.
there is no need for an array of sockets in this case as the socket
needs to be re-opened for each message/destination. in theory there is a
possibility of a performance gain by keeping sockets open and re-using
them, but since each socket is unique to the sender + destination there
would be a large number of sockets and a low probability of re-using a
socket for the next message.
in sysklogd I implemented #2 with the simple code below (once I created a
different filehandle to use for outbound connections)
this closed and re-opened the socket for each message, doing it every X
messages would save on the overhead and be just as good in practice.
the bind command that's commented out is basicly what would be needed for
#3, but with real values for the source.sin_addr.s_addr (and
source.sin_port if you wanted to forge the port as well, leaving it zero
lets the OS pick a port number)
if (finet >0) {
/* close and re-open the outbound socket after each message so that
the source port is randomized and load balancers can distribute the messages */
close(finet);
finet = create_inet_socket();
/* if we want to forge the source IP for the outbound packets, this is
the place to do it by seeting the IP address to the address we received the
message from
struct sockaddr_in source;
source.sin_addr.s_addr = 0x00000000;
source.sin_port = 0x0000;
bind(finet,(struct sockaddr *) &source,sizeof(source));*/
}
the create_inet_socket() routine is:
static int create_inet_socket()
{
int fd, on = 1;
int sockflags;
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
logerror("syslog: Unknown protocol, suspending inet service.");
return fd;
}
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, \
(char *) &on, sizeof(on)) < 0 ) {
logerror("setsockopt(REUSEADDR), suspending inet");
close(fd);
return -1;
}
/* We must not block on the network socket, in case a packet
* gets lost between select and recv, otherise the process
* will stall until the timeout, and other processes trying to
* log will also stall.
*/
if ((sockflags = fcntl(fd, F_GETFL)) != -1) {
sockflags |= O_NONBLOCK;
/*
* SETFL could fail too, so get it caught by the subsequent
* error check.
*/
sockflags = fcntl(fd, F_SETFL, sockflags);
}
if (sockflags == -1) {
logerror("fcntl(O_NONBLOCK), suspending inet");
close(fd);
return -1;
}
return fd;
}
this isn't a patch like you asked for and I offered to put togeather, but
is it enough to clearly explain this? if not I can continue to work on the
patch.
David Lang
On Tue, 24 Feb 2009, Rainer Gerhards wrote:
> David,
>
> Excellent, please do as you have time. I'll make sure it fits.
> Thread-safety, btw., is not a big issue at this level as the output
> modules are guaranteed to be never called by multiple threads
> concurrently. That was a trade-off to enable other folks to easily write
> them (but I have an option in the back of my head that a module can tell
> the engine it *is* capable to run on multiple threads concurrently...).
>
> Rainer
>
>> -----Original Message-----
>> From: [email protected] [mailto:rsyslog-
>> [email protected]] On Behalf Of [email protected]
>> Sent: Tuesday, February 24, 2009 9:43 AM
>> To: rsyslog-users
>> Subject: Re: [rsyslog] UDP source forging.
>>
>> On Tue, 24 Feb 2009, Rainer Gerhards wrote:
>>
>>> Hi David et all,
>>>
>>> Currently rsyslog does not support this and I have to admit I was
>> always
>>> very hesitant to add it (I see potential for misuse). Co-
>> incidentally, I
>>> received a similar request and was about to relay it to the mailing
>> list
>>> to gather feedback. As it looks, this no longer is necessary ;)
>>>
>>> When I thought about implementation, I originally thought about raw
>>> sockets (which indeed require root access), but if there is any
> other
>>> way, I would be most interested. If you can provide some code, I
> will
>>> happily integrate it. I think an addition to the omfwd module, in
> udp
>>> forwarding, together with a new directive ($SpoofOriginalUDPAddress
>> or
>>> so...) would be the right way to go.
>>
>> I'll see about hacking in some example code (probably without any
>> config
>> option and not thread-safe) and send it to you.
>>
>> there's another similar change in the same area that I was looking at,
>> I'll mock it up as well.
>>
>> David Lang
>>
>>> Rainer
>>>
>>>> -----Original Message-----
>>>> From: [email protected]
>>>> [mailto:[email protected]] On Behalf Of
>> [email protected]
>>>> Sent: Tuesday, February 24, 2009 4:40 AM
>>>> To: rsyslog-users
>>>> Subject: Re: [rsyslog] UDP source forging.
>>>>
>>>> On Mon, 23 Feb 2009, RB wrote:
>>>>
>>>>> On Mon, Feb 23, 2009 at 18:11, <[email protected]> wrote:
>>>>>> I have a need to use some products that are stupid enough
>>>> to ignore the
>>>>>> host field in the syslog message and instead base
>>>> everything on the IP
>>>>>> address the message originates from.
>>>>>>
>>>>>> some other syslog servers can handle this by forging the
>>>> source of the UDP
>>>>>> packet, can rsyslog do this?
>>>>>
>>>>> So is rsyslog originating the messages, or are you using it to
>>>>> aggregate them and then feed them on to the other [bad]
>>>> acceptors? I
>>>>> am unaware of a way to get rsyslog to forge packets (short
>>>> of writing
>>>>> an output module), but unless you must get another syslog
>>>> daemon into
>>>>> the mix, you may be better off just feeding your messages
>>>> directly to
>>>>> the other collector.
>>>>
>>>> rsyslog would be the relay from one non-routed network to another
>>>> non-routed network.
>>>>
>>>> this could be a fairly simple change to the UDP output module
>>>> (adding a
>>>> couple commands around the sending of a message), but before
>>>> I dove in to
>>>> do that I wanted to see if I had missed this feature anywhere.
>>>>
>>>> David Lang
>>>> _______________________________________________
>>>> rsyslog mailing list
>>>> http://lists.adiscon.net/mailman/listinfo/rsyslog
>>>> http://www.rsyslog.com
>>>>
>>> _______________________________________________
>>> rsyslog mailing list
>>> http://lists.adiscon.net/mailman/listinfo/rsyslog
>>> http://www.rsyslog.com
>>>
>> _______________________________________________
>> rsyslog mailing list
>> http://lists.adiscon.net/mailman/listinfo/rsyslog
>> http://www.rsyslog.com
> _______________________________________________
> rsyslog mailing list
> http://lists.adiscon.net/mailman/listinfo/rsyslog
> http://www.rsyslog.com
>
_______________________________________________
rsyslog mailing list
http://lists.adiscon.net/mailman/listinfo/rsyslog
http://www.rsyslog.com