Subject: [dtn-users] EthConvergenceLayer

From: Lana Black <[email-masked]>
Date: Fri, 15 Feb 2013 07:10:18 +0000

Hi,

I managed to get ethernet convergence layer working. Changes were made
to both DTN and oasys. See the patches attached. Any chance for this to
be commited?
diff -r 4cf51fd37f22 util/URI.cc
--- a/util/URI.cc	Tue Jul 24 16:09:54 2012 +0100
+++ b/util/URI.cc	Fri Feb 15 07:02:30 2013 +0000
@@ -231,9 +231,14 @@
             }
             host_end++; // include '[' character
         } else {
-            host_end = authority.find(':', curr_pos);
-            if (host_end == std::string::npos) {
+            // Special case for ethernet uri
+            if(this->scheme() == "eth") {
                 host_end = authority.length();
+            } else {
+                host_end = authority.find(':', curr_pos);
+                if (host_end == std::string::npos) {
+                    host_end = authority.length();
+                }
             }
         }
 
@@ -418,6 +423,11 @@
         return validate_ip_literal(host.substr(1, host.length() - 2));
     }
 
+    // check for ethernet literal
+    if(this->scheme() == "eth") {
+        return validate_eth_literal(host);
+    }
+
     // check for valid characters
     for (unsigned int i = 0; i < host.length(); ++i) {
         char c = host.at(i);
@@ -872,6 +882,29 @@
 }
 
 //----------------------------------------------------------------------
+uri_parse_err_t URI::validate_eth_literal(const std::string& host) const
+{
+    if(host.length() != 17) {
+        log_debug_p(URI_LOG, "URI::validate_eth_literal: bad host");
+        return URI_PARSE_BAD_HOST;
+    }
+
+    for(int i = 0; i < host.length(); i++) {
+        if((i + 1) % 3) {
+            if(is_hexdig(host.at(i)))
+                continue;
+        } else {
+            if(host.at(i) == ':')
+                continue;
+        }
+
+        return URI_PARSE_BAD_HOST;
+    }
+
+    return URI_PARSE_OK;
+}
+
+//----------------------------------------------------------------------
 void
 URI::normalize_path()
 {
diff -r 4cf51fd37f22 util/URI.h
--- a/util/URI.h	Tue Jul 24 16:09:54 2012 +0100
+++ b/util/URI.h	Fri Feb 15 07:02:30 2013 +0000
@@ -275,6 +275,7 @@
     uri_parse_err_t validate_query() const;
     uri_parse_err_t validate_fragment() const;
     uri_parse_err_t validate_ip_literal(const std::string& host) const;
+    uri_parse_err_t validate_eth_literal(const std::string& host) const;
  
     /**
      * Normalize the URI to the standard format as described in RFC 3986
diff -r 660ce8ebcd3b servlib/conv_layers/EthConvergenceLayer.cc
--- a/servlib/conv_layers/EthConvergenceLayer.cc	Thu Jan 10 17:14:59 2013 +0000
+++ b/servlib/conv_layers/EthConvergenceLayer.cc	Fri Feb 15 07:06:08 2013 +0000
@@ -23,12 +23,14 @@
 
 #include <sys/poll.h>
 #include <stdlib.h>
+#include <fcntl.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <net/ethernet.h>
 #include <netpacket/packet.h>
 #include <sys/ioctl.h>
+#include <errno.h>
 
 #include <oasys/io/NetUtils.h>
 #include <oasys/io/IO.h>
@@ -59,6 +61,7 @@
 EthConvergenceLayer::Params::serialize(oasys::SerializeAction *a)
 {
 	a->process("beacon_interval", &beacon_interval_);
+	a->process("interface", &if_name_);
 }
 
 /******************************************************************************
@@ -100,6 +103,7 @@
     oasys::OptParser p;
 
     p.addopt(new oasys::UIntOpt("beacon_interval", &params->beacon_interval_));
+    p.addopt(new oasys::StringOpt("interface", &params->if_name_));
 
     if (! p.parse(argc, argv, invalidp)) {
         return false;
@@ -169,7 +173,7 @@
 
     Receiver *receiver = (Receiver *)iface->cl_info();
     receiver->set_should_stop();
-    // receiver->interrupt_from_io();
+    //receiver->interrupt();
     while (! receiver->is_stopped()) {
         oasys::Thread::yield();
     }
@@ -186,6 +190,7 @@
     ASSERT(link != NULL);
     ASSERT(!link->isdeleted());
     ASSERT(link->cl_info() != NULL);
+    Params* link_params = dynamic_cast<Params*>(link->cl_info());
 
     log_debug("EthConvergenceLayer::open_contact: "
               "opening contact to link *%p", link.object());
@@ -199,7 +204,7 @@
     }
     
     // create a new connection for the contact
-    Sender* sender = new Sender(((EthCLInfo*)link->cl_info())->if_name_,
+    Sender* sender = new Sender(link_params->if_name_.c_str(),
                                 link->contact());
     contact->set_cl_info(sender);
 
@@ -265,6 +270,7 @@
     ASSERT(params != NULL);
 
     buf->appendf("beacon_interval: %u\n", params->beacon_interval_);
+    buf->appendf("interface: %s\n", params->if_name_.c_str());
 }
 
 //----------------------------------------------------------------------
@@ -403,8 +409,9 @@
                          link->name());
                 return;
             }
-            ASSERT(link->cl_info() == NULL);
-            link->set_cl_info(new EthCLInfo(if_name_));
+            ASSERT(link->cl_info() != NULL);
+            Params *params = dynamic_cast<Params*>(link->cl_info());
+            params->if_name_ = if_name_;
             l.unlock();
         }
 
@@ -418,15 +425,16 @@
         }
 
         ASSERT(link->cl_info() != NULL);
-        ASSERT(strcmp(((EthCLInfo*)link->cl_info())->if_name_, if_name_) == 0);
+        ASSERT(dynamic_cast<Params*>(link->cl_info())->if_name_ == if_name_);
 
         if(!link->isavailable()) {
             log_info("EthConvergenceLayer::Receiver::process_data: "
                      "Got beacon for previously unavailable link %s",
                      link->name());
             
-            // XXX/demmer something should be done here to kick the link...
-            log_err("XXX/demmer do something about link availability");
+            // Kicking the link available here
+            BundleDaemon::post(
+                    new LinkStateChangeRequest(link, Link::OPEN, ContactUpEvent::DISCOVERY));
         }
         
         /**
@@ -434,14 +442,14 @@
          * will delete it when it bubbles to the top of the timer
          * queue. Then create a new timer.
          */
-        BeaconTimer *timer = ((EthCLInfo*)link->cl_info())->timer;
+        BeaconTimer *timer = ((Params*)link->cl_info())->timer;
         if (timer)
             timer->cancel();
 
         timer = new BeaconTimer(next_hop_string); 
         timer->schedule_in(ETHCL_BEACON_TIMEOUT_INTERVAL);
         
-        ((EthCLInfo*)link->cl_info())->timer = timer;
+        ((Params*)link->cl_info())->timer = timer;
 
         l.unlock();
     }
@@ -488,6 +496,7 @@
 {
     int sock;
     int cc;
+    int flags;
     struct sockaddr_ll iface;
     unsigned char buffer[MAX_ETHER_PACKET];
 
@@ -497,6 +506,16 @@
                 "Couldn't open socket.");       
         exit(1);
     }
+
+    // Make the socket non-blocking
+    flags = fcntl(sock, F_GETFL, 0);
+    if(flags == -1) {
+        perror("fcntl");
+        log_err("EthConvergenceLayer::Receiver::run() "
+                "Couldn't get socket flags.");
+        exit(1);
+    }
+    fcntl(sock, F_SETFL, flags | O_NONBLOCK);
    
     // figure out the interface index of the device with name if_name_
     struct ifreq req;
@@ -517,19 +536,23 @@
     while(true) {
         cc=read (sock, buffer, MAX_ETHER_PACKET);
         if(cc<=0) {
-            perror("EthConvergenceLayer::Receiver::run()");
-            exit(1);
-        }
-        struct ether_header* hdr=(struct ether_header*)buffer;
+            // EAGAIN means we haven't received anything yet
+            if(errno != EAGAIN) {
+                perror("EthConvergenceLayer::Receiver::run()");
+                exit(1);
+            }
+        } else {
+            struct ether_header* hdr=(struct ether_header*)buffer;
   
-        if(ntohs(hdr->ether_type)==ETHERTYPE_DTN) {
-            process_data(buffer, cc);
-        }
-        else if(ntohs(hdr->ether_type)!=0x800)
-        {
-            log_err("Got non-DTN packet in Receiver, type %4X.",
-                    ntohs(hdr->ether_type));
-            // exit(1);
+            if(ntohs(hdr->ether_type)==ETHERTYPE_DTN) {
+                process_data(buffer, cc);
+            }
+            else if(ntohs(hdr->ether_type)!=0x800)
+            {
+                log_err("Got non-DTN packet in Receiver, type %4X.",
+                        ntohs(hdr->ether_type));
+                // exit(1);
+            }
         }
 
         if(should_stop())
@@ -546,11 +569,13 @@
 /**
  * Constructor for the active connection side of a connection.
  */
-EthConvergenceLayer::Sender::Sender(char* if_name,
+EthConvergenceLayer::Sender::Sender(const char* if_name,
                                     const ContactRef& contact)
     : Logger("EthConvergenceLayer::Sender", "/dtn/cl/eth/sender"),
       contact_(contact.object(), "EthConvergenceLayer::Sender")
 {
+    ASSERT(strlen(if_name) > 0);
+
     struct ifreq req;
     struct sockaddr_ll iface;
     LinkRef link = contact->link();
diff -r 660ce8ebcd3b servlib/conv_layers/EthConvergenceLayer.h
--- a/servlib/conv_layers/EthConvergenceLayer.h	Thu Jan 10 17:14:59 2013 +0000
+++ b/servlib/conv_layers/EthConvergenceLayer.h	Fri Feb 15 07:06:08 2013 +0000
@@ -82,29 +82,6 @@
     } __attribute__((packed));
 
     /**
-     * Data state of a Eth cl
-     *
-     */
-    class EthCLInfo : public CLInfo {
-      public:
-        EthCLInfo(char* if_name) {
-            memset(if_name_,0,IFNAMSIZ);
-            strcpy(if_name_,if_name);
-            timer    = NULL;
-        }
-
-        ~EthCLInfo() {
-          if(timer)
-              delete timer;
-        }
-
-        // Name of the device 
-        char if_name_[IFNAMSIZ];
-
-        BeaconTimer* timer;
-    };
-
-    /**
      * Constructor.
      */
     EthConvergenceLayer();
@@ -177,7 +154,15 @@
          */
         virtual void serialize(oasys::SerializeAction *a);
 
+        ~Params() {
+            if(timer)
+                delete timer;
+        }
+
         u_int32_t beacon_interval_;       ///< Beacon Interval
+        std::string if_name_;             ///< Interface name to bind sender to
+        
+        BeaconTimer *timer;               ///< XXX this should be moved somewhere else
     };
     
     /**
@@ -241,7 +226,7 @@
         /**
          * Constructor for the active connection side of a connection.
          */
-        Sender(char* if_name, const ContactRef& contact);
+        Sender(const char* if_name, const ContactRef& contact);
 
         /**
          * Destructor.

---

From: Elwyn Davies <[email-masked]>
Date: Fri, 15 Feb 2013 12:09:20 +0100

Hi, Lana.

Thanks for this... I was actually thinking about how to get the Ethernet 
convergence layer working again when I did the last set of changes 
(3557) - I tried it and reelized it didn't work but no time to get it 
working.

Could you write two sets of 'a few words' to go with the patches:
1.  For the change commentary
2.  Manual notes for how to configure a couple of nodes to use the 
Ethernet convergence layer.

Regards,
Elwyn

On 15/02/13 08:10, Lana Black wrote:
> _______________________________________________
> dtn-users mailing list
> [email-masked]
> https://www.irtf.org/mailman/listinfo/dtn-users

---

From: Lana Black <[email-masked]>
Date: Sun, 17 Feb 2013 10:42:50 +0000

>Ethernet convergence layer.
 
Sure. The oasys patch fixes the ethernet address validation. The EthCL
patch reorganizes parameters handling code and makes the receiver
socket
non-blocking to allow graceful exit.

To configure a node one needs to do the following:

add local network interface:
>interface add string://eth0 eth

add link to the remote host:
>link add host0 <host hwaddr> <link type> eth interface=eth0
please note that the interface parameter is mandatory because there is
no way to figure what interface the host is connected to.

and finally add the route:
>route add dtn://host0.dtn/* host0

P.S. dtn-eth.patch contains a error which results in excessive cpu
load. Please see the patch attached to this message.

diff -r 660ce8ebcd3b servlib/conv_layers/EthConvergenceLayer.cc
--- a/servlib/conv_layers/EthConvergenceLayer.cc	Thu Jan 10 17:14:59 2013 +0000
+++ b/servlib/conv_layers/EthConvergenceLayer.cc	Fri Feb 15 15:33:32 2013 +0000
@@ -23,12 +23,15 @@
 
 #include <sys/poll.h>
 #include <stdlib.h>
+#include <fcntl.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
+#include <unistd.h>
+#include <errno.h>
 
@@ -59,6 +62,7 @@
 
@@ -100,6 +104,7 @@
     oasys::OptParser p;

@@ -169,7 +174,7 @@
 
@@ -186,6 +191,7 @@
 
@@ -199,7 +205,7 @@
     }

@@ -265,6 +271,7 @@
     ASSERT(params != NULL);

@@ -403,8 +410,9 @@
 
@@ -418,15 +426,16 @@
         }

             
         
         /**
@@ -434,14 +443,14 @@

 
         l.unlock();
     }
@@ -488,6 +497,7 @@
 {
     int sock;
     int cc;
+    int flags;
 
@@ -497,6 +507,16 @@
    
@@ -517,23 +537,30 @@

         if(should_stop())
             break;
+
+        // Sleep 1/10000th second here to prevent excessive cpu load
+        usleep(100);
     }
 }
 
@@ -546,11 +573,13 @@
 /**
+++ b/servlib/conv_layers/EthConvergenceLayer.h	Fri Feb 15 15:33:32 2013 +0000
 
     /**
 
+        ~Params() {
+            if(timer)
+                delete timer;
+        }
+
     
     /**
@@ -241,7 +226,7 @@
         /**
 
         /**
          * Destructor.

---

From: Lana Black <[email-masked]>
Date: Tue, 19 Feb 2013 19:50:04 +0000

On 19:05 Mon 18 Feb     , Elwyn Davies wrote:
> Thanks.
> 
> I'll add a bit into the manual to reflect this shortly.
> 
> Since you are working on Ethernet links (well, I assume you are!), and
> you now have them working again, could I ask you to check that the link
> name persistent storage mechanism is working correctly for Ethernet
> links.  I put in the code that should handle this correctly some time
> ago but with the Ethernet links not working at that time I wasn't able
> to check it out.  The purpose of this code is to ensure that the bundle
> forwarding logs can be stored and remain consistent after restarting the
> daemon because link names remain consistent across restarts: after a
> restart, links from previous runs are recorded in the 'Previously active
> links' list and new links are checked for consistency of name and basic
> parameters distinguishing between OPPORTUNISTIC and other types of links
> (see ContactManager::consistent_with_previous_usage in
> servlib/contacts/ContactManager.cc). 
> 
> Testing this should be fairly simple.  There are two cases to check -
> they can be cheked at the two ends of a single link as you are showing
> the setup for below if I remember how it should work:
> 
> 1. Manually configured link:
> 1.0 Check that the database is empty of link data by running the dtnd
> command 'link dump' and verifying that both the lists of 'Active links'
> and Previously active links' are empty  
> 1.1 Set up the link between two nodes as below; for testing purposes
> enter the 'link add' command manually rather than putting it in the
> configuration file and specify a type of 'ALWAYS ON' (indeed anything
> except 'OPPORTUNISTIC').
> 1.2 Once the link is active, on the node where you specified the link
> ('remote' node) use the command interface to perform a 'link dump'
> command.  All being well this should show one active link name 'host0'
> in an OPEN state and an empty set of 'Previously Acive Links' (assuming
> you started with a 'clean' tidied database as mentioned above).
> 1.3 Shutdown the daemon on the 'remote node' - check that the 'local'
> node thinks the link is now CLOSED using 'link dump'.
> 1.4 Restart the 'remote' node but don't (yet) enter the 'add link'
> command.  
> 1.5 Issue a 'link dump' command on the 'remote node' and check that
> there are no active links but there should be a record of the 'host0'
> link under 'Previously active links'.
> 1.6 Reissue the 'link add' command with the same parameters as before.
> The link should come up as expected.  Check all is well using the 'link
> dump' command which should show the link in both 'Active links' and
> 'Previously active links'.  
> 1.7 Repeat the exercise from step 1.3 but choose a different hardware
> address at step 1.6.  The 'link add' command should fail because the new
> 'host0' link has inconsistent parameters to the one in 'Previous active
> links'.
> 
> 2. Opportunistic links:
> Use the same configuration as in case 1, but restart the 'local node'
> which should have the link marked as OPPORTUNISTIC and examine the link
> state on the local node.  The link should be name 'link-0'.  After
> restarting the daemon, the link should restart automatically and have
> the same name as before (link-0).  
> 
> The error case for the OPPORTUNISTIC case can be tested by using a third
> node with a different hardware address.  The 'local node' should end up
> with 'link-1' in 'Active links' connected to the third node and 'link-0'
> still in 'Previously active links'.
> 
> Thanks in adavnce.
> Elwyn

Took some time to test. Both approaches work fine with the only
exception - ethernet links switch to UNAVAILABLE status instead of
CLOSED when I turn off the remote daemon. There is also no way to have
no links at the moment because of the ethernet beacons sent by EthCL.

BTW, I think that the beacons mechanism in EthCL is redundant and should
be merged into the discovery subsystem. Any opinion?