Browse Source

[1540] Moved Linux-specific code to iface_mgr_linux.cc

Tomek Mrugalski 13 years ago
parent
commit
4fd6efcde0
4 changed files with 107 additions and 67 deletions
  1. 22 64
      src/lib/dhcp/iface_mgr.cc
  2. 21 3
      src/lib/dhcp/iface_mgr.h
  3. 9 0
      src/lib/dhcp/iface_mgr_bsd.cc
  4. 55 0
      src/lib/dhcp/iface_mgr_linux.cc

+ 22 - 64
src/lib/dhcp/iface_mgr.cc

@@ -277,6 +277,9 @@ bool IfaceMgr::openSockets6(uint16_t port) {
                 return (false);
             }
 
+            // Binding socket to unicast address and then joining multicast group
+            // works well on Mac OS (and possibly other BSDs), but does not work
+            // on Linux.
             if ( !joinMulticast(sock, iface->getName(),
                                 string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
                 close(sock);
@@ -286,7 +289,8 @@ bool IfaceMgr::openSockets6(uint16_t port) {
 
             count++;
 #if defined(OS_LINUX)
-            // this doesn't work too well on NetBSD
+            // To receive multicast traffic, Linux requires binding socket to
+            // a multicast group. That in turn doesn't work on NetBSD.
             int sock2 = openSocket(iface->getName(),
                                    IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
                                    port);
@@ -623,24 +627,8 @@ IfaceMgr::send(const Pkt4Ptr& pkt)
     m.msg_iov = &v;
     m.msg_iovlen = 1;
 
-#if defined(OS_LINUX)
-    // Setting the interface is a bit more involved.
-    //
-    // We have to create a "control message", and set that to
-    // define the IPv4 packet information. We could set the
-    // source address if we wanted, but we can safely let the
-    // kernel decide what that should be.
-    m.msg_control = &control_buf_[0];
-    m.msg_controllen = control_buf_len_;
-    struct cmsghdr* cmsg = CMSG_FIRSTHDR(&m);
-    cmsg->cmsg_level = IPPROTO_IP;
-    cmsg->cmsg_type = IP_PKTINFO;
-    cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
-    struct in_pktinfo* pktinfo =(struct in_pktinfo *)CMSG_DATA(cmsg);
-    memset(pktinfo, 0, sizeof(struct in_pktinfo));
-    pktinfo->ipi_ifindex = pkt->getIndex();
-    m.msg_controllen = cmsg->cmsg_len;
-#endif
+    // call OS-specific routines (like setting interface index)
+    os_send4(m, control_buf_, control_buf_len_, pkt);
 
     cout << "Trying to send " << pkt->getBuffer().getLength() << " bytes to "
          << pkt->getRemoteAddr().toText() << ":" << pkt->getRemotePort()
@@ -700,13 +688,11 @@ IfaceMgr::receive4() {
 
     // Now we have a socket, let's get some data from it!
     struct sockaddr_in from_addr;
-    struct in_addr to_addr;
     const uint32_t RCVBUFSIZE = 1500;
     static uint8_t buf[RCVBUFSIZE];
 
     memset(&control_buf_[0], 0, control_buf_len_);
     memset(&from_addr, 0, sizeof(from_addr));
-    memset(&to_addr, 0, sizeof(to_addr));
 
     // Initialize our message header structure.
     struct msghdr m;
@@ -737,61 +723,33 @@ IfaceMgr::receive4() {
         return (Pkt4Ptr()); // NULL
     }
 
-    unsigned int ifindex = iface->getIndex();
-
-#if defined(OS_LINUX)
-    struct cmsghdr* cmsg;
-    struct in_pktinfo* pktinfo;
-
-    int found_pktinfo = 0;
-    cmsg = CMSG_FIRSTHDR(&m);
-    while (cmsg != NULL) {
-        if ((cmsg->cmsg_level == IPPROTO_IP) &&
-            (cmsg->cmsg_type == IP_PKTINFO)) {
-            pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg);
+    // We have all data let's create Pkt4 object.
+    Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(buf, result));
 
-            ifindex = pktinfo->ipi_ifindex;
-            to_addr = pktinfo->ipi_addr;
+    unsigned int ifindex = iface->getIndex();
 
-            // This field is useful, when we are bound to unicast
-            // address e.g. 192.0.2.1 and the packet was sent to
-            // broadcast. This will return broadcast address, not
-            // the address we are bound to.
+    IOAddress from(htonl(from_addr.sin_addr.s_addr));
+    uint16_t from_port = htons(from_addr.sin_port);
 
-            // IOAddress tmp(htonl(pktinfo->ipi_spec_dst.s_addr));
-            // cout << "The other addr is: " << tmp.toText() << endl;
+    // Set receiving interface based on information, which socket was used to
+    // receive data. OS-specific info (see os_receive4()) may be more reliable,
+    // so this value may be overwritten.
+    pkt->setIndex(ifindex);
+    pkt->setIface(iface->getName());
+    pkt->setRemoteAddr(from);
+    pkt->setRemotePort(from_port);
+    pkt->setLocalPort(candidate->port_);
 
-            // Perhaps we should uncomment this:
-            // to_addr = pktinfo->ipi_spec_dst;
-            found_pktinfo = 1;
-        }
-        cmsg = CMSG_NXTHDR(&m, cmsg);
-    }
-    if (!found_pktinfo) {
+    if (!os_receive4(m, pkt)) {
         cout << "Unable to find pktinfo" << endl;
         return (boost::shared_ptr<Pkt4>()); // NULL
     }
-#endif
-
-    IOAddress to(htonl(to_addr.s_addr));
-    IOAddress from(htonl(from_addr.sin_addr.s_addr));
-    uint16_t from_port = htons(from_addr.sin_port);
 
     cout << "Received " << result << " bytes from " << from.toText()
          << "/port=" << from_port
-         << " sent to " << to.toText() << " over interface "
+         << " sent to " << pkt->getLocalAddr().toText() << " over interface "
          << iface->getFullName() << endl;
 
-    // we have all data let's create Pkt4 object
-    Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(buf, result));
-
-    pkt->setIface(iface->getName());
-    pkt->setIndex(ifindex);
-    pkt->setLocalAddr(to);
-    pkt->setRemoteAddr(from);
-    pkt->setRemotePort(from_port);
-    pkt->setLocalPort(candidate->port_);
-
     return (pkt);
 }
 

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

@@ -300,7 +300,7 @@ public:
     /// (e.g. remove expired leases)
     ///
     /// @return Pkt6 object representing received packet (or NULL)
-    boost::shared_ptr<Pkt6> receive6();
+    Pkt6Ptr receive6();
 
     /// @brief Tries to receive IPv4 packet over open IPv4 sockets.
     ///
@@ -313,7 +313,7 @@ public:
     /// (e.g. remove expired leases)
     ///
     /// @return Pkt4 object representing received packet (or NULL)
-    boost::shared_ptr<Pkt4> receive4();
+    Pkt4Ptr receive4();
 
     /// Opens UDP/IP socket and binds it to address, interface and port.
     ///
@@ -439,11 +439,29 @@ protected:
     // to people who try to use multicast as source address.
 
     /// length of the control_buf_ array
-    int control_buf_len_;
+    size_t control_buf_len_;
 
     /// control-buffer, used in transmission and reception
     boost::scoped_array<char> control_buf_;
 
+
+    /// @brief A wrapper for OS-specific operations before sending IPv4 packet
+    ///
+    /// @param m message header (will be later used for sendmsg() call)
+    /// @param control_buf buffer to be used during transmission
+    /// @param control_buf_len buffer length
+    /// @param pkt packet to be sent
+    void os_send4(struct msghdr& m, boost::scoped_array<char>& control_buf,
+                        size_t control_buf_len, const Pkt4Ptr& pkt);
+
+    /// @brief OS-specific operations during IPv4 packet reception
+    ///
+    /// @param m message header (was used during recvmsg() call)
+    /// @param pkt packet received (some fields will be set here)
+    ///
+    /// @return true if successful, false otherwise
+    bool os_receive4(struct msghdr& m, Pkt4Ptr& pkt);
+
 private:
 
     /// creates a single instance of this class (a singleton implementation)

+ 9 - 0
src/lib/dhcp/iface_mgr_bsd.cc

@@ -33,6 +33,15 @@ IfaceMgr::detectIfaces() {
     stubDetectIfaces();
 }
 
+void IfaceMgr::os_send4_setup(struct msghdr& ,
+                              boost::scoped_array<char>& ,
+                              size_t , const Pkt4Ptr& ) {
+    // do nothing here. There's nothing BSD specific to do and os_send4_setup()
+    // interface is there only to not mix Linux-specific code in common
+    // IfaceMgr file.
+}
+
+
 }
 
 #endif

+ 55 - 0
src/lib/dhcp/iface_mgr_linux.cc

@@ -376,6 +376,61 @@ void IfaceMgr::Iface::setFlags(uint32_t flags) {
     flag_broadcast_ = flags & IFF_BROADCAST;
 }
 
+void IfaceMgr::os_send4(struct msghdr& m, boost::scoped_array<char>& control_buf,
+                        size_t control_buf_len, const Pkt4Ptr& pkt) {
+
+    // Setting the interface is a bit more involved.
+    //
+    // We have to create a "control message", and set that to
+    // define the IPv4 packet information. We could set the
+    // source address if we wanted, but we can safely let the
+    // kernel decide what that should be.
+    m.msg_control = &control_buf[0];
+    m.msg_controllen = control_buf_len;
+    struct cmsghdr* cmsg = CMSG_FIRSTHDR(&m);
+    cmsg->cmsg_level = IPPROTO_IP;
+    cmsg->cmsg_type = IP_PKTINFO;
+    cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+    struct in_pktinfo* pktinfo =(struct in_pktinfo *)CMSG_DATA(cmsg);
+    memset(pktinfo, 0, sizeof(struct in_pktinfo));
+    pktinfo->ipi_ifindex = pkt->getIndex();
+    m.msg_controllen = cmsg->cmsg_len;
+}
+
+bool IfaceMgr::os_receive4(struct msghdr& m, Pkt4Ptr& pkt) {
+    struct cmsghdr* cmsg;
+    struct in_pktinfo* pktinfo;
+    struct in_addr to_addr;
+
+    memset(&to_addr, 0, sizeof(to_addr));
+
+    cmsg = CMSG_FIRSTHDR(&m);
+    while (cmsg != NULL) {
+        if ((cmsg->cmsg_level == IPPROTO_IP) &&
+            (cmsg->cmsg_type == IP_PKTINFO)) {
+            pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg);
+
+            pkt->setIndex(pktinfo->ipi_ifindex);
+            pkt->setLocalAddr(IOAddress(htonl(pktinfo->ipi_addr.s_addr)));
+            return (true);
+
+            // This field is useful, when we are bound to unicast
+            // address e.g. 192.0.2.1 and the packet was sent to
+            // broadcast. This will return broadcast address, not
+            // the address we are bound to.
+
+            // IOAddress tmp(htonl(pktinfo->ipi_spec_dst.s_addr));
+            // cout << "The other addr is: " << tmp.toText() << endl;
+
+            // Perhaps we should uncomment this:
+            // to_addr = pktinfo->ipi_spec_dst;
+        }
+        cmsg = CMSG_NXTHDR(&m, cmsg);
+    }
+
+    return (false);
+}
+
 }
 
 #endif // if defined(LINUX)