Subject: [dtn-users] Ethernet discovery

From: Lana Black <[email-masked]>
Date: Mon, 11 Mar 2013 14:58:04 +0000

Hi

I have reimplemented ethernet beacons as a discovery module. The
example of usage below:

discovery add eth0d eth interface=eth0
discovery announce eth0a eth0d eth interval=1

The interface argument is mandatory, otherwise all of the interfaces
present in the system will be used which is undesirable. Local EID is
also transmitted with every beacon, so that the receiving node might set
the correct route.

The other patch fixes a bug where discovery entry is deleted twice,
which results in abort() call.

With best regards.
>From 96c814928924b62d2fba9c9e1e74b4e71869e759 Mon Sep 17 00:00:00 2001
From: Lana Black <[email-masked]>
Date: Wed, 6 Mar 2013 03:31:02 +0000
Subject: [PATCH] Add ethernet discovery module

---
 servlib/Makefile                           |   2 +
 servlib/conv_layers/EthConvergenceLayer.cc | 252 +------------------------
 servlib/conv_layers/EthConvergenceLayer.h  |  47 +----
 servlib/discovery/Announce.cc              |   6 +-
 servlib/discovery/Discovery.cc             |   5 +
 servlib/discovery/DiscoveryTable.cc        |   1 -
 servlib/discovery/EthAnnounce.cc           |  88 +++++++++
 servlib/discovery/EthAnnounce.h            |  56 ++++++
 servlib/discovery/EthDiscovery.cc          | 288 +++++++++++++++++++++++++++++
 servlib/discovery/EthDiscovery.h           |  72 ++++++++
 10 files changed, 518 insertions(+), 299 deletions(-)
 create mode 100644 servlib/discovery/EthAnnounce.cc
 create mode 100644 servlib/discovery/EthAnnounce.h
 create mode 100644 servlib/discovery/EthDiscovery.cc
 create mode 100644 servlib/discovery/EthDiscovery.h

diff --git a/servlib/Makefile b/servlib/Makefile
index a9f9efa..e8c22a2 100644
--- a/servlib/Makefile
+++ b/servlib/Makefile
@@ -123,6 +123,8 @@ DISCOVERY_SRCS :=				\
 	discovery/BonjourDiscovery.cc		\
 	discovery/Discovery.cc			\
 	discovery/DiscoveryTable.cc		\
+	discovery/EthAnnounce.cc 		\
+	discovery/EthDiscovery.cc 		\
 	discovery/IPAnnounce.cc			\
 	discovery/IPDiscovery.cc
 
diff --git a/servlib/conv_layers/EthConvergenceLayer.cc b/servlib/conv_layers/EthConvergenceLayer.cc
index 3ec5dc1..fe781b6 100644
--- a/servlib/conv_layers/EthConvergenceLayer.cc
+++ b/servlib/conv_layers/EthConvergenceLayer.cc
@@ -61,7 +61,6 @@ struct EthConvergenceLayer::Params EthConvergenceLayer::defaults_;
 void
 EthConvergenceLayer::Params::serialize(oasys::SerializeAction *a)
 {
-	a->process("beacon_interval", &beacon_interval_);
 	a->process("interface", &if_name_);
 }
 
@@ -74,7 +73,7 @@ EthConvergenceLayer::Params::serialize(oasys::SerializeAction *a)
 EthConvergenceLayer::EthConvergenceLayer()
     : ConvergenceLayer("EthConvergenceLayer", "eth")
 {
-    defaults_.beacon_interval_          = 1;
+    defaults_.if_name_ = "";
 }
 
 //----------------------------------------------------------------------
@@ -103,7 +102,6 @@ EthConvergenceLayer::parse_params(Params* params,
 {
     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)) {
@@ -149,11 +147,6 @@ EthConvergenceLayer::interface_up(Interface* iface,
     receiver->start();
     iface->set_cl_info(receiver);
 
-    // remembers the interface beacon object
-    if_beacon_ = new Beacon(if_name, params.beacon_interval_);
-    if_beacon_->logpathf("/cl/eth");
-    if_beacon_->start();
-    
     return true;
 }
 
@@ -165,13 +158,6 @@ EthConvergenceLayer::interface_down(Interface* iface)
   //             interface and kill them.
   // NOTIMPLEMENTED;
 
-    // xxx/shawn needs to find a way to delete beacon;
-    if_beacon_->set_should_stop();
-    while (! if_beacon_->is_stopped()) {
-        oasys::Thread::yield();
-    }
-    delete if_beacon_;
-
     Receiver *receiver = (Receiver *)iface->cl_info();
     receiver->set_should_stop();
     //receiver->interrupt();
@@ -270,7 +256,6 @@ EthConvergenceLayer::dump_link(const LinkRef& link, oasys::StringBuffer* buf)
     Params* params = dynamic_cast<Params*>(link->cl_info());
     ASSERT(params != NULL);
 
-    buf->appendf("beacon_interval: %u\n", params->beacon_interval_);
     buf->appendf("interface: %s\n", params->if_name_.c_str());
 }
 
@@ -365,96 +350,7 @@ EthConvergenceLayer::Receiver::process_data(u_char* bp, size_t len)
         return;
     }
 
-    if(ethclhdr.type == ETHCL_BEACON) {
-        ContactManager* cm = BundleDaemon::instance()->contactmgr();
-
-        char bundles_string[60];
-        memset(bundles_string,0,60);
-        EthernetScheme::to_string(&ethhdr->ether_shost[0],
-                                  bundles_string);
-        char next_hop_string[50], *ptr;
-        memset(next_hop_string,0,50);
-        ptr = strrchr(bundles_string, '/');
-        strcpy(next_hop_string, ptr+1);
-        
-        ConvergenceLayer* cl = ConvergenceLayer::find_clayer("eth");
-        EndpointID remote_eid(bundles_string);
-
-        LinkRef link = cm->find_link_to(cl,
-                                        next_hop_string,
-                                        remote_eid,
-                                        Link::OPPORTUNISTIC);
-        
-        if(link == NULL) {
-            log_info("EthConvergenceLayer::Receiver::process_data: "
-                     "Discovered next_hop %s on interface %s.",
-                     next_hop_string, if_name_);
-            
-            // registers a new contact with the routing layer
-            link = cm->new_opportunistic_link(cl,
-                                              next_hop_string,
-                                              EndpointID(bundles_string));
-
-            if (link == NULL) {
-                log_debug("EthConvergenceLayer::Receiver::process_data: "
-                          "failed to create new opportunistic link");
-                return;
-            }
-
-            oasys::ScopeLock l(link->lock(), "EthConvergenceLayer::Receiver");
-            // It is doubtful that the link would be deleted already,
-            // but check just in case.
-            if (link->isdeleted()) {
-                log_warn("EthConvergenceLayer::Receiver::process_data: "
-                         "link %s deleted before fully initialized",
-                         link->name());
-                return;
-            }
-            ASSERT(link->cl_info() != NULL);
-            Params *params = dynamic_cast<Params*>(link->cl_info());
-            params->if_name_ = if_name_;
-            l.unlock();
-        }
-
-        ASSERT(link != NULL);
-        oasys::ScopeLock l(link->lock(), "EthConvergenceLayer::Receiver");
-
-        if (link->isdeleted()) {
-            log_warn("EthConvergenceLayer::Receiver::process_data: "
-                     "link %s already deleted", link->name());
-            return;
-        }
-
-        ASSERT(link->cl_info() != NULL);
-        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());
-            
-            // Kicking the link available here
-            BundleDaemon::post(
-                    new LinkStateChangeRequest(link, Link::OPEN, ContactUpEvent::DISCOVERY));
-        }
-        
-        /**
-         * If there already is a timer for this link, cancel it, which
-         * will delete it when it bubbles to the top of the timer
-         * queue. Then create a new 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);
-        
-        ((Params*)link->cl_info())->timer = timer;
-
-        l.unlock();
-    }
-    else if(ethclhdr.type == ETHCL_BUNDLE) {
+    if(ethclhdr.type == ETHCL_BUNDLE) {
         // infer the bundle length based on the packet length minus the
         // eth cl header
         bundle_len = len - sizeof(EthCLHeader) - sizeof(struct ether_header);
@@ -715,150 +611,6 @@ EthConvergenceLayer::Sender::send_bundle(const BundleRef& bundle)
     return ok;
 }
 
-//----------------------------------------------------------------------
-EthConvergenceLayer::Beacon::Beacon(const char* if_name,
-                                    unsigned int beacon_interval)
-  : Logger("EthConvergenceLayer::Beacon", "/dtn/cl/eth/beacon"),
-    Thread("EthConvergenceLayer::Beacon")
-{
-    Thread::flags_ |= INTERRUPTABLE;
-    memset(if_name_, 0, IFNAMSIZ);
-    strcpy(if_name_, if_name);
-    beacon_interval_ = beacon_interval;
-}
-
-//----------------------------------------------------------------------
-void EthConvergenceLayer::Beacon::run()
-{
-    // ethernet broadcast address
-    char bcast_mac_addr[6]={0xff,0xff,0xff,0xff,0xff,0xff};
-    
-    struct ether_header hdr;
-    struct sockaddr_ll iface;
-    EthCLHeader ethclhdr;
-    
-    int sock,cc;
-    struct iovec iov[2];
-    
-    memset(&hdr,0,sizeof(hdr));
-    memset(&ethclhdr,0,sizeof(ethclhdr));
-    memset(&iface,0,sizeof(iface));
-
-    ethclhdr.version = ETHCL_VERSION;
-    ethclhdr.type    = ETHCL_BEACON;
-    
-    hdr.ether_type=htons(ETHERTYPE_DTN);
-    
-    // iovec slot 0 holds the ethernet header
-    iov[0].iov_base = (char*)&hdr;
-    iov[0].iov_len = sizeof(struct ether_header);
-    
-    // use iovec slot 1 for the eth cl header
-    iov[1].iov_base = (char*)&ethclhdr;
-    iov[1].iov_len  = sizeof(EthCLHeader); 
-    
-    /* 
-       Get ourselves a raw socket, and configure it.
-    */
-    if((sock = socket(AF_PACKET,SOCK_RAW, htons(ETHERTYPE_DTN))) < 0) { 
-        perror("socket");
-        exit(1);
-    }
-
-    struct ifreq req;
-    strcpy(req.ifr_name, if_name_);
-    if(ioctl(sock, SIOCGIFINDEX, &req))
-    {
-        perror("ioctl");
-        exit(1);
-    }    
-
-    iface.sll_ifindex=req.ifr_ifindex;
-
-    if(ioctl(sock, SIOCGIFHWADDR, &req))
-    {
-        perror("ioctl");
-        exit(1);
-    } 
-    
-    memcpy(hdr.ether_dhost,bcast_mac_addr,6);
-    memcpy(hdr.ether_shost,req.ifr_hwaddr.sa_data,6);
-    
-    log_info("Interface %s has interface number %d.",if_name_,req.ifr_ifindex);
-    
-    iface.sll_family=AF_PACKET;
-    iface.sll_protocol=htons(ETHERTYPE_DTN);
-    
-    if (bind(sock, (struct sockaddr *) &iface, sizeof(iface)) == -1) {
-        perror("bind");
-        exit(1);
-    }
-
-    /*
-     * Send the beacon on the socket every beacon_interval_ second.
-     */
-    while(1) {
-        sleep(beacon_interval_);
-
-        if (should_stop())
-            break;
-
-        log_debug("Sent beacon out interface %s.\n",if_name_ );
-        
-        cc=IO::writevall(sock, iov, 2);
-        if(cc<0) {
-            perror("send beacon");
-            log_err("Send beacon failed!\n");
-        }
-    }
-}
-
-//----------------------------------------------------------------------
-EthConvergenceLayer::BeaconTimer::BeaconTimer(char * next_hop)
-    :  Logger("EthConvergenceLayer::BeaconTimer", "/dtn/cl/eth/beacontimer")
-{
-    next_hop_=(char*)malloc(strlen(next_hop)+1);
-    strcpy(next_hop_, next_hop);
-}
-
-//----------------------------------------------------------------------
-EthConvergenceLayer::BeaconTimer::~BeaconTimer()
-{
-    free(next_hop_);
-}
-
-//----------------------------------------------------------------------
-void
-EthConvergenceLayer::BeaconTimer::timeout(const struct timeval& now)
-{
-    ContactManager* cm = BundleDaemon::instance()->contactmgr();
-    ConvergenceLayer* cl = ConvergenceLayer::find_clayer("eth");
-    LinkRef link = cm->find_link_to(cl, next_hop_);
-
-    (void)now;
-
-    log_info("Neighbor %s timer expired.",next_hop_);
-
-    if(link == NULL) {
-      log_warn("No link for next_hop %s.",next_hop_);
-    }
-    else if(link->isopen()) {
-        BundleDaemon::post(
-            new LinkStateChangeRequest(link, Link::CLOSED,
-                                       ContactDownEvent::BROKEN));
-    }
-    else {
-        log_warn("next_hop %s unexpectedly not open",next_hop_);
-    }
-}
-
-//----------------------------------------------------------------------
-Timer *
-EthConvergenceLayer::BeaconTimer::copy()
-{
-    return new BeaconTimer(*this);
-}
-
 } // namespace dtn
 
 #endif // __linux
diff --git a/servlib/conv_layers/EthConvergenceLayer.h b/servlib/conv_layers/EthConvergenceLayer.h
index a4a8c07..9796d12 100644
--- a/servlib/conv_layers/EthConvergenceLayer.h
+++ b/servlib/conv_layers/EthConvergenceLayer.h
@@ -22,7 +22,7 @@
 
 #include <sys/types.h>
 #include <netinet/in.h>
-#include <linux/if.h>
+#include <net/if.h>
 
 #include <oasys/thread/Thread.h>
 #include <oasys/thread/Timer.h>
@@ -62,8 +62,6 @@ public:
     static const u_int8_t  ETHCL_BEACON = 0x01;
     static const u_int8_t  ETHCL_BUNDLE = 0x02;
 
-    static const u_int32_t ETHCL_BEACON_TIMEOUT_INTERVAL = 2500; // 2.5 seconds
-
     static const u_int16_t MAX_ETHER_PACKET = 1518;
 
     /**
@@ -154,16 +152,7 @@ public:
          */
         virtual void serialize(oasys::SerializeAction *a);
 
-        ~Params() {
-            if(timer) {
-                timer->cancel();
-            }
-        }
-
-        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
     };
     
     /**
@@ -265,36 +254,6 @@ public:
         u_char buf_[EthConvergenceLayer::MAX_BUNDLE_LEN];
     };
 
-    /** 
-     *  helper class (and thread) that periodically sends beacon messages
-     *  over the specified ethernet interface.
-     */
-    class Beacon : public oasys::Logger,
-                   public oasys::Thread
-    {
-    public:
-        Beacon(const char* if_name, unsigned int beacon_interval);
-
-        virtual ~Beacon() {};
-
-    private:
-        virtual void run();
-        char if_name_[IFNAMSIZ];
-        unsigned int beacon_interval_;
-    };
-
-    class BeaconTimer : public oasys::Logger, public oasys::Timer, public CLInfo {
-    public:
-        char * next_hop_;
-
-        BeaconTimer(char * next_hop);
-        ~BeaconTimer();
-
-        void timeout(const struct timeval& now);
-
-        Timer* copy();
-    };    
-
 protected:
     /**
      * Parses parameters during EthConvegenceLayer initialization
@@ -303,10 +262,6 @@ protected:
                       const char** invalidp);
 
     virtual CLInfo* new_link_params();
-
-
-private:
-    Beacon *if_beacon_;
 };

diff --git a/servlib/discovery/Announce.cc b/servlib/discovery/Announce.cc
index 0af62b8..8d6dc9a 100644
--- a/servlib/discovery/Announce.cc
+++ b/servlib/discovery/Announce.cc
@@ -19,6 +19,7 @@
 #endif
 
 #include "IPAnnounce.h"
+#include "EthAnnounce.h"
 #include "BluetoothAnnounce.h"
 #include "Announce.h"
 
@@ -46,8 +47,9 @@ Announce::create_announce(const std::string& name, ConvergenceLayer* cl,
         announce = new BluetoothAnnounce();
     }
 #endif
-    else
-    {
+    else if(strncmp(cl->name(), "eth", 3) == 0) {
+        announce = new EthAnnounce();
+    } else {
         //no announce implemented for CL type
         return NULL;
     }
diff --git a/servlib/discovery/Discovery.cc b/servlib/discovery/Discovery.cc
index 98eca27..6ed497e 100644
--- a/servlib/discovery/Discovery.cc
+++ b/servlib/discovery/Discovery.cc
@@ -28,6 +28,7 @@
 #include "IPNDDiscovery.h"
 #endif
 #include "IPDiscovery.h"
+#include "EthDiscovery.h"
 #include "contacts/ContactManager.h"
 #include "bundling/BundleDaemon.h"
 
@@ -71,6 +72,10 @@ Discovery::create_discovery(const std::string& name,
         disc = new BluetoothDiscovery(name);
     }
 #endif
+    else if (af == "eth")
+    {
+        disc = new EthDiscovery(name);
+    }
     else
     {
         // not a recognized address family
diff --git a/servlib/discovery/EthAnnounce.cc b/servlib/discovery/EthAnnounce.cc
new file mode 100644
index 0000000..426817d
--- /dev/null
+++ b/servlib/discovery/EthAnnounce.cc
@@ -0,0 +1,88 @@
+/*
+ *    Copyright 2013 Lana Black <[email-masked]>
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <dtn-config.h>
+#endif
+
+#include <net/ethernet.h>
+
+#include <oasys/util/OptParser.h>
+#include <bundling/BundleDaemon.h>
+#include "EthAnnounce.h"
+
+namespace dtn {
+
+EthAnnounce::EthAnnounce()
+{
+}
+
+size_t EthAnnounce::format_advertisement(u_char *bp, size_t len)
+{
+    const EndpointID& eid = BundleDaemon::instance()->local_eid();
+
+    if(len < sizeof(EthBeaconHeader) + eid.length()) {
+        log_warn("Not enough room to fill");
+        return 0;
+    }
+
+    EthBeaconHeader *beacon= reinterpret_cast<EthBeaconHeader*>(bp);
+
+    memset(bp, 0, len);
+
+    beacon->version = ETHCL_VERSION;
+    beacon->type = ETHCL_BEACON;
+    beacon->interval = interval_ / 100;     // 100ms units
+    beacon->eid_len = htons(eid.length());
+    memcpy(beacon->sender_eid, eid.c_str(), eid.length());
+
+    return sizeof(*beacon) + eid.length();
+}
+
+bool EthAnnounce::configure(const std::string &name, ConvergenceLayer *cl, int argc, const char *argv[])
+{
+    cl_ = cl;
+    name_ = name;
+    type_.assign(cl->name());
+
+    if(strncmp(cl_->name(), "eth", 3)) {
+        log_err("ethernet announce does not support cl type %s", cl_->name());
+        return false;
+    }
+
+    oasys::OptParser p;
+
+    p.addopt(new oasys::UIntOpt("interval", &interval_));
+
+    const char* invalid;
+    if(!p.parse(argc, argv, &invalid)) {
+        log_err("bad parameter %s", invalid);
+        return false;
+    }
+
+    if (interval_ == 0)
+    {
+        log_err("interval must be greater than 0");
+        return false;
+    }
+
+    // convert from seconds to ms
+    interval_ *= 1000;
+
+    return true;
+}
+
+} // dtn
diff --git a/servlib/discovery/EthAnnounce.h b/servlib/discovery/EthAnnounce.h
new file mode 100644
index 0000000..de27955
--- /dev/null
+++ b/servlib/discovery/EthAnnounce.h
@@ -0,0 +1,56 @@
+/*
+ *    Copyright 2013 Lana Black <[email-masked]>
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef _ETH_ANNOUNCE_H_
+#define _ETH_ANNOUNCE_H_
+
+#ifndef DTN_CONFIG_STATE
+#error "MUST INCLUDE dtn-config.h before including this file"
+#endif
+
+#include "Announce.h"
+#include "EthDiscovery.h"
+
+namespace dtn {
+
+class EthAnnounce : public Announce
+{
+public:
+
+    size_t format_advertisement(u_char*, size_t);
+
+    virtual ~EthAnnounce() {}
+
+protected:
+
+    friend class Announce;
+
+    // Some stuff from EthConvergenceLayer here
+    typedef EthDiscovery::EthBeaconHeader EthBeaconHeader;
+    static const u_int8_t  ETHCL_VERSION = 0x01;
+    static const u_int16_t ETHERTYPE_DTN = 0xd710;
+    static const u_int8_t  ETHCL_BEACON = 0x01;
+
+
+    EthAnnounce();
+
+    bool configure(const std::string& name, ConvergenceLayer* cl,
+                   int argc, const char *argv[]);
+};
+
+} // dtn
+
+#endif
diff --git a/servlib/discovery/EthDiscovery.cc b/servlib/discovery/EthDiscovery.cc
new file mode 100644
index 0000000..60a7a88
--- /dev/null
+++ b/servlib/discovery/EthDiscovery.cc
@@ -0,0 +1,288 @@
+/*
+ *    Copyright 2013 Lana Black <[email-masked]>
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <dtn-config.h>
+#endif
+
+#include <cstring>
+#include <climits>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <netpacket/packet.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <poll.h>
+
+#include <oasys/io/IO.h>
+#include <oasys/util/OptParser.h>
+#include <bundling/BundleDaemon.h>
+#include <contacts/ContactManager.h>
+#include <contacts/InterfaceTable.h>
+#include <conv_layers/EthConvergenceLayer.h>
+
+#include "EthDiscovery.h"
+#include "EthAnnounce.h"
+
+namespace dtn {
+
+EthDiscovery::EthDiscovery(const std::string& name)
+    : Discovery(name, "eth"),
+      oasys::Thread("EthDiscovery", DELETE_ON_EXIT)
+{
+    shutdown_ = false;
+}
+
+bool EthDiscovery::configure(int argc, const char *argv[])
+{
+    if(oasys::Thread::started()) {
+        log_warn("reconfiguration of EthDiscovery no supported");
+        return false;
+    }
+
+    oasys::OptParser p;
+
+    p.addopt(new oasys::StringOpt("interface", &iface_));
+
+    const char *invalid;
+    if(!p.parse(argc, argv, &invalid)) {
+        log_err("bad option for Ethernet discovery: %s", invalid);
+        return false;
+    }
+
+    // Check the interface
+    if(!iface_.empty()) {
+        oasys::StringBuffer iface_list;
+
+        InterfaceTable::instance()->list(&iface_list);
+        if(strstr(iface_list.c_str(), iface_.c_str()) == NULL) {
+            // No interface found
+            log_err("bad interface name");
+            return false;
+        }
+    } else {
+        log_err("Ethernet discovery requires interface name");
+        return false;
+    }
+
+
+    log_debug("starting thread");
+    start();
+
+    return true;
+}
+
+void EthDiscovery::run()
+{
+    struct sockaddr_ll ifaddr;
+    struct ifreq req;
+    struct pollfd fds;
+    struct iovec iov[2];
+    struct ether_header ethhdr;
+    struct ::ether_addr src_addr;
+    u_char buf[1500];   // Buffer to store beacons
+    int cc;
+
+    log_debug("discovery thread running");
+
+    sock_ = socket(PF_PACKET, SOCK_RAW, htons(ETHERTYPE_DTN_BEACON));
+    if(sock_ < 0) {
+        log_err("socket() failed: %s", strerror(errno));
+        return;
+    }
+
+    memset(&req, 0, sizeof(req));
+    strcpy(req.ifr_name, iface_.c_str());
+    ioctl(sock_, SIOCGIFHWADDR, &req);
+    memcpy(&src_addr.ether_addr_octet, req.ifr_hwaddr.sa_data, 6);
+
+    memset(&req, 0, sizeof(req));
+    strcpy(req.ifr_name, iface_.c_str());
+    ioctl(sock_, SIOCGIFINDEX, &req);
+
+    memset(&ifaddr, 0, sizeof(ifaddr));
+    ifaddr.sll_family = AF_PACKET;
+    ifaddr.sll_protocol = htons(ETHERTYPE_DTN_BEACON);
+    ifaddr.sll_ifindex = req.ifr_ifindex;
+
+    if(bind(sock_, (struct sockaddr*) &ifaddr, sizeof(ifaddr)) == -1) {
+        log_err("bind() failed: %s", strerror(errno));
+        return;
+    }
+
+    fds.fd = sock_;
+    fds.events = POLLIN;
+
+    memset(iov, 0, sizeof(iov));
+
+    iov[0].iov_base = &ethhdr;
+    iov[0].iov_len = sizeof(ethhdr);
+
+    // The following code is an adapted copy of the same code from IPDiscovery.cc
+
+    while(true) {
+        if(shutdown_) break;
+
+        memset(&ethhdr.ether_dhost, 0xff, sizeof(ethhdr.ether_dhost));
+        memcpy(&ethhdr.ether_shost, src_addr.ether_addr_octet, 6);
+        ethhdr.ether_type = htons(ETHERTYPE_DTN_BEACON);
+
+        iov[0].iov_base = &ethhdr;
+        iov[0].iov_len = sizeof(ethhdr);
+
+        iov[1].iov_base = buf;
+
+        u_int timeout = UINT_MAX;
+        for(iterator iter = list_.begin(); iter != list_.end(); ++iter) {
+            EthAnnounce* announce = dynamic_cast<EthAnnounce*>(*iter);
+
+            u_int remaining = announce->interval_remaining();
+
+            if(remaining) {
+                log_debug("announce not ready: %u ms remaining", remaining);
+                if(remaining < timeout)
+                    timeout = remaining;
+            } else {
+                int len;
+
+                len = announce->format_advertisement(buf, sizeof(buf));
+                iov[1].iov_len = len;
+
+                cc = oasys::IO::writevall(sock_, iov, 2);
+                if(cc != sizeof(ethhdr)+len) {
+                    log_err("send failed: %s", strerror(errno));
+                    return;
+                }
+
+                timeout = announce->interval();
+            }
+        }
+
+        // We cannot obviously wait UINT_MAX milliseconds
+        cc = poll(&fds, 1, timeout == UINT_MAX ? 1 : timeout);
+        if(cc == 0) {
+            continue;
+        } else if(cc < 0) {
+            if(errno == EINTR) {
+                continue;
+            } else {
+                log_err("poll failed: %s", strerror(errno));
+                return;
+            }
+        }
+
+        iov[1].iov_len = sizeof(buf);
+
+        cc = oasys::IO::readv(sock_, iov, 2);
+        if(cc < 0) {
+            log_err("readvall() failed");
+            return;
+        } else if(cc < sizeof(ethhdr)) {
+            log_warn("corrupt packet received, ignore");
+            continue;
+        }
+
+        std::string nexthop;
+        EndpointID remote_eid;
+
+        if(!parse_advertisement(buf, cc - sizeof(ethhdr), ethhdr.ether_shost, nexthop, remote_eid)) {
+            log_warn("unable to parse beacon");
+            continue;
+        }
+
+        if(remote_eid.equals(BundleDaemon::instance()->local_eid())) {
+            log_debug("ignoring our own beacon");
+            continue;
+        }
+
+        handle_neighbor_discovered("eth", nexthop, remote_eid);
+    }
+
+}
+
+bool EthDiscovery::parse_advertisement(u_char *bp, size_t len, u_char *remote_addr, std::string& nexthop, EndpointID& remote_eid)
+{
+    if(len < sizeof(struct EthBeaconHeader))
+        return false;
+
+    struct EthBeaconHeader *beacon = reinterpret_cast<struct EthBeaconHeader*>(bp);
+
+    if(beacon->version != ETHCL_VERSION)
+        return false;
+    if(beacon->type != ETHCL_BEACON) {
+        log_warn("Received non beacon packet with type %u", beacon->type);
+        return false;
+    }
+
+    size_t eid_len = ntohs(beacon->eid_len);
+    std::string eid(beacon->sender_eid, eid_len);
+
+    oasys::StringBuffer buf("%x:%x:%x:%x:%x:%x", remote_addr[0], remote_addr[1], remote_addr[2],
+                                                 remote_addr[3], remote_addr[4], remote_addr[5]);
+    nexthop.assign(buf.c_str());
+
+    return remote_eid.assign(eid);
+}
+
+// Have to reimplement this method to fill the interface name
+// in the link parameters structure
+void
+EthDiscovery::handle_neighbor_discovered(const std::string& cl_type,
+                                         const std::string& cl_addr,
+                                         const EndpointID& remote_eid)
+{
+    ContactManager* cm = BundleDaemon::instance()->contactmgr();
+
+    ConvergenceLayer* cl = ConvergenceLayer::find_clayer(cl_type.c_str());
+    if (cl == NULL) {
+        log_err("handle_neighbor_discovered: "
+                "unknown convergence layer type '%s'", cl_type.c_str());
+        return;
+    }
+
+    // Look for match on convergence layer and remote EID
+    LinkRef link("Announce::handle_neighbor_discovered");
+    link = cm->find_link_to(cl, "", remote_eid);
+
+    if (link == NULL) {
+        link = cm->new_opportunistic_link(cl, cl_addr, remote_eid);
+        if (link == NULL) {
+            log_debug("Discovery::handle_neighbor_discovered: "
+                      "failed to create opportunistic link");
+            return;
+        }
+    }
+
+    ASSERT(link != NULL);
+    ASSERT(link->cl_info() != NULL)
+
+    EthConvergenceLayer::Params* link_params = dynamic_cast<EthConvergenceLayer::Params*>(link->cl_info());
+    link_params->if_name_ = iface_;
+
+    if (!link->isavailable())
+    {
+        // request to set link available
+        BundleDaemon::post(
+            new LinkStateChangeRequest(link, Link::AVAILABLE,
+                                       ContactEvent::DISCOVERY)
+            );
+    }
+}
+
+
+} //dtn
diff --git a/servlib/discovery/EthDiscovery.h b/servlib/discovery/EthDiscovery.h
new file mode 100644
index 0000000..094372a
--- /dev/null
+++ b/servlib/discovery/EthDiscovery.h
@@ -0,0 +1,72 @@
+/*
+ *    Copyright 2013 Lana Black <[email-masked]>
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef _ETH_DISCOVERY_H_
+#define _ETH_DISCOVERY_H_
+
+#include <oasys/thread/Thread.h>
+
+#include "Discovery.h"
+
+namespace dtn {
+
+class EthDiscovery : public Discovery,
+                     public oasys::Thread
+{
+public:
+
+    void shutdown() { shutdown_ = true; }
+
+    virtual ~EthDiscovery() {}
+
+protected:
+    friend class Discovery;
+    friend class EthAnnounce;
+
+    /**
+     * Ethernet beacon header structure.
+     * Version and type fields are for compatibility with EthCLHeader
+     */
+    struct EthBeaconHeader {
+        u_int8_t version;               ///< ethcl protocol version
+        u_int8_t type;                  ///<
+        u_int8_t interval;              ///< interval in 100ms units
+        u_int16_t eid_len;              ///<
+        char sender_eid[0];
+    } __attribute__((packed));
+
+
+    static const u_int8_t  ETHCL_VERSION = 0x01;
+    static const u_int16_t ETHERTYPE_DTN_BEACON = 0xd711;
+    static const u_int8_t  ETHCL_BEACON = 0x01;
+
+    EthDiscovery(const std::string& name);
+    bool configure(int argc, const char* argv[]);
+    void run();
+    bool parse_advertisement(u_char* bp, size_t len, u_char* remote_addr,
+                             std::string& nexthop, EndpointID& remote_eid);
+    void handle_neighbor_discovered(const std::string& cl_type,
+                                    const std::string& cl_addr,
+                                    const EndpointID& remote_eid);
+
+    volatile bool shutdown_;
+    std::string iface_;
+    int sock_;
+};
+
+} // dtn
+
+#endif
-- 
1.8.1.5

>From 8f7e213e220d73635670867da86c9586e844ef9e Mon Sep 17 00:00:00 2001
From: Lana Black <[email-masked]>
Date: Wed, 6 Mar 2013 05:38:53 +0000
Subject: [PATCH] DiscoveryTable: do not delete discovery agent explicitely,
 because it is already deleted by Discovery::shutdown()

---
 servlib/discovery/DiscoveryTable.cc | 1 -
 1 file changed, 1 deletion(-)

diff --git a/servlib/discovery/DiscoveryTable.cc b/servlib/discovery/DiscoveryTable.cc
index dcb85c7..818659d 100644
--- a/servlib/discovery/DiscoveryTable.cc
+++ b/servlib/discovery/DiscoveryTable.cc
@@ -120,7 +120,6 @@ DiscoveryTable::del(const std::string& name)
     disc = *iter;
     dlist_.erase(iter);
     disc->shutdown(); 
-    delete disc;
 
     return true;
 }
-- 
1.8.1.5