From: Marcin Siodelski Date: Tue, 16 Apr 2013 22:23:31 +0000 (-0700) Subject: [2902] Basic implementation to send packet over raw socket. X-Git-Tag: bind10-1.2.0beta1-release~440^2~35 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=45e29ee0eeeced40dd047ec9da32da674124c55e;p=thirdparty%2Fkea.git [2902] Basic implementation to send packet over raw socket. --- diff --git a/src/lib/dhcp/hwaddr.h b/src/lib/dhcp/hwaddr.h index 13a16bf113..ccb11178ae 100644 --- a/src/lib/dhcp/hwaddr.h +++ b/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; diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc index 74f5fe8445..fd3a6c11af 100644 --- a/src/lib/dhcp/iface_mgr.cc +++ b/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)); } diff --git a/src/lib/dhcp/pkt_filter.h b/src/lib/dhcp/pkt_filter.h index 946bd14696..1c7332faf1 100644 --- a/src/lib/dhcp/pkt_filter.h +++ b/src/lib/dhcp/pkt_filter.h @@ -16,6 +16,7 @@ #define PKT_FILTER_H #include +#include 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 PktFilterPtr; + } // namespace isc::dhcp } // namespace isc diff --git a/src/lib/dhcp/pkt_filter_inet.cc b/src/lib/dhcp/pkt_filter_inet.cc index a6360aa9a1..a05ea482ff 100644 --- a/src/lib/dhcp/pkt_filter_inet.cc +++ b/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. diff --git a/src/lib/dhcp/pkt_filter_inet.h b/src/lib/dhcp/pkt_filter_inet.h index 4e98612a21..3ffbe805b8 100644 --- a/src/lib/dhcp/pkt_filter_inet.h +++ b/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. diff --git a/src/lib/dhcp/pkt_filter_lpf.cc b/src/lib/dhcp/pkt_filter_lpf.cc index ef75426065..e0a4616552 100644 --- a/src/lib/dhcp/pkt_filter_lpf.cc +++ b/src/lib/dhcp/pkt_filter_lpf.cc @@ -16,16 +16,118 @@ #include #include #include +#include + +#include +#include +#include +#include + +using namespace isc::util; namespace isc { namespace dhcp { +void +PktFilterLPF::assembleEthernetHeader(const Iface& iface, + const Pkt4Ptr& pkt, + util::OutputBuffer& out_buf) { + + std::vector 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(&ip_hdr), + sizeof(ip_hdr))); + + out_buf.writeData(static_cast(&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(&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(&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(&sa), + sizeof(sockaddr_ll)); + if (result < 0) { + isc_throw(SocketWriteError, "pkt4 send failed"); + } + + return (0); + } diff --git a/src/lib/dhcp/pkt_filter_lpf.h b/src/lib/dhcp/pkt_filter_lpf.h index 67b190fcd6..5987b9956e 100644 --- a/src/lib/dhcp/pkt_filter_lpf.h +++ b/src/lib/dhcp/pkt_filter_lpf.h @@ -17,6 +17,8 @@ #include +#include + 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 diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc index 31b9300086..df346db863 100644 --- a/src/lib/dhcp/tests/iface_mgr_unittest.cc +++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -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 sendPkt(new Pkt4(DHCPDISCOVER, 1234) ); @@ -765,7 +767,7 @@ TEST_F(IfaceMgrTest, sendReceive4) { boost::shared_ptr 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