Browse Source

[1708] Data reception over many interfaces (IPv6) added

Tomek Mrugalski 13 years ago
parent
commit
ed478d2878

+ 14 - 2
src/bin/dhcp6/dhcp6.spec

@@ -1,6 +1,6 @@
 {
   "module_spec": {
-    "module_name": "dhcp6",
+    "module_name": "Dhcp6",
     "module_description": "DHCPv6 server daemon",
     "config_data": [
       { "item_name": "interface",
@@ -9,6 +9,18 @@
         "item_default": "eth0"
       }
     ],
-    "commands": []
+    "commands": [
+        {
+            "command_name": "shutdown",
+            "command_description": "Shuts down DHCPv6 server.",
+            "command_args": [
+                {
+                    "item_name": "pid",
+                    "item_type": "integer",
+                    "item_optional": true
+                }
+            ]
+        }
+    ]
   }
 }

+ 3 - 1
src/bin/dhcp6/dhcp6_srv.cc

@@ -79,9 +79,11 @@ void Dhcpv6Srv::shutdown() {
 
 bool Dhcpv6Srv::run() {
     while (!shutdown_) {
+        /// @todo: calculate actual timeout once we have lease database
+        int timeout = 1000;
 
         // client's message and server's response
-        Pkt6Ptr query = IfaceMgr::instance().receive6();
+        Pkt6Ptr query = IfaceMgr::instance().receive6(timeout);
         Pkt6Ptr rsp;
 
         if (query) {

+ 104 - 42
src/lib/dhcp/iface_mgr.cc

@@ -693,13 +693,12 @@ IfaceMgr::receive4(uint32_t timeout) {
 
     const SocketInfo* candidate = 0;
     IfaceCollection::const_iterator iface;
-
     fd_set sockets;
-    FD_ZERO(&sockets);
     int maxfd = 0;
-
     stringstream names;
 
+    FD_ZERO(&sockets);
+
     /// @todo: marginal performance optimization. We could create the set once
     /// and then use its copy for select(). Please note that select() modifies
     /// provided set to indicated which sockets have something to read.
@@ -858,9 +857,108 @@ IfaceMgr::receive4(uint32_t timeout) {
     return (pkt);
 }
 
-Pkt6Ptr IfaceMgr::receive6() {
-    uint8_t buf[RCVBUFSIZE];
+Pkt6Ptr IfaceMgr::receive6(uint32_t timeout) {
+
+    const SocketInfo* candidate = 0;
+    fd_set sockets;
+    int maxfd = 0;
+    stringstream names;
+
+    FD_ZERO(&sockets);
+
+    /// @todo: marginal performance optimization. We could create the set once
+    /// and then use its copy for select(). Please note that select() modifies
+    /// provided set to indicated which sockets have something to read.
+    IfaceCollection::const_iterator iface;
+    for (iface = ifaces_.begin(); iface != ifaces_.end(); ++iface) {
+
+        for (SocketCollection::const_iterator s = iface->sockets_.begin();
+             s != iface->sockets_.end(); ++s) {
+
+            // Only deal with IPv4 addresses.
+            if (s->addr_.getFamily() == AF_INET6) {
+                names << s->sockfd_ << "(" << iface->getName() << ") ";
+
+                // Add this socket to listening set
+                FD_SET(s->sockfd_, &sockets);
+                if (maxfd < s->sockfd_) {
+                    maxfd = s->sockfd_;
+                }
+            }
+        }
+    }
+
+    // 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)";
+    }
+
+    cout << "Trying to receive data on sockets:" << names.str()
+         << ".Timeout is " << timeout << " seconds." << endl;
+
+    /// @todo: implement sub-second precision one day
+    struct timeval select_timeout;
+    select_timeout.tv_sec = timeout;
+    select_timeout.tv_usec = 0;
+
+    int result = select(maxfd + 1, &sockets, NULL, NULL, &select_timeout);
+
+    if (result == 0) {
+        // nothing received and timeout has been reached
+        return (Pkt6Ptr()); // NULL
+    } else if (result < 0) {
+        cout << "Socket read error: " << strerror(errno) << endl;
+
+        /// @todo: perhaps throw here?
+        return (Pkt6Ptr()); // NULL
+    }
+
+    // Let's find out which socket has the data
+    if ((session_socket_ != INVALID_SOCKET) && (FD_ISSET(session_socket_, &sockets))) {
+        // something received over session socket
+        cout << "BIND10 command or config available over session socket." << endl;
+
+        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_();
+        }
+
+        return (Pkt6Ptr()); // NULL
+    }
+
+    // Let's find out which interface/socket has the data
+    for (iface = ifaces_.begin(); iface != ifaces_.end(); ++iface) {
+        for (SocketCollection::const_iterator s = iface->sockets_.begin();
+             s != iface->sockets_.end(); ++s) {
+            if (FD_ISSET(s->sockfd_, &sockets)) {
+                candidate = &(*s);
+                break;
+            }
+        }
+        if (candidate) {
+            break;
+        }
+    }
 
+    if (!candidate) {
+        cout << "Received data over unknown socket." << endl;
+        return (Pkt6Ptr()); // NULL
+    }
+
+    cout << "Trying to receive over UDP6 socket " << candidate->sockfd_ << " bound to "
+         << candidate->addr_.toText() << "/port=" << candidate->port_ << " on "
+         << iface->getFullName() << endl;
+
+    // Now we have a socket, let's get some data from it!
+    uint8_t buf[RCVBUFSIZE];
     memset(&control_buf_[0], 0, control_buf_len_);
     struct sockaddr_in6 from;
     memset(&from, 0, sizeof(from));
@@ -892,43 +990,7 @@ Pkt6Ptr IfaceMgr::receive6() {
     m.msg_control = &control_buf_[0];
     m.msg_controllen = control_buf_len_;
 
-    /// TODO: Need to move to select() and pool over
-    /// all available sockets. For now, we just take the
-    /// first interface and use first socket from it.
-    IfaceCollection::const_iterator iface = ifaces_.begin();
-    const SocketInfo* candidate = 0;
-    while (iface != ifaces_.end()) {
-        for (SocketCollection::const_iterator s = iface->sockets_.begin();
-             s != iface->sockets_.end(); ++s) {
-            if (s->addr_.getFamily() != AF_INET6) {
-                continue;
-            }
-            if (s->addr_.getAddress().to_v6().is_multicast()) {
-                candidate = &(*s);
-                break;
-            }
-            if (!candidate) {
-                candidate = &(*s); // it's not multicast, but it's better than nothing
-            }
-        }
-        if (candidate) {
-            break;
-        }
-        ++iface;
-    }
-    if (iface == ifaces_.end()) {
-        isc_throw(Unexpected, "No suitable IPv6 interfaces detected. Can't receive anything.");
-    }
-
-    if (!candidate) {
-        isc_throw(Unexpected, "Interface " << iface->getFullName()
-                  << " does not have any sockets open.");
-    }
-
-    cout << "Trying to receive over UDP6 socket " << candidate->sockfd_ << " bound to "
-         << candidate->addr_.toText() << "/port=" << candidate->port_ << " on "
-         << iface->getFullName() << endl;
-    int result = recvmsg(candidate->sockfd_, &m, 0);
+    result = recvmsg(candidate->sockfd_, &m, 0);
 
     struct in6_addr to_addr;
     memset(&to_addr, 0, sizeof(to_addr));

+ 3 - 1
src/lib/dhcp/iface_mgr.h

@@ -345,8 +345,10 @@ public:
     /// to not wait infinitely, but rather do something useful
     /// (e.g. remove expired leases)
     ///
+    /// @param timeout specifies timeout (in seconds)
+    ///
     /// @return Pkt6 object representing received packet (or NULL)
-    Pkt6Ptr receive6();
+    Pkt6Ptr receive6(uint32_t timeout);
 
     /// @brief Tries to receive IPv4 packet over open IPv4 sockets.
     ///

+ 1 - 1
src/lib/dhcp/tests/iface_mgr_unittest.cc

@@ -309,7 +309,7 @@ TEST_F(IfaceMgrTest, sendReceive6) {
 
     EXPECT_EQ(true, ifacemgr->send(sendPkt));
 
-    rcvPkt = ifacemgr->receive6();
+    rcvPkt = ifacemgr->receive6(10);
 
     ASSERT_TRUE(rcvPkt); // received our own packet