diff --git a/CMakeLists.txt b/CMakeLists.txt index 726d6ce..a19cdbb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,6 +81,12 @@ IF(LIBTINS_ENABLE_DOT11) ENDIF(LIBTINS_ENABLE_WPA2) ENDIF(LIBTINS_ENABLE_DOT11) +OPTION(LIBTINS_USE_PCAP_SENDPACKET "Use pcap_sendpacket to send l2 packets" OFF) +IF(LIBTINS_USE_PCAP_SENDPACKET) + SET(HAVE_PACKET_SENDER_PCAP_SENDPACKET ON) + MESSAGE(STATUS "Using pcap_sendpacket to send l2 packets.") +ENDIF(LIBTINS_USE_PCAP_SENDPACKET) + # Add a target to generate API documentation using Doxygen FIND_PACKAGE(Doxygen QUIET) IF(DOXYGEN_FOUND) diff --git a/include/tins/config.h.in b/include/tins/config.h.in index b56c018..88bc5f2 100644 --- a/include/tins/config.h.in +++ b/include/tins/config.h.in @@ -10,4 +10,7 @@ /* Have WPA2 decryption library */ #cmakedefine HAVE_WPA2_DECRYPTION +/* Use pcap_sendpacket to send l2 packets */ +#cmakedefine HAVE_PACKET_SENDER_PCAP_SENDPACKET + #endif // TINS_CONFIG_H diff --git a/include/tins/ethernetII.h b/include/tins/ethernetII.h index dacc420..fc2bb58 100644 --- a/include/tins/ethernetII.h +++ b/include/tins/ethernetII.h @@ -145,13 +145,10 @@ namespace Tins { */ uint32_t trailer_size() const; - // Windows does not support sending L2 PDUs. - #ifndef WIN32 /** * \sa PDU::send() */ void send(PacketSender &sender, const NetworkInterface &iface); - #endif // WIN32 /** * \brief Check wether ptr points to a valid response for this PDU. diff --git a/include/tins/packet_sender.h b/include/tins/packet_sender.h index 8654346..afd3582 100644 --- a/include/tins/packet_sender.h +++ b/include/tins/packet_sender.h @@ -36,6 +36,10 @@ #include #include #include +#include "config.h" +#ifdef HAVE_PACKET_SENDER_PCAP_SENDPACKET + #include +#endif // HAVE_PACKET_SENDER_PCAP_SENDPACKET #include "network_interface.h" #include "macros.h" #include "cxxstd.h" @@ -378,6 +382,9 @@ namespace Tins { void send(PDU &pdu, const NetworkInterface &iface) { static_cast(pdu).send(*this, iface); } + #ifdef HAVE_PACKET_SENDER_PCAP_SENDPACKET + pcap_t* make_pcap_handle(const NetworkInterface& iface) const; + #endif // HAVE_PACKET_SENDER_PCAP_SENDPACKET PDU *recv_match_loop(const std::vector& sockets, PDU &pdu, struct sockaddr* link_addr, uint32_t addrlen); @@ -397,7 +404,11 @@ namespace Tins { // In BSD we need to store the buffer size, retrieved using BIOCGBLEN #if defined(BSD) || defined(__FreeBSD_kernel__) int buffer_size; - #endif + #endif // BSD + #ifdef HAVE_PACKET_SENDER_PCAP_SENDPACKET + typedef std::map PcapHandleMap; + PcapHandleMap pcap_handles; + #endif // HAVE_PACKET_SENDER_PCAP_SENDPACKET }; } diff --git a/src/ethernetII.cpp b/src/ethernetII.cpp index a4c71cb..dfd430d 100644 --- a/src/ethernetII.cpp +++ b/src/ethernetII.cpp @@ -43,6 +43,7 @@ #include #include #endif +#include "config.h" #include "ethernetII.h" #include "packet_sender.h" #include "rawpdu.h" @@ -111,12 +112,17 @@ uint32_t EthernetII::trailer_size() const { return padding; } -#ifndef WIN32 void EthernetII::send(PacketSender &sender, const NetworkInterface &iface) { if(!iface) throw invalid_interface(); - - #if !defined(BSD) && !defined(__FreeBSD_kernel__) + #if defined(HAVE_PACKET_SENDER_PCAP_SENDPACKET) || defined(BSD) || defined(__FreeBSD_kernel__) + // Sending using pcap_sendpacket/BSD bpf packet mode is the same here + sender.send_l2(*this, 0, 0, iface); + #elif defined(WIN32) + // On Windows we can only send l2 PDUs using pcap_sendpacket + throw std::runtime_error("LIBTINS_USE_PCAP_SENDPACKET is not enabled"); + #else + // Default GNU/Linux behaviour struct sockaddr_ll addr; memset(&addr, 0, sizeof(struct sockaddr_ll)); @@ -128,11 +134,8 @@ void EthernetII::send(PacketSender &sender, const NetworkInterface &iface) { memcpy(&(addr.sll_addr), _eth.dst_mac, address_type::address_size); sender.send_l2(*this, (struct sockaddr*)&addr, (uint32_t)sizeof(addr)); - #else - sender.send_l2(*this, 0, 0, iface); #endif } -#endif // WIN32 bool EthernetII::matches_response(const uint8_t *ptr, uint32_t total_sz) const { if(total_sz < sizeof(ethhdr)) diff --git a/src/packet_sender.cpp b/src/packet_sender.cpp index 5a202e9..244a12f 100644 --- a/src/packet_sender.cpp +++ b/src/packet_sender.cpp @@ -66,6 +66,8 @@ #include "ieee802_3.h" #include "internals.h" +using std::string; +using std::runtime_error; namespace Tins { const int PacketSender::INVALID_RAW_SOCKET = -1; @@ -107,12 +109,19 @@ PacketSender::~PacketSender() { #endif } #if defined(BSD) || defined(__FreeBSD_kernel__) - for(BSDEtherSockets::iterator it = _ether_socket.begin(); it != _ether_socket.end(); ++it) - ::close(it->second); + for(BSDEtherSockets::iterator it = _ether_socket.begin(); it != _ether_socket.end(); ++it) + ::close(it->second); #elif !defined(WIN32) - if(_ether_socket != INVALID_RAW_SOCKET) - ::close(_ether_socket); + if(_ether_socket != INVALID_RAW_SOCKET) + ::close(_ether_socket); #endif + + #ifdef HAVE_PACKET_SENDER_PCAP_SENDPACKET + for (PcapHandleMap::iterator it = pcap_handles.begin(); it != pcap_handles.end(); ++it) { + pcap_close(it->second); + } + pcap_handles.clear(); + #endif // HAVE_PACKET_SENDER_PCAP_SENDPACKET } void PacketSender::default_interface(const NetworkInterface &iface) { @@ -142,8 +151,37 @@ int PacketSender::get_ether_socket(const NetworkInterface& iface) { #endif } +#ifdef HAVE_PACKET_SENDER_PCAP_SENDPACKET + +pcap_t* PacketSender::make_pcap_handle(const NetworkInterface& iface) const { + #ifdef WIN32 + #define TINS_PREFIX_INTERFACE(x) ("\\Device\\NPF_" + x) + #else // WIN32 + #define TINS_PREFIX_INTERFACE(x) (x) + #endif // WIN32 + + char error[PCAP_ERRBUF_SIZE]; + pcap_t* handle = pcap_create(TINS_PREFIX_INTERFACE(iface.name()).c_str(), error); + if (!handle) { + throw runtime_error("Error opening pcap handle: " + string(error)); + } + if (pcap_set_promisc(handle, 1) < 0) { + throw runtime_error("Failed to set pcap handle promisc mode: " + string(pcap_geterr(handle))); + } + if (pcap_activate(handle) < 0) { + throw runtime_error("Failed to activate pcap handle: " + string(pcap_geterr(handle))); + } + return handle; +} + +#endif // HAVE_PACKET_SENDER_PCAP_SENDPACKET + void PacketSender::open_l2_socket(const NetworkInterface& iface) { - #if defined(BSD) || defined(__FreeBSD_kernel__) + #ifdef HAVE_PACKET_SENDER_PCAP_SENDPACKET + if (pcap_handles.count(iface) == 0) { + pcap_handles.insert(std::make_pair(iface, make_pcap_handle(iface))); + } + #elif defined(BSD) || defined(__FreeBSD_kernel__) int sock = -1; // At some point, there should be an available device for (int i = 0; sock == -1;i++) { @@ -258,7 +296,7 @@ PDU *PacketSender::send_recv(PDU &pdu, const NetworkInterface &iface) { try { pdu.send(*this, iface); } - catch(std::runtime_error&) { + catch(runtime_error&) { return 0; } return pdu.recv_response(*this, iface); @@ -268,16 +306,25 @@ PDU *PacketSender::send_recv(PDU &pdu, const NetworkInterface &iface) { void PacketSender::send_l2(PDU &pdu, struct sockaddr* link_addr, uint32_t len_addr, const NetworkInterface &iface) { - int sock = get_ether_socket(iface); PDU::serialization_type buffer = pdu.serialize(); - if(!buffer.empty()) { - #if defined(BSD) || defined(__FreeBSD_kernel__) - if(::write(sock, &buffer[0], buffer.size()) == -1) - #else - if(::sendto(sock, &buffer[0], buffer.size(), 0, link_addr, len_addr) == -1) - #endif - throw socket_write_error(make_error_string()); - } + + #ifdef HAVE_PACKET_SENDER_PCAP_SENDPACKET + open_l2_socket(iface); + pcap_t* handle = pcap_handles[iface]; + if (pcap_sendpacket(handle, (u_char*)&buffer[0], buffer.size()) != 0) { + throw runtime_error("Failed to send packet: " + string(pcap_geterr(handle))); + } + #else // HAVE_PACKET_SENDER_PCAP_SENDPACKET + int sock = get_ether_socket(iface); + if(!buffer.empty()) { + #if defined(BSD) || defined(__FreeBSD_kernel__) + if(::write(sock, &buffer[0], buffer.size()) == -1) + #else + if(::sendto(sock, &buffer[0], buffer.size(), 0, link_addr, len_addr) == -1) + #endif + throw socket_write_error(make_error_string()); + } + #endif // HAVE_PACKET_SENDER_PCAP_SENDPACKET } PDU *PacketSender::recv_l2(PDU &pdu, struct sockaddr *link_addr, @@ -294,7 +341,7 @@ PDU *PacketSender::recv_l3(PDU &pdu, struct sockaddr* link_addr, uint32_t len_ad std::vector sockets(1, _sockets[type]); if(type == IP_TCP_SOCKET || type == IP_UDP_SOCKET) { #ifdef BSD - throw std::runtime_error("Receiving L3 packets not supported on this platform"); + throw runtime_error("Receiving L3 packets not supported on this platform"); #endif open_l3_socket(ICMP_SOCKET); sockets.push_back(_sockets[ICMP_SOCKET]);