Browse Source

Merge branch 'trac3315'

Tomek Mrugalski 11 years ago
parent
commit
7686ac9a3e

+ 5 - 4
src/bin/dhcp4/ctrl_dhcp4_srv.cc

@@ -226,7 +226,7 @@ void ControlledDhcpv4Srv::establishSession() {
     int ctrl_socket = cc_session_->getSocketDesc();
     LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_CCSESSION_STARTED)
               .arg(ctrl_socket);
-    IfaceMgr::instance().set_session_socket(ctrl_socket, sessionReader);
+    IfaceMgr::instance().addExternalSocket(ctrl_socket, sessionReader);
 }
 
 void ControlledDhcpv4Srv::disconnectSession() {
@@ -235,13 +235,14 @@ void ControlledDhcpv4Srv::disconnectSession() {
         config_session_ = NULL;
     }
     if (cc_session_) {
+
+        int ctrl_socket = cc_session_->getSocketDesc();
         cc_session_->disconnect();
+
+        IfaceMgr::instance().deleteExternalSocket(ctrl_socket);
         delete cc_session_;
         cc_session_ = NULL;
     }
-
-    // deregister session socket
-    IfaceMgr::instance().set_session_socket(IfaceMgr::INVALID_SOCKET, NULL);
 }
 
 ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t port /*= DHCP4_SERVER_PORT*/)

+ 7 - 4
src/bin/dhcp6/ctrl_dhcp6_srv.cc

@@ -227,7 +227,7 @@ void ControlledDhcpv6Srv::establishSession() {
     int ctrl_socket = cc_session_->getSocketDesc();
     LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_CCSESSION_STARTED)
               .arg(ctrl_socket);
-    IfaceMgr::instance().set_session_socket(ctrl_socket, sessionReader);
+    IfaceMgr::instance().addExternalSocket(ctrl_socket, sessionReader);
 }
 
 void ControlledDhcpv6Srv::disconnectSession() {
@@ -236,13 +236,16 @@ void ControlledDhcpv6Srv::disconnectSession() {
         config_session_ = NULL;
     }
     if (cc_session_) {
+
+        int ctrl_socket = cc_session_->getSocketDesc();
         cc_session_->disconnect();
+
+        // deregister session socket
+        IfaceMgr::instance().deleteExternalSocket(ctrl_socket);
+
         delete cc_session_;
         cc_session_ = NULL;
     }
-
-    // deregister session socket
-    IfaceMgr::instance().set_session_socket(IfaceMgr::INVALID_SOCKET, NULL);
 }
 
 ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t port)

+ 79 - 39
src/lib/dhcp/iface_mgr.cc

@@ -170,7 +170,6 @@ bool Iface::delSocket(const uint16_t sockfd) {
 IfaceMgr::IfaceMgr()
     :control_buf_len_(CMSG_SPACE(sizeof(struct in6_pktinfo))),
      control_buf_(new char[control_buf_len_]),
-     session_socket_(INVALID_SOCKET), session_callback_(NULL),
      packet_filter_(new PktFilterInet()),
      packet_filter6_(new PktFilterInet6())
 {
@@ -228,6 +227,37 @@ IfaceMgr::isDirectResponseSupported() const {
 }
 
 void
+IfaceMgr::addExternalSocket(int socketfd, SocketCallback callback) {
+    for (SocketCallbackInfoContainer::iterator s = callbacks_.begin();
+         s != callbacks_.end(); ++s) {
+
+        // There's such a socket description there already.
+        // Update the callback and we're done
+        if (s->socket_ == socketfd) {
+            s->callback_ = callback;
+            return;
+        }
+    }
+
+    // Add a new entry to the callbacks vector
+    SocketCallbackInfo x;
+    x.socket_ = socketfd;
+    x.callback_ = callback;
+    callbacks_.push_back(x);
+}
+
+void
+IfaceMgr::deleteExternalSocket(int socketfd) {
+    for (SocketCallbackInfoContainer::iterator s = callbacks_.begin();
+         s != callbacks_.end(); ++s) {
+        if (s->socket_ == socketfd) {
+            callbacks_.erase(s);
+            return;
+        }
+    }
+}
+
+void
 IfaceMgr::setPacketFilter(const PktFilterPtr& packet_filter) {
     // Do not allow NULL pointer.
     if (!packet_filter) {
@@ -789,7 +819,6 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {
     IfaceCollection::const_iterator iface;
     fd_set sockets;
     int maxfd = 0;
-    stringstream names;
 
     FD_ZERO(&sockets);
 
@@ -804,7 +833,6 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {
 
             // Only deal with IPv4 addresses.
             if (s->addr_.isV4()) {
-                names << s->sockfd_ << "(" << iface->getName() << ") ";
 
                 // Add this socket to listening set
                 FD_SET(s->sockfd_, &sockets);
@@ -815,13 +843,15 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {
         }
     }
 
-    // if there is session socket registered...
-    if (session_socket_ != INVALID_SOCKET) {
-        // at it to the set as well
-        FD_SET(session_socket_, &sockets);
-        if (maxfd < session_socket_)
-            maxfd = session_socket_;
-        names << session_socket_ << "(session)";
+    // if there are any callbacks for external sockets registered...
+    if (!callbacks_.empty()) {
+        for (SocketCallbackInfoContainer::const_iterator s = callbacks_.begin();
+             s != callbacks_.end(); ++s) {
+            FD_SET(s->socket_, &sockets);
+            if (maxfd < s->socket_) {
+                maxfd = s->socket_;
+            }
+        }
     }
 
     struct timeval select_timeout;
@@ -838,18 +868,22 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {
     }
 
     // Let's find out which socket has the data
-    if ((session_socket_ != INVALID_SOCKET) && (FD_ISSET(session_socket_, &sockets))) {
-        // something received over session socket
-        if (session_callback_) {
-            // in theory we could call io_service.run_one() here, instead of
-            // implementing callback mechanism, but that would introduce
-            // asiolink dependency to libdhcp++ and that is something we want
-            // to avoid (see CPE market and out long term plans for minimalistic
-            // implementations.
-            session_callback_();
+    for (SocketCallbackInfoContainer::iterator s = callbacks_.begin();
+         s != callbacks_.end(); ++s) {
+        if (!FD_ISSET(s->socket_, &sockets)) {
+            continue;
         }
 
-        return (Pkt4Ptr()); // NULL
+        // something received over external socket
+
+        // Calling the external socket's callback provides its service
+        // layer access without integrating any specific features
+        // in IfaceMgr
+        if (s->callback_) {
+            s->callback_();
+        }
+
+        return (Pkt4Ptr());
     }
 
     // Let's find out which interface/socket has the data
@@ -886,7 +920,6 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
     const SocketInfo* candidate = 0;
     fd_set sockets;
     int maxfd = 0;
-    stringstream names;
 
     FD_ZERO(&sockets);
 
@@ -901,7 +934,6 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
 
             // Only deal with IPv6 addresses.
             if (s->addr_.isV6()) {
-                names << s->sockfd_ << "(" << iface->getName() << ") ";
 
                 // Add this socket to listening set
                 FD_SET(s->sockfd_, &sockets);
@@ -912,13 +944,17 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
         }
     }
 
-    // if there is session socket registered...
-    if (session_socket_ != INVALID_SOCKET) {
-        // at it to the set as well
-        FD_SET(session_socket_, &sockets);
-        if (maxfd < session_socket_)
-            maxfd = session_socket_;
-        names << session_socket_ << "(session)";
+    // if there are any callbacks for external sockets registered...
+    if (!callbacks_.empty()) {
+        for (SocketCallbackInfoContainer::const_iterator s = callbacks_.begin();
+             s != callbacks_.end(); ++s) {
+
+            // Add it to the set as well
+            FD_SET(s->socket_, &sockets);
+            if (maxfd < s->socket_) {
+                maxfd = s->socket_;
+            }
+        }
     }
 
     struct timeval select_timeout;
@@ -935,18 +971,22 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
     }
 
     // Let's find out which socket has the data
-    if ((session_socket_ != INVALID_SOCKET) && (FD_ISSET(session_socket_, &sockets))) {
-        // something received over session socket
-        if (session_callback_) {
-            // in theory we could call io_service.run_one() here, instead of
-            // implementing callback mechanism, but that would introduce
-            // asiolink dependency to libdhcp++ and that is something we want
-            // to avoid (see CPE market and out long term plans for minimalistic
-            // implementations.
-            session_callback_();
+    for (SocketCallbackInfoContainer::iterator s = callbacks_.begin();
+         s != callbacks_.end(); ++s) {
+        if (!FD_ISSET(s->socket_, &sockets)) {
+            continue;
         }
 
-        return (Pkt6Ptr()); // NULL
+        // something received over external socket
+
+        // Calling the external socket's callback provides its service
+        // layer access without integrating any specific features
+        // in IfaceMgr
+        if (s->callback_) {
+            s->callback_();
+        }
+
+        return (Pkt6Ptr());
     }
 
     // Let's find out which interface/socket has the data

+ 25 - 18
src/lib/dhcp/iface_mgr.h

@@ -394,8 +394,20 @@ boost::function<void(const std::string& errmsg)> IfaceMgrErrorMsgCallback;
 ///
 class IfaceMgr : public boost::noncopyable {
 public:
-    /// Defines callback used when commands are received over control session.
-    typedef void (*SessionCallback) (void);
+    /// Defines callback used when data is received over external sockets.
+    typedef boost::function<void ()> SocketCallback;
+
+    /// Keeps callback information for external sockets.
+    struct SocketCallbackInfo {
+        /// Socket descriptor of the external socket.
+        int socket_;
+
+        /// A callback that will be called when data arrives over socket_.
+        SocketCallback callback_;
+    };
+
+    /// Defines storage container for callbacks for external sockets
+    typedef std::list<SocketCallbackInfo> SocketCallbackInfoContainer;
 
     /// @brief Packet reception buffer size
     ///
@@ -785,17 +797,18 @@ public:
     /// @return number of detected interfaces
     uint16_t countIfaces() { return ifaces_.size(); }
 
-    /// @brief Sets session socket and a callback
+    /// @brief Adds external socket and a callback
     ///
-    /// Specifies session socket and a callback that will be called
+    /// Specifies external socket and a callback that will be called
     /// when data will be received over that socket.
     ///
     /// @param socketfd socket descriptor
     /// @param callback callback function
-    void set_session_socket(int socketfd, SessionCallback callback) {
-        session_socket_ = socketfd;
-        session_callback_ = callback;
-    }
+    void addExternalSocket(int socketfd, SocketCallback callback);
+
+    /// @brief Deletes external socket
+
+    void deleteExternalSocket(int socketfd);
 
     /// @brief Set packet filter object to handle sending and receiving DHCPv4
     /// messages.
@@ -881,9 +894,6 @@ public:
     /// @return true if there is a socket bound to the specified address.
     bool hasOpenSocket(const isc::asiolink::IOAddress& addr) const;
 
-    /// A value of socket descriptor representing "not specified" state.
-    static const int INVALID_SOCKET = -1;
-
     // don't use private, we need derived classes in tests
 protected:
 
@@ -977,13 +987,7 @@ protected:
     /// @return true if successful, false otherwise
     bool os_receive4(struct msghdr& m, Pkt4Ptr& pkt);
 
-    /// Socket descriptor of the session socket.
-    int session_socket_;
-
-    /// A callback that will be called when data arrives over session_socket_.
-    SessionCallback session_callback_;
 private:
-
     /// @brief Identifies local network address to be used to
     /// connect to remote address.
     ///
@@ -1022,7 +1026,7 @@ private:
     /// error occurs during opening a socket, or NULL if exception should
     /// be thrown upon error.
     bool openMulticastSocket(Iface& iface,
-                             const isc::asiolink::IOAddress addr,
+                             const isc::asiolink::IOAddress& addr,
                              const uint16_t port,
                              IfaceMgrErrorMsgCallback error_handler = NULL);
 
@@ -1041,6 +1045,9 @@ private:
     /// messages. It is possible to supply a custom object using
     /// setPacketFilter method.
     PktFilter6Ptr packet_filter6_;
+
+    /// @brief Contains list of callbacks for external sockets
+    SocketCallbackInfoContainer callbacks_;
 };
 
 }; // namespace isc::dhcp

+ 1 - 1
src/lib/dhcp/iface_mgr_bsd.cc

@@ -152,7 +152,7 @@ IfaceMgr::setMatchingPacketFilter(const bool /* direct_response_desired */) {
 
 bool
 IfaceMgr::openMulticastSocket(Iface& iface,
-                              const isc::asiolink::IOAddress addr,
+                              const isc::asiolink::IOAddress& addr,
                               const uint16_t port,
                               IfaceMgrErrorMsgCallback error_handler) {
     try {

+ 1 - 1
src/lib/dhcp/iface_mgr_linux.cc

@@ -536,7 +536,7 @@ bool IfaceMgr::os_receive4(struct msghdr&, Pkt4Ptr&) {
 
 bool
 IfaceMgr::openMulticastSocket(Iface& iface,
-                              const isc::asiolink::IOAddress addr,
+                              const isc::asiolink::IOAddress& addr,
                               const uint16_t port,
                               IfaceMgrErrorMsgCallback error_handler) {
     // This variable will hold a descriptor of the socket bound to

+ 1 - 1
src/lib/dhcp/iface_mgr_sun.cc

@@ -156,7 +156,7 @@ IfaceMgr::setMatchingPacketFilter(const bool /* direct_response_desired */) {
 
 bool
 IfaceMgr::openMulticastSocket(Iface& iface,
-                              const isc::asiolink::IOAddress addr,
+                              const isc::asiolink::IOAddress& addr,
                               const uint16_t port,
                               IfaceMgrErrorMsgCallback error_handler) {
     try {

+ 308 - 5
src/lib/dhcp/tests/iface_mgr_unittest.cc

@@ -2590,15 +2590,19 @@ TEST_F(IfaceMgrTest, detectIfaces) {
 }
 
 volatile bool callback_ok;
+volatile bool callback2_ok;
 
 void my_callback(void) {
-    cout << "Callback triggered." << endl;
     callback_ok = true;
 }
 
-TEST_F(IfaceMgrTest, controlSession) {
-    // Tests if extra control socket and its callback can be passed and
-    // it is supported properly by receive4() method.
+void my_callback2(void) {
+    callback2_ok = true;
+}
+
+// Tests if a single external socket and its callback can be passed and
+// it is supported properly by receive4() method.
+TEST_F(IfaceMgrTest, SingleExternalSocket4) {
 
     callback_ok = false;
 
@@ -2607,7 +2611,7 @@ TEST_F(IfaceMgrTest, controlSession) {
     // Create pipe and register it as extra socket
     int pipefd[2];
     EXPECT_TRUE(pipe(pipefd) == 0);
-    EXPECT_NO_THROW(ifacemgr->set_session_socket(pipefd[0], my_callback));
+    EXPECT_NO_THROW(ifacemgr->addExternalSocket(pipefd[0], my_callback));
 
     Pkt4Ptr pkt4;
     ASSERT_NO_THROW(pkt4 = ifacemgr->receive4(1));
@@ -2635,6 +2639,305 @@ TEST_F(IfaceMgrTest, controlSession) {
     close(pipefd[0]);
 }
 
+// Tests if multiple external sockets and their callbacks can be passed and
+// it is supported properly by receive4() method.
+TEST_F(IfaceMgrTest, MiltipleExternalSockets4) {
+
+    callback_ok = false;
+    callback2_ok = false;
+
+    scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
+
+    // Create first pipe and register it as extra socket
+    int pipefd[2];
+    EXPECT_TRUE(pipe(pipefd) == 0);
+    EXPECT_NO_THROW(ifacemgr->addExternalSocket(pipefd[0], my_callback));
+
+    // Let's create a second pipe and register it as well
+    int secondpipe[2];
+    EXPECT_TRUE(pipe(secondpipe) == 0);
+    EXPECT_NO_THROW(ifacemgr->addExternalSocket(secondpipe[0], my_callback2));
+
+    Pkt4Ptr pkt4;
+    ASSERT_NO_THROW(pkt4 = ifacemgr->receive4(1));
+
+    // Our callbacks should not be called this time (there was no data)
+    EXPECT_FALSE(callback_ok);
+    EXPECT_FALSE(callback2_ok);
+
+    // IfaceMgr should not process control socket data as incoming packets
+    EXPECT_FALSE(pkt4);
+
+    // Now, send some data over the first pipe (38 bytes)
+    EXPECT_EQ(38, write(pipefd[1], "Hi, this is a message sent over a pipe", 38));
+
+    // ... and repeat
+    ASSERT_NO_THROW(pkt4 = ifacemgr->receive4(1));
+
+    // IfaceMgr should not process control socket data as incoming packets
+    EXPECT_FALSE(pkt4);
+
+    // There was some data, so this time callback should be called
+    EXPECT_TRUE(callback_ok);
+    EXPECT_FALSE(callback2_ok);
+
+    // Read the data sent, because our test callbacks are too dumb to actually
+    // do it. We don't care about the content read, because we're testing
+    // the callbacks, not pipes.
+    char buf[80];
+    read(pipefd[0], buf, 80);
+
+    // Clear the status...
+    callback_ok = false;
+    callback2_ok = false;
+
+    // And try again, using the second pipe
+    EXPECT_EQ(38, write(secondpipe[1], "Hi, this is a message sent over a pipe", 38));
+
+    // ... and repeat
+    ASSERT_NO_THROW(pkt4 = ifacemgr->receive4(1));
+
+    // IfaceMgr should not process control socket data as incoming packets
+    EXPECT_FALSE(pkt4);
+
+    // There was some data, so this time callback should be called
+    EXPECT_FALSE(callback_ok);
+    EXPECT_TRUE(callback2_ok);
+
+    // close both pipe ends
+    close(pipefd[1]);
+    close(pipefd[0]);
+
+    close(secondpipe[1]);
+    close(secondpipe[0]);
+}
+
+// Tests if existing external socket can be deleted and that such deletion does
+// not affect any other existing sockets. Tests uses receive4()
+TEST_F(IfaceMgrTest, DeleteExternalSockets4) {
+
+    callback_ok = false;
+    callback2_ok = false;
+
+    scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
+
+    // Create first pipe and register it as extra socket
+    int pipefd[2];
+    EXPECT_TRUE(pipe(pipefd) == 0);
+    EXPECT_NO_THROW(ifacemgr->addExternalSocket(pipefd[0], my_callback));
+
+    // Let's create a second pipe and register it as well
+    int secondpipe[2];
+    EXPECT_TRUE(pipe(secondpipe) == 0);
+    EXPECT_NO_THROW(ifacemgr->addExternalSocket(secondpipe[0], my_callback2));
+
+    // Now delete the first session socket
+    EXPECT_NO_THROW(ifacemgr->deleteExternalSocket(pipefd[0]));
+
+    // Now check whether the second callback is still functional
+    EXPECT_EQ(38, write(secondpipe[1], "Hi, this is a message sent over a pipe", 38));
+
+    // ... and repeat
+    Pkt4Ptr pkt4;
+    ASSERT_NO_THROW(pkt4 = ifacemgr->receive4(1));
+
+    // IfaceMgr should not process control socket data as incoming packets
+    EXPECT_FALSE(pkt4);
+
+    // There was some data, so this time callback should be called
+    EXPECT_FALSE(callback_ok);
+    EXPECT_TRUE(callback2_ok);
+
+    // Let's reset the status
+    callback_ok = false;
+    callback2_ok = false;
+
+    // Now let's send something over the first callback that was unregistered.
+    // We should NOT receive any callback.
+    EXPECT_EQ(38, write(pipefd[1], "Hi, this is a message sent over a pipe", 38));
+
+    // Now check that the first callback is NOT called.
+    ASSERT_NO_THROW(pkt4 = ifacemgr->receive4(1));
+    EXPECT_FALSE(callback_ok);
+
+    // close both pipe ends
+    close(pipefd[1]);
+    close(pipefd[0]);
+
+    close(secondpipe[1]);
+    close(secondpipe[0]);
+}
+
+
+// Tests if a single external socket and its callback can be passed and
+// it is supported properly by receive6() method.
+TEST_F(IfaceMgrTest, SingleExternalSocket6) {
+
+    callback_ok = false;
+
+    scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
+
+    // Create pipe and register it as extra socket
+    int pipefd[2];
+    EXPECT_TRUE(pipe(pipefd) == 0);
+    EXPECT_NO_THROW(ifacemgr->addExternalSocket(pipefd[0], my_callback));
+
+    Pkt6Ptr pkt6;
+    ASSERT_NO_THROW(pkt6 = ifacemgr->receive6(1));
+
+    // Our callback should not be called this time (there was no data)
+    EXPECT_FALSE(callback_ok);
+
+    // IfaceMgr should not process control socket data as incoming packets
+    EXPECT_FALSE(pkt6);
+
+    // Now, send some data over pipe (38 bytes)
+    EXPECT_EQ(38, write(pipefd[1], "Hi, this is a message sent over a pipe", 38));
+
+    // ... and repeat
+    ASSERT_NO_THROW(pkt6 = ifacemgr->receive6(1));
+
+    // IfaceMgr should not process control socket data as incoming packets
+    EXPECT_FALSE(pkt6);
+
+    // There was some data, so this time callback should be called
+    EXPECT_TRUE(callback_ok);
+
+    // close both pipe ends
+    close(pipefd[1]);
+    close(pipefd[0]);
+}
+
+// Tests if multiple external sockets and their callbacks can be passed and
+// it is supported properly by receive6() method.
+TEST_F(IfaceMgrTest, MiltipleExternalSockets6) {
+
+    callback_ok = false;
+    callback2_ok = false;
+
+    scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
+
+    // Create first pipe and register it as extra socket
+    int pipefd[2];
+    EXPECT_TRUE(pipe(pipefd) == 0);
+    EXPECT_NO_THROW(ifacemgr->addExternalSocket(pipefd[0], my_callback));
+
+    // Let's create a second pipe and register it as well
+    int secondpipe[2];
+    EXPECT_TRUE(pipe(secondpipe) == 0);
+    EXPECT_NO_THROW(ifacemgr->addExternalSocket(secondpipe[0], my_callback2));
+
+    Pkt6Ptr pkt6;
+    ASSERT_NO_THROW(pkt6 = ifacemgr->receive6(1));
+
+    // Our callbacks should not be called this time (there was no data)
+    EXPECT_FALSE(callback_ok);
+    EXPECT_FALSE(callback2_ok);
+
+    // IfaceMgr should not process control socket data as incoming packets
+    EXPECT_FALSE(pkt6);
+
+    // Now, send some data over the first pipe (38 bytes)
+    EXPECT_EQ(38, write(pipefd[1], "Hi, this is a message sent over a pipe", 38));
+
+    // ... and repeat
+    ASSERT_NO_THROW(pkt6 = ifacemgr->receive6(1));
+
+    // IfaceMgr should not process control socket data as incoming packets
+    EXPECT_FALSE(pkt6);
+
+    // There was some data, so this time callback should be called
+    EXPECT_TRUE(callback_ok);
+    EXPECT_FALSE(callback2_ok);
+
+    // Read the data sent, because our test callbacks are too dumb to actually
+    // do it. We don't care about the content read, because we're testing
+    // the callbacks, not pipes.
+    char buf[80];
+    read(pipefd[0], buf, 80);
+
+    // Clear the status...
+    callback_ok = false;
+    callback2_ok = false;
+
+    // And try again, using the second pipe
+    EXPECT_EQ(38, write(secondpipe[1], "Hi, this is a message sent over a pipe", 38));
+
+    // ... and repeat
+    ASSERT_NO_THROW(pkt6 = ifacemgr->receive6(1));
+
+    // IfaceMgr should not process control socket data as incoming packets
+    EXPECT_FALSE(pkt6);
+
+    // There was some data, so this time callback should be called
+    EXPECT_FALSE(callback_ok);
+    EXPECT_TRUE(callback2_ok);
+
+    // close both pipe ends
+    close(pipefd[1]);
+    close(pipefd[0]);
+
+    close(secondpipe[1]);
+    close(secondpipe[0]);
+}
+
+// Tests if existing external socket can be deleted and that such deletion does
+// not affect any other existing sockets. Tests uses receive6()
+TEST_F(IfaceMgrTest, DeleteExternalSockets6) {
+
+    callback_ok = false;
+    callback2_ok = false;
+
+    scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
+
+    // Create first pipe and register it as extra socket
+    int pipefd[2];
+    EXPECT_TRUE(pipe(pipefd) == 0);
+    EXPECT_NO_THROW(ifacemgr->addExternalSocket(pipefd[0], my_callback));
+
+    // Let's create a second pipe and register it as well
+    int secondpipe[2];
+    EXPECT_TRUE(pipe(secondpipe) == 0);
+    EXPECT_NO_THROW(ifacemgr->addExternalSocket(secondpipe[0], my_callback2));
+
+    // Now delete the first session socket
+    EXPECT_NO_THROW(ifacemgr->deleteExternalSocket(pipefd[0]));
+
+    // Now check whether the second callback is still functional
+    EXPECT_EQ(38, write(secondpipe[1], "Hi, this is a message sent over a pipe", 38));
+
+    // ... and repeat
+    Pkt6Ptr pkt6;
+    ASSERT_NO_THROW(pkt6 = ifacemgr->receive6(1));
+
+    // IfaceMgr should not process control socket data as incoming packets
+    EXPECT_FALSE(pkt6);
+
+    // There was some data, so this time callback should be called
+    EXPECT_FALSE(callback_ok);
+    EXPECT_TRUE(callback2_ok);
+
+    // Let's reset the status
+    callback_ok = false;
+    callback2_ok = false;
+
+    // Now let's send something over the first callback that was unregistered.
+    // We should NOT receive any callback.
+    EXPECT_EQ(38, write(pipefd[1], "Hi, this is a message sent over a pipe", 38));
+
+    // Now check that the first callback is NOT called.
+    ASSERT_NO_THROW(pkt6 = ifacemgr->receive6(1));
+    EXPECT_FALSE(callback_ok);
+
+    // close both pipe ends
+    close(pipefd[1]);
+    close(pipefd[0]);
+
+    close(secondpipe[1]);
+    close(secondpipe[0]);
+}
+
+
 // Test checks if the unicast sockets can be opened.
 // This test is now disabled, because there is no reliable way to test it. We
 // can't even use loopback, beacuse openSockets() skips loopback interface