Boost Asio accessing asio::ip::address underlying data and byte ordering - c++

My goal is to create a unique ID for all IP address - port pair. The UID must be same across systems (no conflict for different endian systems). Size of IPV4 UID is 6 bytes and for ipv6 is 18 bytes.
uint8_t sourcePair[18]; /*ipv4=(4+2) bytes or ipv6=(16+2) bytes*/
I have two functions that will take the remote endpoint of a socket and get the desired UID. The design is as follows.
void CmdInterpreter::makeSourcePairV4(asio::ip::tcp::endpoint& remoteEp, unsigned short portNum, unsigned char(&binSourcePair)[18])
{
auto addressClass = remoteEp.address().to_v4();
auto ipBin = addressClass.to_uint();
memcpy(&binSourcePair[0], &ipBin, 4);
memcpy(&binSourcePair[4], &portNum, 2);
}
void CmdInterpreter::makeSourcePairV6(asio::ip::tcp::endpoint& remoteEp, unsigned short portNum, unsigned char(&binSourcePair)[18])
{
auto addressClass = remoteEp.address().to_v6();
auto ipBin = addressClass.to_bytes();
memcpy(&binSourcePair[0], &ipBin[0], 16);
memcpy(&binSourcePair[16], &portNum, 2);
}
This is how these functions are called
remoteEp = socketPtr->remote_endpoint();
if (remoteEp.address().is_v4())
CmdInterpreter::makeSourcePairV4(remoteEp, remoteEp.port(), sourcePair);
else
CmdInterpreter::makeSourcePairV6(remoteEp, remoteEp.port(), sourcePair);
Here the problem is the only way to access the IPv6 underlying data is using to_byte() which will give the data in network byte order. Also, I am doing a memcopy in unsigned short which is multibyte in length. Does this work? Is it a safe way? Is their any workarounds?

void CmdInterpreter::makeSourcePairV4(asio::ip::tcp::endpoint& remoteEp, unsigned short portNum, uint8_t(&sourcePair)[18])
{
auto addressClass = remoteEp.address().to_v4();
auto ipBin = addressClass.to_bytes();
memcpy(&sourcePair[0], &ipBin[0], 4);
#ifdef BOOST_ENDIAN_LITTLE_BYTE
byteSwap(portNum);
#endif
memcpy(&sourcePair[4], &portNum, 2);
}
void CmdInterpreter::makeSourcePairV6(asio::ip::tcp::endpoint& remoteEp, unsigned short portNum, uint8_t(&sourcePair)[18])
{
auto addressClass = remoteEp.address().to_v6();
auto ipBin = addressClass.to_bytes();
memcpy(&sourcePair[0], &ipBin[0], 16);
#ifdef BOOST_ENDIAN_LITTLE_BYTE
byteSwap(portNum);
#endif
memcpy(&sourcePair[16], &portNum, 2);
}
For both IPv4 and IPv6 address, use to_byte() function to get the remote endpoint address in big-endian format. For little-endian host, the port number will make endianness problem which can be fixed by swapping the bytes. To encode it to base 64 I used cppcodec library.
UID = cppcodec::base64_rfc4648::encode(sourcePair, 6);
UID = cppcodec::base64_rfc4648::encode(sourcePair, 18);
The template function used to swap the port number is:
template <typename T>
void byteSwap(T& portNumber)
{
char* startIndex = static_cast<char*>((void*)&portNumber);
char* endIndex = startIndex + sizeof(T);
std::reverse(startIndex, endIndex);
}

Related

sending i2c command from C++ application

I want to send a signal to i2c device though C++ application. I have tried to use system() function, but it take about 7-10ms to return.
so I have found this library but it doesn't allow me to send the port number.
this is the command that i want to send
i2cset -f -y 0 0x74 2 0x00
where, 2 is the port number. 0x00: is the command that I need to set in destination device.
So my question is is there any way to send a direct way to communicate with i2c device the same as i2cset application does?
Yes, there is a way. You can read some documentation here: https://www.kernel.org/doc/Documentation/i2c/dev-interface
Basically you have to first open the I2C device for reading and writing, on Raspberry Pi (which is where I have used this) it is:
int m_busFD = open("/dev/i2c-0", O_RDWR);
Then there are two ways:
Either use ioctl to set the address and then read() or write() to read or write to the line. This can look like so:
bool receiveBytes(const int addr, uint8_t *buf, const int len)
{
if (ioctl(busFD, I2C_SLAVE, addr) < 0)
return -1;
int res = read(busFD, buf, len);
return res == len;
}
Or use the i2c_msg/i2c_rdwr_ioctl_data struct interface with ioctl. This looks more complicated, but allows you to do more complex operations such as a write-restart-read operation. Here is the same read as before, but using this interface:
bool receiveBytes(const int addr, uint8_t *buf, const int len)
{
i2c_msg msgs[1] = {
{.addr = static_cast<uint16_t>(addr),
.flags = I2C_M_RD,
.len = static_cast<uint16_t>(len),
.buf = buf}};
i2c_rdwr_ioctl_data wrapper = {
.msgs = msgs,
.nmsgs = 1};
if (ioctl(m_busFD, I2C_RDWR, &wrapper) < 0)
return false;
return (msgs[0].len == len);
}
And here is an example of a write-restart-read:
bool sendRecBytes(
const int addr,
uint8_t *sbuf, const int slen,
uint8_t *rbuf, const int rlen)
{
i2c_msg msgs[2] = {
{.addr = static_cast<uint16_t>(addr),
.flags = {},
.len = static_cast<uint16_t>(slen),
.buf = sbuf},
{.addr = static_cast<uint16_t>(addr),
.flags = I2C_M_RD,
.len = static_cast<uint16_t>(rlen),
.buf = rbuf}};
i2c_rdwr_ioctl_data wrapper = {
.msgs = msgs,
.nmsgs = 2};
if (ioctl(m_busFD, I2C_RDWR, &wrapper) < 0)
return false;
return (msgs[0].len == slen) && (msgs[1].len == rlen);
}
Edit: Forgot to mention that this all requires:
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>

Populating struct resembling an IP Header with nibbles/sub-8-bit values

I've been wanting to get into networking with raw sockets recently and have decided to perform a generic ICMP ping using C++ and raw sockets.
I started with making a struct called IP_Header, defined as such:
struct IP_Header {
uint8_t version : 4;
uint8_t IHL : 4;
uint8_t DSCP : 6;
uint8_t ECN : 2;
uint16_t total_len;
uint16_t ident;
uint16_t flags : 3;
uint16_t frag_offset : 13;
uint8_t ttl;
uint8_t proto;
uint16_t header_chksum;
uint32_t src;
uint32_t dst;
};
And so i populated this struct with some default values:
void populateHeaderDefault(IP_Header* ip) {
ip->version = 4;
ip->IHL = 5;
ip->DSCP = 0;
ip->ECN = 0;
ip->total_len = htons(20); //header only
ip->ident = htons(1);
ip->flags = 2;
ip->frag_offset = 0;
ip->ttl = 64;
ip->proto = IPPROTO::IPPROTO_HOPOPTS;
ip->header_chksum = 0;
IN_ADDR ia;
inet_pton(AF_INET, "192.168.178.31", &ia);
ip->src = ia.S_un.S_addr;
inet_pton(AF_INET, "127.0.0.1", &ia);
ip->dst = ia.S_un.S_addr;
ip->header_chksum = header_checksum(ip, sizeof IP_Header);
}
However, the resulting IP Header Version/IHL contains 0x54 instead of 0x45, and the short that contains the Flags and Fragmentation is 0x0200 instead 0f 0x4000. (I'm comparing my values to an exact copy of the packet recreated in scapy)
So my question is how I would fix these values? I know manually assigning the right value would probably work, but I'd like to use nibbles for better accessability.
So my question is how I would fix these values?
The values in the struct are already correct. The problem is in how you use the value. You simply cannot rely on order of bit fields.
Here is a correct way to get 0x45 from the Version/IHL:
uint8_t verIHL = ip->version << 4 | ip->IHL;
You can declare version and header length bitfields based on host machine byte order similar to Linux IP header as shown below, which will make sure right values set.
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 ihl:4,
version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
__u8 version:4,
ihl:4;
#else
#error "Please fix <asm/byteorder.h>"
#endif
Secondly, you can define IP flags supported and set required values as below,
#define IP_FLAG_RF 0x8000 /* reserved fragment flag */
#define IP_FLAG_DF 0x4000 /* dont fragment flag */
#define IP_FLAG_MF 0x2000 /* more fragments flag */
ip->flags = htons(IP_FLAG_DF);

I am trying to create ntp client which is connecting with some ntp server to sync its clock

I have tried creating a ntp client by creating a packet with ntp request send that to server. but I am not able to code to calculate offset and set time according to that offset. Though whatever i have done through that I am getting some offset value but i am not sure that value is correct. Please if anyone can help me. Thanks in advance.
#include <stdio.h>
#define NTP_HDR_LEN 48
#define FRAC 4294967296
struct ntp_packet
{
uint8_t li_vn_mode; // Eight bits. li, vn, and mode.
// li. Two bits. Leap indicator.
// vn. Three bits. Version number of the protocol.
// mode. Three bits. Client will pick mode 3 for client.
uint8_t stratum; // Eight bits. Stratum level of the local clock.
uint8_t poll; // Eight bits. Maximum interval between successive messages.
uint8_t precision; // Eight bits. Precision of the local clock.
uint32_t rootDelay; // 32 bits. Total round trip delay time.
uint32_t rootDispersion; // 32 bits. Max error aloud from primary clock source.
uint32_t refId; // 32 bits. Reference clock identifier.
uint32_t refTm_s;
uint32_t refTm_f; // 64 bits. Reference time-stamp seconds.
uint32_t origTm_s;
uint32_t origTm_f; // 64 bits. Originate time-stamp seconds.
uint32_t rxTm_s;
uint32_t rxTm_f; // 64 bits. Received time-stamp seconds.
uint32_t txTm_s; // 32 bits and the most important field the client cares about. Transmit time-stamp seconds.
uint32_t txTm_f; // 32 bits. Transmit time-stamp fraction of a second.
}packet;
char sendBuf[2048];
char rcvBuf[2048];
struct timeval txt, trecv, tsend;
void adjust_time(signed long long off)
{
unsigned long long ntp_tym;
struct timeval unix_time;
gettimeofday(&unix_time, NULL);
ntp_tym = off + ((ntohl(txt.tv_sec + 2208988800)) + (ntohl(txt.tv_usec) /
1e6));
unix_time.tv_sec = ntp_tym >> 32;
unix_time.tv_usec = (long)(((ntp_tym - unix_time.tv_sec) << 32) / FRAC *
1e6);
settimeofday(&unix_time, NULL);
}
void CreateSocket()
{
sourceIp = "192.168.1.109";
hostNameInteractive = "139.143.5.30";
source_dest_Port = 123;
uint32_t s_recv_s, s_recv_f, s_trans_s, s_trans_f;
unsigned long long t1, t2, t3, t4;
signed long long offs;
double offset;
if ((sock_raw = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 0)
{
perror ("socket() failed to get socket descriptor for using ioctl() ");
exit(1);
}
CreateNtpHeader();
SendRecv();
gettimeofday(&trecv, NULL);
struct ntp_packet *npkt = (struct ntp_packet*)(rcvBuf);
s_recv_s = ntohl(npkt->rxTm_s);
s_recv_f = ntohl(npkt->rxTm_f);
s_trans_s = ntohl(npkt->txTm_s);
s_trans_f = ntohl(npkt->txTm_f);
t1 = ((ntohl(txt.tv_sec + 2208988800)) + (ntohl(txt.tv_usec) /
1000000));
t4 = ((ntohl(trecv.tv_sec + 2208988800)) + (ntohl(trecv.tv_usec) /
1000000));
t2 = ((s_recv_s) + (s_recv_f / 1e12));
t3 = ((s_trans_s) + (s_trans_f / 1e12));
offs = (((t2 - t1) + (t3 - t4))/2);
offset = ((double)offs) / FRAC;
std::cout<<"offset : "<<offset<<" sec"<<std::endl;
adjust_time(offs);
close(sock_raw);
}
void CreateNtpHeader()
{
struct ntp_packet *pkt = (struct ntp_packet *)(sendBuf);
gettimeofday(&txt, NULL);
pkt->li_vn_mode = 0xe3;
pkt->stratum = 0;
pkt->poll = 3;
pkt->precision = 0xfa;
pkt->rootDelay = 0x00000100;
pkt->rootDispersion = 0x00000100;
pkt->refId = 0;
pkt->refTm_s = 0;
pkt->refTm_f = 0;
pkt->origTm_s = 0;
pkt->origTm_f = 0;
pkt->rxTm_s = 0;
pkt->rxTm_f = 0;
pkt->txTm_s = htonl(txt.tv_sec + 2208988800);
pkt->txTm_f = htonl(txt.tv_usec);
}
int main(int arg, char* args[])
{
CreateSocket();
return 0;
}
This is not a complete code. this is just a part of code where i am having the problem. I have included all the needed headers. If any thing else needed or any explanation needed please let me know.

Reinjecting modified packets in netfilter module

I have used netfiler_queue to create a NFQUEUE module for iptables that handles all outgoing UDP packets.
I want to modify all UDP packets that match a certain pattern, and reinject them into the network.
Here is some example code:
...
static int Callback( nfq_q_handle *myQueue, struct nfgenmsg *msg, nfq_data *pkt, void *cbData) {
uint32_t id = 0;
nfqnl_msg_packet_hdr *header;
if ((header = nfq_get_msg_packet_hdr(pkt))) {
id = ntohl(header->packet_id);
}
// Get the packet payload
unsigned char *pktData;
int len = nfq_get_payload(pkt, &pktData);
// The following is an example.
// In reality, it involves more parsing of the packet payload.
if (len && pktData[40] == 'X') {
// Modify byte 40
pktData[40] = 'Y';
}
// Pass through the (modified) packet.
return nfq_set_verdict(myQueue, id, NF_ACCEPT, 0, NULL);
}
...
int main(){
...
struct nfq_handle nfqHandle;
nfq_create_queue(nfqHandle, 0, &Callback, NULL)
...
return 0;
}
The modified packet does not get reinjected into the stream. How would I inject the modified version of the packet?
Two things. First:
return nfq_set_verdict(myQueue, id, NF_ACCEPT, 0, NULL);
should be:
return nfq_set_verdict(myQueue, id, NF_ACCEPT, len, pktData);
That tells it you want to send a modified packet. (you might need some type casting)
Second, you just modified the packet. The IP stack isn't helping you out any more at this point, so you'll need to recompute the UDP checksum for that packet, or zero it out so the other end won't even check it.
The UDP checksum will live in bytes 0x1A and 0x1B of your packet, so this will zero them out:
pktData[0x1a] = 0;
pktData[0x1b] = 0;
and then your packet will go through.

Get audio samples from byte array

How to get data samples from QAudioInput
I found in this examples code from audioinput example code
void InputTest::readMore()
{
if(!m_audioInput)
return;
qint64 len = m_audioInput->bytesReady();
if(len > 4096)
len = 4096;
qint64 l = m_input->read(m_buffer.data(), len);
if(l > 0) {
m_audioInfo->write(m_buffer.constData(), l);
}
}
I understood that m_buffer contains audio data samples
but my audio processing library receives short samples
How I can convert this to short pointer
My audio library function like this
putSample( short *Sample, int numberOfSample)
I can get number of samples from
Q_ASSERT(m_format.sampleSize() % 8 == 0);
const int channelBytes = m_format.sampleSize() / 8;
const int sampleBytes = m_format.channels() * channelBytes;
Q_ASSERT(len % sampleBytes == 0);
const int numSamples = len / sampleBytes;
This page indicates read() is expecting a char* to store the data in. If you have set up the format of the audio device properly the data will indeed be 'segmented' as shorts in the char array and you can simply cast the char* to a short* before passing it to your library.