Commit 45e29ee0 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

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

parent f85cdd32
......@@ -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;
......
......@@ -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));
}
......
......@@ -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
......
......@@ -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.
......
......@@ -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.
......
......@@ -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);
}
......
......@@ -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
......
......@@ -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
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment