[Xorp-users] Question on supporting multiple routing tables [PATCH]

Ben Greear greearb at candelatech.com
Thu Aug 30 10:09:51 PDT 2007


Here's a CVS diff that appears to add BINDTODEVICE support.  I still 
don't have OSPF
working properly..but I am not sure if that is related to my 
configuration or socket binding.

Comments welcome.

Thanks,
Ben


Index: fea/data_plane/io/io_ip_socket.cc
===================================================================
RCS file: /cvs/xorp/fea/data_plane/io/io_ip_socket.cc,v
retrieving revision 1.10
diff -u -r1.10 io_ip_socket.cc
--- fea/data_plane/io/io_ip_socket.cc   26 Jul 2007 01:18:40 -0000      1.10
+++ fea/data_plane/io/io_ip_socket.cc   30 Aug 2007 17:04:40 -0000
@@ -1744,8 +1744,8 @@
     if ((ifp == NULL) || (vifp == NULL)) {
        // No vif found. Ignore this packet.
        XLOG_WARNING("proto_socket_read() failed: "
-                    "RX packet from %s to %s: no vif found",
-                    cstring(src_address), cstring(dst_address));
+                    "RX packet from %s to %s: no vif found, pif_index: %u",
+                    cstring(src_address), cstring(dst_address), pif_index);
        return;                 // Error
     }
     if (! (ifp->enabled() || vifp->enabled())) {
@@ -2024,6 +2024,25 @@
                return (XORP_ERROR);
            }

+#ifndef HOST_OS_WINDOWS
+           if (if_name.c_str() && if_name.c_str()[0]) {
+               if (setsockopt(_proto_socket_out, SOL_SOCKET, 
SO_BINDTODEVICE,
+                              if_name.c_str(), IFNAMSIZ)) {
+                   error_msg += c_format("setsockopt(SO_BINDTODEVICE, 
%s) failed: %s",
+                                         if_name.c_str(), strerror(errno));
+               }
+           }
+           else {
+               // Un-bind just in case we were previously bound...
+               char nl[1];
+               nl[0] = 0;
+               if (setsockopt(_proto_socket_out, SOL_SOCKET, 
SO_BINDTODEVICE, nl, 0)) {
+                   error_msg += c_format("setsockopt(SO_BINDTODEVICE, 
NULL) failed: %s",
+                                         strerror(errno));
+               }
+           }
+#endif
+
            //
            // Now hook the data
            //


Pavlin Radoslavov wrote:
> Ben Greear <greearb at candelatech.com> wrote:
>
>   
>>>> I'm sure you'll want to be able to bind the socket to a local IP, but
>>>> if you want to leave out the SO_BINDTODEVICE I can test it and see
>>>> if it works.  I can add the SO_BINDTODEVICE if needed and send you a patch.
>>>>     
>>>>         
>>> Currently, we don't bind to the local IP (we do but in certain cases
>>> only). I believe even if you bind to a local IP you cannot really
>>> force the unicast IP packet to exit the system on the particular
>>> interface. Anyway, I might be wrong here, so please let me know if
>>> you find that bind()-ing only gives us the desired behavior.
>>>   
>>>       
>> If you set up the routing tables and rules correctly, then binding to a 
>> local IP
>> is probably sufficient.  If you are certain that you want the pkt to 
>> leave by a certain
>> interface, then I don't think it can ever hurt to bind to that local IP, 
>> but just in case,
>> it could also be a config option...
>>     
>>> BTW, what protocols are you planning to run? Without SO_BINDTODEVICE
>>> we might have to use different solution for each type of
>>> sockets/packets: raw IP packets, TCP, UDP.
>>> FYI, the I/O system-specific stuff is inside fea/data_plane/io,
>>> though io_tcpudp_socket.cc itself uses the xorp/libcomm wrapper
>>> library.
>>> BGP only doesn't use the FEA (yet) and does its own TCP connection
>>> (inside bgp/socket.{hh,cc}).
>>>
>>> Also, there could be some gotchas with RIP's UDP socket, but lets
>>> address first the protocols you are actually going to use.
>>>   
>>>       
>> At a minimum, I want to support OSPF.  However, I'd like to have options to
>> do other protocols as well.  In my own experience, binding UDP is very 
>> similar
>> to binding TCP, but if you want some sample code I can post it.  I'm not 
>> sure
>> about raw IP packets.
>>
>> Also, for my application, it will always be running on Linux, so I can 
>> depend on
>> SO_BINDTODEVICE being available...
>>     
>
> I played a bit with SO_BINDTODEVICE, and here is what I found.
> For the record, I am using Gentoo 2006.1 with kernel 2.6.20.1.
>
> In my test I opened an UDP socket, then used
> setsockopt(SO_BINDTODEVICE) to bind the socket to a specific
> interface (eth1), and then used sendto() to transmit a single UDP packet.
> At the end I am attaching the test program in case someone wants to play
> with it.
>
> * If the destination address belongs to the same network interface
>   that is used with SO_BINDTODEVICE (eth1), then the transmitted packets
>   are sent over the loopback interface (lo) instead of the external
>   (physical) interface (eth1).
>
> * If the destination address belongs to the same subnet as the
>   network interface that is used with SO_BINDTODEVICE (eth1), then
>   an ARP request is sent first by the kernel (as we would expect).
>   If the ARP is resolved, then the UDP packet should follow.
>
> * For all other (i.e., remote) destination addresses or IP
>   addresses that belong to some other interfaces of that host, an
>   ARP request is sent first by the kernel for the destination
>   address. If the ARP request is not answered (as it would be the
>   case for a remote destination unless somebody else is acting as a
>   proxy), then the UDP transmission will fail.
>
> >From the above observations, the interesting behavior (at least for
> me) is that SO_BINDTODEVICE can be used to force a packet with
> destination address that belongs to some other interface of that
> host (e.g., eth0) to be transmitted over the specified interface
> (eth1). Without SO_BINDTODEVICE such packets are transmitted over
> the loopback interface (lo).
>
> I continued the testing by connecting eth0 directly with eth1, and
> then used SO_BINDTODEVICE to see whether the UDP packet will be
> actually sent out of interface eth1 to eth0's IP address.
> It turned out that the preceding ARP request out of eth1 is never
> answered by the eth0 interface (probably the kernel recognizes that
> the origin of the ARP is that host itself so the ARP reply is
> suppressed).
> However, after playing a bit I was able to add the ARP entry by
> hand, but it was a bit tricky:
>
> 1. Added the ARP entry, where xx:xx:xx:xx:xx:xx is the Ethernet
> address of eth0. Note that it must be done before configuring
> the IP address on eth0:
> arp -s 10.0.0.3 xx:xx:xx:xx:xx:xx
>
> 2. Configure the IP address on eth0:
> ip addr add 10.0.0.3/8 dev eth0
>
> Finally after that I was able to see the UDP packet going out on
> eth1 with destination address the IP address on eth0.
>
> In conclusion, if you are running 2+ virtual routing instances each
> of them with separate forwarding tables in the kernel and a
> allocated corresponding subset of the physical interfaces, then it
> is possible to use SO_BINDTODEVICE to make sure that unicast packets
> transmit by one virtual router are transmit over the physical link
> to another virtual router, but it is tricky (see the above ARP
> hack).
>
> However, if we don't care that the unicast packets between the
> virtual instances are transmitted over the loopback interface, then
> we don't need SO_BINDTODEVICE: just bind(2)-ing to the outgoing
> interface's IP address will be sufficient to guarantee the IP source
> address is set to the desired value.
>
> Regards,
> Pavlin
>
>   
> ------------------------------------------------------------------------
>
> /* Test program to play with setsockopt(SO_BINDTODEVICE) on Linux */
>
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
>
> #include <sys/socket.h>
> #include <net/if.h>
> #include <netinet/in.h>
>
> #define MY_INTERFACE_NAME "eth1"
> #define MY_LOCAL_ADDR "10.0.0.1"
> #define MY_DST_ADDR "10.0.0.3"
>
> int
> main()
> {
>     char ifname[IFNAMSIZ];
>     int s;
>     char data[50];
>     struct sockaddr_in sin;
>     socklen_t sin_len = sizeof(sin);
>
>     /* Init */
>     strncpy(ifname, MY_INTERFACE_NAME, IFNAMSIZ);
>     memset(data, 1, sizeof(data));
>     s = socket(AF_INET, SOCK_DGRAM, 0);
>
>     /* Optionally: bind to a local IP address */
> #if 0
>     memset(&sin, 0, sizeof(sin));
>     sin.sin_family = AF_INET;
>     sin.sin_port = htons(0);
>     sin.sin_addr.s_addr = inet_addr(MY_LOCAL_ADDR);
>     if (bind(s, (struct sockaddr*)&sin, sin_len) != 0) {
> 	perror("bind()");
> 	exit(1);
>     }
> #endif
>
>     /* Bind to the interface */
> #if 1
>     if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, ifname, sizeof(ifname))
> 	< 0) {
> 	perror("setsockopt(SO_BINDTODEVICE)");
> 	exit(1);
>     }
> #endif
>
>     /* Set the dest. address and port */
>     memset(&sin, 0, sizeof(sin));
>     sin.sin_family = AF_INET;
>     sin.sin_port = htons(5000);
>     sin.sin_addr.s_addr = inet_addr(MY_DST_ADDR);
>
>     /* Send the packet */
>     if (sendto(s, data, sizeof(data), 0, (struct sockaddr*)&sin, sin_len)
> 	< sizeof(data)) {
> 	perror("sendto()");
> 	exit(1);
>     }
>
>     exit(0);
> }
>   


-- 
Ben Greear <greearb at candelatech.com> 
Candela Technologies Inc  http://www.candelatech.com




More information about the Xorp-users mailing list