Parcourir la source

[2902] Basic implementation to send packet over raw socket.

Marcin Siodelski il y a 12 ans
Parent
commit
45e29ee0ee

+ 4 - 0
src/lib/dhcp/hwaddr.h

@@ -27,6 +27,10 @@ namespace dhcp {
 /// @brief Hardware type that represents information from DHCPv4 packet
 struct HWAddr {
 public:
+
+    /// @brief Size of an ethernet hardware address.
+    static const size_t ETHERNET_HWADDR_LEN = 6;
+
     /// @brief Maximum size of a hardware address.
     static const size_t MAX_HWADDR_LEN = 20;
 

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

@@ -758,7 +758,7 @@ IfaceMgr::send(const Pkt4Ptr& pkt) {
 
     // Skip checking if packet filter is non-NULL because it has been
     // already checked when packet filter was set.
-    return (packet_filter_->send(getSocket(*pkt), pkt));
+    return (packet_filter_->send(*iface, getSocket(*pkt), pkt));
 }
 
 

+ 7 - 1
src/lib/dhcp/pkt_filter.h

@@ -16,6 +16,7 @@
 #define PKT_FILTER_H
 
 #include <asiolink/io_address.h>
+#include <boost/shared_ptr.hpp>
 
 namespace isc {
 namespace dhcp {
@@ -71,13 +72,18 @@ public:
 
     /// @brief Send packet over specified socket.
     ///
+    /// @param iface interface to be used to send packet
     /// @param sockfd socket descriptor
     /// @param pkt packet to be sent
     ///
     /// @return result of sending the packet. It is 0 if successful.
-    virtual int send(uint16_t sockfd, const Pkt4Ptr& pkt) = 0;
+    virtual int send(const Iface& iface, uint16_t sockfd,
+                     const Pkt4Ptr& pkt) = 0;
 };
 
+/// Pointer to a PktFilter object.
+typedef boost::shared_ptr<PktFilter> PktFilterPtr;
+
 } // namespace isc::dhcp
 } // namespace isc
 

+ 2 - 1
src/lib/dhcp/pkt_filter_inet.cc

@@ -198,7 +198,8 @@ PktFilterInet::receive(const Iface& iface, const SocketInfo& socket_info) {
 }
 
 int
-PktFilterInet::send(uint16_t sockfd, const Pkt4Ptr& pkt) {
+PktFilterInet::send(const Iface&, uint16_t sockfd,
+                    const Pkt4Ptr& pkt) {
     memset(&control_buf_[0], 0, control_buf_len_);
 
     // Set the target address we're sending to.

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

@@ -57,11 +57,13 @@ public:
 
     /// @brief Send packet over specified socket.
     ///
+    /// @param iface interface to be used to send packet
     /// @param sockfd socket descriptor
     /// @param pkt packet to be sent
     ///
     /// @return result of sending a packet. It is 0 if successful.
-    virtual int send(uint16_t sockfd, const Pkt4Ptr& pkt);
+    virtual int send(const Iface& iface, uint16_t sockfd,
+                     const Pkt4Ptr& pkt);
 
 private:
     /// Length of the control_buf_ array.

+ 129 - 6
src/lib/dhcp/pkt_filter_lpf.cc

@@ -16,16 +16,118 @@
 #include <dhcp/iface_mgr.h>
 #include <dhcp/pkt4.h>
 #include <dhcp/pkt_filter_lpf.h>
+#include <exceptions/exceptions.h>
+
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+
+using namespace isc::util;
 
 namespace isc {
 namespace dhcp {
 
+void
+PktFilterLPF::assembleEthernetHeader(const Iface& iface,
+                                     const Pkt4Ptr& pkt,
+                                     util::OutputBuffer& out_buf) {
+
+    std::vector<uint8_t> dest_addr = pkt->getHWAddr()->hwaddr_;
+    if (dest_addr.empty()) {
+        dest_addr.resize(HWAddr::ETHERNET_HWADDR_LEN);
+    }
+    out_buf.writeData(&dest_addr[0], dest_addr.size());
+    out_buf.writeData(iface.getMac(), iface.getMacLen());
+
+    out_buf.writeUint16(0x0800);
+}
+
+void
+PktFilterLPF::assembleIpUdpHeader(const Pkt4Ptr& pkt,
+                                  util::OutputBuffer& out_buf) {
+
+    struct ip ip_hdr;
+    memset(&ip_hdr, 0, sizeof(ip_hdr));
+    ip_hdr.ip_hl = (ip_hdr.ip_hl | 5) & 0xF;
+    ip_hdr.ip_v = (ip_hdr.ip_v | 4) & 0xF;
+    ip_hdr.ip_tos = IPTOS_LOWDELAY;
+    ip_hdr.ip_len = htons(sizeof(ip) + sizeof(udphdr) +
+                          pkt->getBuffer().getLength());
+    ip_hdr.ip_id = 0;
+    ip_hdr.ip_off = 0;
+    ip_hdr.ip_ttl = 128;
+    ip_hdr.ip_p = IPPROTO_UDP;
+    ip_hdr.ip_src.s_addr = htonl(pkt->getLocalAddr());
+    ip_hdr.ip_dst.s_addr = htonl(pkt->getRemoteAddr());
+    ip_hdr.ip_sum = checksumFinish(checksum(reinterpret_cast<const char*>(&ip_hdr),
+                                            sizeof(ip_hdr)));
+
+    out_buf.writeData(static_cast<void*>(&ip_hdr), sizeof(ip_hdr));
+
+    struct udphdr udp_hdr;
+    memset(&udp_hdr, 0, sizeof(udp_hdr));
+    udp_hdr.source = htons(pkt->getLocalPort());
+    udp_hdr.dest = htons(pkt->getRemotePort());
+    udp_hdr.len = htons(sizeof(udp_hdr) + pkt->getBuffer().getLength());
+    udp_hdr.check = 0;
+
+    out_buf.writeData(static_cast<void*>(&udp_hdr), sizeof(udp_hdr));
+
+}
+
+uint16_t
+PktFilterLPF::checksum(const char* buf, const uint32_t buf_size,
+                       uint32_t sum) {
+
+    uint32_t i;
+    for (i = 0; i < (buf_size & ~1U); i += 2) {
+        uint16_t chunk = buf[i] << 8 | buf[i+1];
+        sum += chunk;
+        if (sum > 0xFFFF) {
+            sum -= 0xFFFF;
+        }
+    }
+
+    if (i < buf_size) {
+        sum += buf[i] << 8;
+        if (sum > 0xFFFF) {
+            sum -= 0xFFFF;
+        }
+    }
+
+    return (sum);
+}
+
+uint16_t
+PktFilterLPF::checksumFinish(uint16_t sum) {
+    return (htons(~sum));
+}
+
 int
-PktFilterLPF::openSocket(const Iface&, const isc::asiolink::IOAddress&,
+PktFilterLPF::openSocket(const Iface& iface, const isc::asiolink::IOAddress&,
                          const uint16_t, const bool,
                          const bool) {
-    isc_throw(isc::NotImplemented,
-              "Linux Packet Filtering is not implemented yet");
+
+    int sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+    if (sock < 0) {
+        isc_throw(SocketConfigError, "Failed to create raw LPF socket");
+    }
+
+    struct sockaddr_ll sa;
+    memset(&sa, 0, sizeof(sockaddr_ll));
+    sa.sll_family = AF_PACKET;
+    sa.sll_ifindex = iface.getIndex();
+
+    if (bind(sock, reinterpret_cast<const struct sockaddr*>(&sa),
+             sizeof(sa)) < 0) {
+        close(sock);
+        isc_throw(SocketConfigError, "Failed to bind LPF socket '" << sock
+                  << "' to interface '" << iface.getName() << "'");
+    }
+
+    return (sock);
+
 }
 
 Pkt4Ptr
@@ -35,9 +137,30 @@ PktFilterLPF::receive(const Iface&, const SocketInfo&) {
 }
 
 int
-PktFilterLPF::send(uint16_t, const Pkt4Ptr&) {
-    isc_throw(isc::NotImplemented,
-              "Linux Packet Filtering is not implemented yet");
+PktFilterLPF::send(const Iface& iface, uint16_t sockfd, const Pkt4Ptr& pkt) {
+
+    OutputBuffer buf(14);
+
+    assembleEthernetHeader(iface, pkt, buf);
+    assembleIpUdpHeader(pkt, buf);
+
+    buf.writeData(pkt->getBuffer().getData(), pkt->getBuffer().getLength());
+
+    sockaddr_ll sa;
+    sa.sll_family = AF_PACKET;
+    sa.sll_ifindex = iface.getIndex();
+    sa.sll_protocol = htons(ETH_P_IP);
+    sa.sll_halen = 6;
+
+    int result = sendto(sockfd, buf.getData(), buf.getLength(), 0,
+                        reinterpret_cast<const struct sockaddr*>(&sa),
+                        sizeof(sockaddr_ll));
+    if (result < 0) {
+        isc_throw(SocketWriteError, "pkt4 send failed");
+    }
+    
+    return (0);
+    
 }
 
 

+ 19 - 1
src/lib/dhcp/pkt_filter_lpf.h

@@ -17,6 +17,8 @@
 
 #include <dhcp/pkt_filter.h>
 
+#include <util/buffer.h>
+
 namespace isc {
 namespace dhcp {
 
@@ -57,13 +59,29 @@ public:
 
     /// @brief Send packet over specified socket.
     ///
+    /// @oaram iface interface to be used to send packet
     /// @param sockfd socket descriptor
     /// @param pkt packet to be sent
     ///
     /// @throw isc::NotImplemented always
     /// @return result of sending a packet. It is 0 if successful.
-    virtual int send(uint16_t sockfd, const Pkt4Ptr& pkt);
+    virtual int send(const Iface& iface, uint16_t sockfd,
+                     const Pkt4Ptr& pkt);
+
+protected:
+
+    static void assembleEthernetHeader(const Iface& iface,
+                                       const Pkt4Ptr& pkt,
+                                       util::OutputBuffer& out_buf);
+
+    static void assembleIpUdpHeader(const Pkt4Ptr& pkt,
+                                    util::OutputBuffer& out_buf);
+
+    static uint16_t checksum(const char* buf, const uint32_t buf_size,
+                             uint32_t sum = 0);
 
+    static uint16_t checksumFinish(uint16_t sum);
+    
 };
 
 } // namespace isc::dhcp

+ 8 - 6
src/lib/dhcp/tests/iface_mgr_unittest.cc

@@ -19,6 +19,7 @@
 #include <dhcp/iface_mgr.h>
 #include <dhcp/pkt6.h>
 #include <dhcp/pkt_filter.h>
+#include <dhcp/pkt_filter_lpf.h>
 
 #include <boost/scoped_ptr.hpp>
 #include <gtest/gtest.h>
@@ -91,7 +92,7 @@ public:
     }
 
     /// Does nothing
-    virtual int send(uint16_t, const Pkt4Ptr&) {
+    virtual int send(const Iface&, uint16_t, const Pkt4Ptr&) {
         return (0);
     }
 
@@ -103,7 +104,9 @@ public:
 class NakedIfaceMgr: public IfaceMgr {
     // "naked" Interface Manager, exposes internal fields
 public:
-    NakedIfaceMgr() { }
+    NakedIfaceMgr() {
+        setPacketFilter(PktFilterPtr(new PktFilterLPF()));
+    }
     IfaceCollection & getIfacesLst() { return ifaces_; }
 };
 
@@ -725,14 +728,13 @@ TEST_F(IfaceMgrTest, sendReceive4) {
 
     // let's assume that every supported OS have lo interface
     IOAddress loAddr("127.0.0.1");
-    int socket1 = 0, socket2 = 0;
+    int socket1 = 0;
     EXPECT_NO_THROW(
         socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, DHCP4_SERVER_PORT + 10000);
-        socket2 = ifacemgr->openSocket(LOOPBACK, loAddr, DHCP4_SERVER_PORT + 10000 + 1);
     );
 
     EXPECT_GE(socket1, 0);
-    EXPECT_GE(socket2, 0);
+    //    EXPECT_GE(socket2, 0);
 
     boost::shared_ptr<Pkt4> sendPkt(new Pkt4(DHCPDISCOVER, 1234) );
 
@@ -765,7 +767,7 @@ TEST_F(IfaceMgrTest, sendReceive4) {
 
     boost::shared_ptr<Pkt4> rcvPkt;
 
-    EXPECT_EQ(true, ifacemgr->send(sendPkt));
+    EXPECT_NO_THROW(ifacemgr->send(sendPkt));
 
     ASSERT_NO_THROW(rcvPkt = ifacemgr->receive4(10));
     ASSERT_TRUE(rcvPkt); // received our own packet