DPDK create a packet for transmission - c

I am new to DPDK and trying to create a packet to send it from one DPDK enabled machine to another connected directly via an ethernet. I modified an example/rxtx_callbacks/main.c provided with DPDK at both side. However, I am not receiving anything at the receiver. What wrong am I doing?
Modified function at transmitter: lcore_main is modified:
static __attribute__((noreturn)) void lcore_main()
{
uint16_t port;
struct ether_hdr *eth_hdr;
struct ether_addr daddr;
daddr.addr_bytes[0] = 116;
daddr.addr_bytes[1] = 225;
daddr.addr_bytes[2] = 228;
daddr.addr_bytes[3] = 204;
daddr.addr_bytes[4] = 106;
daddr.addr_bytes[5] = 82;
//rte_eth_macaddr_get(portid, &addr);
struct ipv4_hdr *ipv4_hdr;
int32_t i;
int ret;
RTE_ETH_FOREACH_DEV(port)
if (rte_eth_dev_socket_id(port) > 0 &&
rte_eth_dev_socket_id(port) !=
(int)rte_socket_id())
printf("WARNING, port %u is on remote NUMA node to "
"polling thread.\n\tPerformance will "
"not be optimal.\n", port);
printf("\nCore %u forwarding packets. [Ctrl+C to quit]\n",
rte_lcore_id());
//struct rte_mbuf *m_head = rte_pktmbuf_alloc(mbuf_pool);
struct rte_mbuf *m_head[BURST_SIZE];
for (;;) {
RTE_ETH_FOREACH_DEV(port) {
if(rte_pktmbuf_alloc_bulk(mbuf_pool, m_head, BURST_SIZE)!=0)
{
printf("Allocation problem\n");
}
for(i = 0; i < BURST_SIZE; i++) {
eth_hdr = rte_pktmbuf_mtod(m_head[i], struct ether_hdr *);
//eth_hdr = (struct ether_hdr *)rte_pktmbuf_append(m_head[i],
// sizeof(struct ether_hdr));
eth_hdr->ether_type = htons(ETHER_TYPE_IPv4);
rte_memcpy(&(eth_hdr->s_addr), &addr, sizeof(struct ether_addr));
rte_memcpy(&(eth_hdr->d_addr), &daddr, sizeof(struct ether_addr));
}
const uint16_t nb_tx = rte_eth_tx_burst(port, 0, m_head, BURST_SIZE);
if (unlikely(nb_tx < BURST_SIZE)) {
uint16_t buf;
for (buf = nb_tx; buf < BURST_SIZE; buf++)
rte_pktmbuf_free(m_head[buf]);
}
}
}
}
receiver side RTE_ETH_FOREACH_DEV of tx part is modified to:
RTE_ETH_FOREACH_DEV(port) {
struct rte_mbuf *bufs[BURST_SIZE];
const uint16_t nb_rx = rte_eth_rx_burst(port, bufs, BURST_SIZE);
//printf("Number of Packets received %d\n", nb_rx);
for(i = 0; i < nb_rx; i++) {
//ipv4_hdr = rte_pktmbuf_mtod_offset(bufs[i], struct ipv4_hdr *,
// sizeof(struct ether_hdr));
//printf("Packet ip received %d\n", ipv4_hdr->src_addr);
eth_hdr = rte_pktmbuf_mtod(bufs[i], struct ether_hdr *);
printf("Packet ip received %d\n", eth_hdr->ether_type);
}
if (unlikely(nb_rx == 0))
continue;
const uint16_t nb_tx = 0; // = rte_eth_tx_burst(port ^ 1, 0, bufs, nb_rx);
if (unlikely(nb_tx < nb_rx)) {
uint16_t buf;
for (buf = nb_tx; buf < nb_rx; buf++)
rte_pktmbuf_free(bufs[buf]);
}
}
Please let me know if I missed something.

There are few issues with the code:
eth_hdr = rte_pktmbuf_mtod(m_head[i], struct ether_hdr *);
Unlike rte_pktmbuf_append(), the rte_pktmbuf_mtod() does not change the packet length, so it should be set manually before the tx.
eth_hdr->ether_type = htons(ETHER_TYPE_IPv4);
If we set ETHER_TYPE_IPv4, a correct IPv4 header must follow. So we need either to add the header or to change the ether_type.
rte_memcpy(&(eth_hdr->s_addr), &addr, sizeof(struct ether_addr));
Where is the source address comes from?
const uint16_t nb_tx = rte_eth_tx_burst(port, 0, m_head, BURST_SIZE);
Looks like we transmit a burst of zero-sized packets with invalid IPv4 headers. Please also make sure the source/destination addresses are correct.

As suggested by #andriy-berestovsky, I used rte_eth_stats_get() and it shows packets are present in ethernet ring via the field ipackets but rte_eth_rx_burst is not returning any packets. Full code is included here, please let me know what I am doing wrong. (I am using testpmd at transmitter side)
#include <stdint.h>
#include <inttypes.h>
#include <rte_eal.h>
#include <rte_ethdev.h>
#include <rte_ether.h>
#include <rte_cycles.h>
#include <rte_lcore.h>
#include <rte_ip.h>
#include <rte_mbuf.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <signal.h>
#define MAX_SOURCE_SIZE (0x100000)
#define RX_RING_SIZE 1024
#define TX_RING_SIZE 1024
#define NUM_MBUFS 8191
#define MBUF_CACHE_SIZE 250
#define BURST_SIZE 32
static const struct rte_eth_conf port_conf_default = {
.rxmode = {
.max_rx_pkt_len = ETHER_MAX_LEN,
},
};
static struct {
uint64_t total_cycles;
uint64_t total_pkts;
} latency_numbers;
static volatile bool force_quit;
struct rte_mempool *mbuf_pool;
static void
signal_handler(int signum)
{
struct rte_eth_stats eth_stats;
int i;
if (signum == SIGINT || signum == SIGTERM) {
printf("\n\nSignal %d received, preparing to exit...\n",
signum);
RTE_ETH_FOREACH_DEV(i) {
rte_eth_stats_get(i, &eth_stats);
printf("Total number of packets received %llu, dropped rx full %llu and rest= %llu, %llu, %llu\n", eth_stats.ipackets, eth_stats.imissed, eth_stats.ierrors, eth_stats.rx_nombuf, eth_stats.q_ipackets[0]);
}
force_quit = true;
}
}
struct ether_addr addr;
/*
* Initialises a given port using global settings and with the rx buffers
* coming from the mbuf_pool passed as parameter
*/
static inline int
port_init(uint16_t port, struct rte_mempool *mbuf_pool)
{
struct rte_eth_conf port_conf = port_conf_default;
const uint16_t rx_rings = 1, tx_rings = 1;
uint16_t nb_rxd = RX_RING_SIZE;
uint16_t nb_txd = TX_RING_SIZE;
int retval;
uint16_t q;
struct rte_eth_dev_info dev_info;
struct rte_eth_txconf txconf;
if (!rte_eth_dev_is_valid_port(port))
return -1;
rte_eth_dev_info_get(port, &dev_info);
if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
port_conf.txmode.offloads |=
DEV_TX_OFFLOAD_MBUF_FAST_FREE;
retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
if (retval != 0)
return retval;
retval = rte_eth_dev_adjust_nb_rx_tx_desc(port, &nb_rxd, &nb_txd);
if (retval != 0) {
printf("Error in adjustment\n");
return retval;
}
for (q = 0; q < rx_rings; q++) {
retval = rte_eth_rx_queue_setup(port, q, nb_rxd,
rte_eth_dev_socket_id(port), NULL, mbuf_pool);
if (retval < 0) {
printf("RX queue setup prob\n");
return retval;
}
}
txconf = dev_info.default_txconf;
txconf.offloads = port_conf.txmode.offloads;
for (q = 0; q < tx_rings; q++) {
retval = rte_eth_tx_queue_setup(port, q, nb_txd,
rte_eth_dev_socket_id(port), &txconf);
if (retval < 0)
return retval;
}
retval = rte_eth_dev_start(port);
if (retval < 0) {
printf("Error in start\n");
return retval;
}
rte_eth_macaddr_get(port, &addr);
printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8
" %02"PRIx8" %02"PRIx8" %02"PRIx8"\n",
(unsigned)port,
addr.addr_bytes[0], addr.addr_bytes[1],
addr.addr_bytes[2], addr.addr_bytes[3],
addr.addr_bytes[4], addr.addr_bytes[5]);
rte_eth_promiscuous_enable(port);
return 0;
}
/*
* Main thread that does the work, reading from INPUT_PORT
* and writing to OUTPUT_PORT
*/
static __attribute__((noreturn)) void
lcore_main(void)
{
uint16_t port;
struct ether_hdr *eth_hdr;
//struct ether_addr addr;
//rte_eth_macaddr_get(portid, &addr);
struct ipv4_hdr *ipv4_hdr;
int32_t i;
RTE_ETH_FOREACH_DEV(port)
{
if (rte_eth_dev_socket_id(port) > 0 &&
rte_eth_dev_socket_id(port) !=
(int)rte_socket_id())
printf("WARNING, port %u is on remote NUMA node to "
"polling thread.\n\tPerformance will "
"not be optimal.\n", port);
}
printf("\nCore %u forwarding packets. [Ctrl+C to quit]\n",
rte_lcore_id());
for (;;) {
RTE_ETH_FOREACH_DEV(port) {
struct rte_mbuf *bufs[BURST_SIZE];
const uint16_t nb_rx = rte_eth_rx_burst(port, 0,bufs, BURST_SIZE);
for(i = 0; i < nb_rx; i++) {
ipv4_hdr = rte_pktmbuf_mtod_offset(bufs[i], struct ipv4_hdr *, sizeof(struct ether_hdr));
printf("Packet ip received %d\n", ipv4_hdr->src_addr);
}
if (unlikely(nb_rx == 0))
continue;
const uint16_t nb_tx = 0; // = rte_eth_tx_burst(port ^ 1, 0, bufs, nb_rx);
if (unlikely(nb_tx < nb_rx)) {
uint16_t buf;
for (buf = nb_tx; buf < nb_rx; buf++)
rte_pktmbuf_free(bufs[buf]);
}
}
if(force_quit)
break;
}
}
/* Main function, does initialisation and calls the per-lcore functions */
int
main(int argc, char *argv[])
{
uint16_t nb_ports;
uint16_t portid, port;
/* init EAL */
int ret = rte_eal_init(argc, argv);
if (ret < 0)
rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
argc -= ret;
argv += ret;
force_quit = false;
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
nb_ports = rte_eth_dev_count_avail();
printf("size ordered %lld\n", NUM_MBUFS *nb_ports);
mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL",
NUM_MBUFS * nb_ports, MBUF_CACHE_SIZE, 0,
RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
if (nb_ports < 1)
rte_exit(EXIT_FAILURE, "Error: number of ports must be greater than %d\n", nb_ports);
if (mbuf_pool == NULL)
rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
// initialize all ports
RTE_ETH_FOREACH_DEV(portid)
if (port_init(portid, mbuf_pool) != 0)
rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu8"\n",
portid);
if (rte_lcore_count() > 1)
printf("\nWARNING: Too much enabled lcores - "
"App uses only 1 lcore\n");
// call lcore_main on master core only
lcore_main();
return 0;
}
It seems to be a problem of ethernet card with ubuntu 14.04. With ubuntu 16.04 it is working fine.

Related

Manage Linux routing rules using RTNETLINK

I'm trying to write a small C based user space app that provides feature of managing routing rules using RTNETLINK. Below is an example accepting 3 arguments: add/del (rule), IP address and iface.
The problem with code below that it adds routing rule for "to" direction, while it doesn't for "from" direction. So basically code below is equal to: ip rule add to <src_addr> table <table_id>, and I would like to rewrite it so it can also do ip rule add from <src_addr> table <table_id>. Any suggestions?
/*
*
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <linux/rtnetlink.h>
/* Open netlink socket */
int open_netlink()
{
struct sockaddr_nl saddr;
int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (sock < 0) {
perror("Failed to open netlink socket");
return -1;
}
memset(&saddr, 0, sizeof(saddr));
return sock;
}
/* Helper structure for ip address data and attributes */
typedef struct {
char family;
char bitlen;
unsigned char data[sizeof(struct in6_addr)];
} _inet_addr;
/* */
#define NLMSG_TAIL(nmsg) \
((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
/* Add new data to rtattr */
int rtattr_add(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen)
{
int len = RTA_LENGTH(alen);
struct rtattr *rta;
if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) {
fprintf(stderr, "rtattr_add error: message exceeded bound of %d\n", maxlen);
return -1;
}
rta = NLMSG_TAIL(n);
rta->rta_type = type;
rta->rta_len = len;
if (alen) {
memcpy(RTA_DATA(rta), data, alen);
}
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
return 0;
}
int do_rule(int sock, int cmd, int flags, _inet_addr *address, int if_idx)
{
struct {
struct nlmsghdr n;
struct rtmsg r;
char buf[4096];
} nl_request;
/* Initialize request structure */
nl_request.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
nl_request.n.nlmsg_flags = NLM_F_REQUEST | flags;
nl_request.n.nlmsg_type = cmd;
nl_request.r.rtm_family = address->family;
nl_request.r.rtm_table = 1;
nl_request.r.rtm_scope = RT_SCOPE_LINK;
/* Set additional flags if NOT deleting route */
if (cmd != RTM_DELRULE) {
nl_request.r.rtm_protocol = RTPROT_BOOT;
nl_request.r.rtm_type = RTN_UNICAST;
}
nl_request.r.rtm_family = address->family;
nl_request.r.rtm_dst_len = address->bitlen;
/* Select scope, for simplicity we supports here only IPv6 and IPv4 */
if (nl_request.r.rtm_family == AF_INET6) {
nl_request.r.rtm_scope = RT_SCOPE_UNIVERSE;
} else {
nl_request.r.rtm_scope = RT_SCOPE_LINK;
}
/* Set destination network */
rtattr_add(&nl_request.n, sizeof(nl_request), /*RTA_NEWDST*/ RTA_DST, &address->data, address->bitlen / 8);
/* Send message to the netlink */
return send(sock, &nl_request, sizeof(nl_request), 0);
}
/* Simple parser of the string IP address
*/
int read_addr(char *addr, _inet_addr *res)
{
if (strchr(addr, ':')) {
res->family = AF_INET6;
res->bitlen = 128;
} else {
res->family = AF_INET;
res->bitlen = 32;
}
return inet_pton(res->family, addr, res->data);
}
#define NEXT_CMD_ARG() do { argv++; if (--argc <= 0) exit(-1); } while(0)
int main(int argc, char **argv)
{
int default_gw = 0;
int if_idx = 0;
int nl_sock;
_inet_addr to_addr = { 0 };
_inet_addr gw_addr = { 0 };
_inet_addr address = { 0 };
int nl_cmd;
int nl_flags;
/* Parse command line arguments */
while (argc > 0) {
if (strcmp(*argv, "add") == 0) {
nl_cmd = RTM_NEWRULE;
nl_flags = NLM_F_CREATE | NLM_F_EXCL;
} else if (strcmp(*argv, "del") == 0) {
nl_cmd = RTM_DELRULE;
nl_flags = 0;
} else if (strcmp(*argv, "to") == 0) {
NEXT_CMD_ARG(); /* skip "to" and jump to the actual destination addr */
if (read_addr(*argv, &address) != 1) {
fprintf(stderr, "Failed to parse destination network %s\n", *argv);
exit(-1);
}
} else if (strcmp(*argv, "dev") == 0) {
NEXT_CMD_ARG(); /* skip "dev" */
if_idx = if_nametoindex(*argv);
}
argc--; argv++;
}
nl_sock = open_netlink();
if (nl_sock < 0) {
exit(-1);
}
// do_route(nl_sock, nl_cmd, nl_flags, &to_addr, &gw_addr, default_gw, if_idx);
do_rule(nl_sock, nl_cmd, nl_flags, &address, if_idx);
close (nl_sock);
return 0;
}
Created netlink socket and request, however parts of request structure might be configured incorrectly to achieve the goal.

Using perf_event_open() to get sample address ,however the addr=0

I tried to use perf_event_open() to track all the store instructions to get their access address. I found only when I set attr.precise_ip > 0, I can get the non-zero address. But when I ran the same process on vm instead of host, the error massage was "Operation not supported", I can fix this problem by setting precise_ip = 0 on vm, but now I only get bunch of addresses equal to zero. I don't understand why precise_ip is related to the sample addrress which is not pointed out on document, and I also don't understand why I can't set precise_ip = 1 on vm while I can do it on host. Is there anybody can help me??
FYI: I use - cpu host option when I start vm using qemu-system-x86_64
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/fcntl.h>
#include <sys/syscall.h>
#include <linux/perf_event.h>
#define PERF_PAGES (1 + (1 << 16))
struct perf_sample {
struct perf_event_header header;
__u64 ip;
__u32 pid, tid; /* if PERF_SAMPLE_TID */
__u64 addr; /* if PERF_SAMPLE_ADDR */
__u64 weight; /* if PERF_SAMPLE_WEIGHT */
/* __u64 data_src; /\* if PERF_SAMPLE_DATA_SRC *\/ */
__u64 phy_addr;
};
int perf_event_open(struct perf_event_attr *attr,pid_t pid,int cpu,int group_fd,unsigned long flags)
{
return syscall(__NR_perf_event_open,attr,pid,cpu,group_fd,flags);
}
void workload()
{
int i,c=0;
for(i=0;i<100000000;i++)
{
c+=i*i;
c-=i*100;
c+=i*i*i/100;
}
}
int startup()
{
struct perf_event_attr attr;
memset(&attr,0,sizeof(struct perf_event_attr));
attr.type = PERF_TYPE_RAW;
attr.size = sizeof(struct perf_event_attr);
attr.config = 0x82d0;
attr.config1 = 0;
attr.sample_period = 1000;
attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_WEIGHT | PERF_SAMPLE_ADDR | PERF_SAMPLE_PHYS_ADDR ;
attr.disabled = 0;
//attr.inherit = 1;
attr.exclude_kernel = 1;
attr.exclude_hv = 1;
attr.exclude_callchain_kernel = 1;
attr.exclude_callchain_user = 1;
attr.precise_ip = 1; // when i set attr.precise_ip = 0 , all the addr = 0;
int fd=perf_event_open(&attr,0,-1,-1,0);
if(fd<0)
{
perror("Cannot open perf fd!");
return -1;
}
return fd;
}
void scan_thread(struct perf_event_mmap_page *p)
{
char *pbuf = (char *)p + p->data_offset;
__sync_synchronize();
printf("%d,\n", p->data_size);
if(p->data_head == p->data_tail) {
return;
}
struct perf_event_header *ph = (void *)(pbuf + (p->data_tail % p->data_size));
struct perf_sample* ps;
switch(ph->type) {
case PERF_RECORD_SAMPLE:
ps = (struct perf_sample*)ph;
// assert(ps != NULL);
if(ps == NULL)
{
printf("null\n");
}
if(ps!= NULL && ps->addr != 0) {
printf("ip %lx\n", ps->ip);
printf("tid %d\n", ps->tid);
printf("addr: %lx \n", ps->addr);
}
//printf("addr, %lx\n", ps->addr);
//printf("phy addr, %lx\n", ps->phy_addr);
break;
default:
printf("type %d\n", ph->type);
break;
}
}
int main()
{
int fd = startup();
size_t mmap_size = sysconf(_SC_PAGESIZE) * PERF_PAGES;
struct perf_event_mmap_page *p = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// start to perf
ioctl(fd,PERF_EVENT_IOC_ENABLE,0);
int a= 0;
while(1)
{
// uint64_t instructions;
// read(fd,&instructions,sizeof(instructions));
// printf("instructions=%ld\n",instructions);
// sleep(1);
workload();
scan_thread(p);
sleep(1);
}
}

howto: setup a bidirectional UDP connection for a message based communication

The problem I want to solve is to build a stable connection for exchanging data between a PC and my Raspberry Pi(RPi). They are connected via WLAN in a LAN by a router.
I created a simple way, by defining on every device a client(c) and a server(s). I give in short the pseudo-code for that:
#init:
s = createSocket
c = createSocket
s = bind to "localhost"
create thread for message handling
#message handling thread:
msg = recvfrom(s)
#main:
init(serverPort=10001, clientIP="raspberryPi", clientPort=10002)
sendto(c, "hello")
The problem with UDP via WLAN is, that some messages can get lost. So I decided to create a simple protocol for that data exchange. The idea is that the server acknowledges the reception of the data. The problem changes into that kind pseudo-code:
#init:
s = createSocket
c = createSocket
s = bind to "localhost"
create thread for message handling
#message handling thread:
msg = recvfrom(s)
sendto (c, "ack")
#main:
sendto(c, "hello")
wait for 100ms for res = recvfrom(s)
if res == timeout goto sendto
if res <> 'ack' wrong message
I am running into a problem, that the sending and receiving process using both recvfrom(s). Also the easy loop back test by using the same port for client and server can not be done.
Any ideas?
Some not working c code follows:
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <pthread.h>
#include <semaphore.h>
#include <errno.h>
// sockets
#ifdef WIN32
#ifndef WINVER
// set min win version to Win XP
#define WINVER 0x0501
#endif
//use lib: ws2_32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/un.h>
#include <unistd.h>
#include <arpa/inet.h>
#define ADDR_ANY INADDR_ANY
#define SOCKET_ERROR (-1)
#define INVALID_SOCKET (SOCKET)(~0)
#define closesocket(x) (close(x))
typedef int SOCKET;
typedef struct sockaddr_in SOCKADDR_IN;
typedef struct sockaddr SOCKADDR;
#endif
typedef int (* TfkpTCPcallback) (uint8_t * pData, size_t amount);
// size of the header
#define dStjTCPSocketControlMsg (sizeof(uint_32))
// start data msg struct
// <uint_32> id = 's'
// <uint_32> len
// res struct
// <uint_32> id = 'r'
// <uint_32> error code (0 = no error)
enum eStjTCPSocketControlMsgIDs {
eStjTCPSocketControlMsgID_start = 's',
eStjTCPSocketControlMsgID_result = 'r'
};
enum eStjTCPSocketControlMsgErrorIDs {
eStjTCPSocketControlMsgErrorID_noError = 0,
eStjTCPSocketControlMsgErrorID_otherError,
eStjTCPSocketControlMsgErrorID_socket,
eStjTCPSocketControlMsgErrorID_msgID,
eStjTCPSocketControlMsgErrorID_realloc,
eStjTCPSocketControlMsgErrorID_amount,
};
//! type to control a udp socket based message communication
typedef struct SstjTCPSocketControl {
pthread_t srvThr;
SOCKET sCli; //!< socket for the input
SOCKET sSrv; //!< socket for the output
struct sockaddr_in sAddrCli; //!< client address
int cliConnectedFlag; //!< <>0 if the client is connected
uint8_t * pMsgBuffer;
size_t msgBufferSize;
sem_t serverSign;
TfkpTCPcallback rxCB;
} TstjTCPSocketControl;
//! a global variable to control a udp message based communication
TstjTCPSocketControl gTCPsocketControl = {
.srvThr = NULL,
.sCli = -1,
.sSrv = -1,
.cliConnectedFlag = 0,
.pMsgBuffer = NULL,
.msgBufferSize = 0,
};
int recvResult(SOCKET s) {
int r;
uint32_t contrlMsg[2];
// recv that the server is ready to transmit
r = recv(s , (char *)contrlMsg , sizeof(contrlMsg) , 0);
if(r < 0) {
return eStjTCPSocketControlMsgErrorID_socket;
}
if (r != sizeof(contrlMsg)) {
return eStjTCPSocketControlMsgErrorID_amount;
}
if (contrlMsg[0] != eStjTCPSocketControlMsgID_result) {
return eStjTCPSocketControlMsgErrorID_msgID;
}
return contrlMsg[1];
}
int sendResult(SOCKET s, uint32_t errorCode) {
uint32_t contrlMsg[2];
int r;
contrlMsg[0] = eStjTCPSocketControlMsgID_result;
contrlMsg[1] = errorCode;
r = send(s , (char *)contrlMsg , sizeof(contrlMsg) , 0);
if (r < 0) return eStjTCPSocketControlMsgErrorID_socket;
return eStjTCPSocketControlMsgErrorID_noError;
}
//! sends a block of data
int TCPcontrolSend(uint8_t * pD, size_t dataSize) {
int r;
uint32_t contrlMsg[2];
// check if we have to connect
if (!gTCPsocketControl.cliConnectedFlag) {
if (connect(gTCPsocketControl.sCli , (struct sockaddr *)&gTCPsocketControl.sAddrCli , sizeof(gTCPsocketControl.sAddrCli)) < 0){
gTCPsocketControl.cliConnectedFlag = 0;
return -1;
} else {
gTCPsocketControl.cliConnectedFlag = 1;
}
}
// ok we are connected - lets send the data
start:
contrlMsg[0] = eStjTCPSocketControlMsgID_start;
contrlMsg[1] = dataSize;
// send that we what to transmit some data
r = send(gTCPsocketControl.sCli , (char *)contrlMsg , sizeof(contrlMsg) , 0);
if(r < 0) {
return -2;
}
// recv that the server is ready to transmit
r = recvResult(gTCPsocketControl.sCli);
if (eStjTCPSocketControlMsgErrorID_socket == r) return -3;
if (eStjTCPSocketControlMsgErrorID_amount == r) goto start;
// ok let's send
r = send(gTCPsocketControl.sCli , pD ,dataSize , 0);
if(r < 0) {
return -2;
}
// get ack from the server
r = recvResult(gTCPsocketControl.sCli);
if (eStjTCPSocketControlMsgErrorID_socket == r) return -3;
if (eStjTCPSocketControlMsgErrorID_amount == r) goto start;
return r;
}
//! the message pump
void * TCPcontrolMsgPump (void *pParams) {
int r;
uint32_t contrlMsg[2];
struct sockaddr_in cliAddr;
SOCKET sCli;
uint32_t dataSize;
socklen_t cliAddrSize;
sem_post(&gTCPsocketControl.serverSign);
//accept connection from an incoming client
cliAddrSize = sizeof(struct sockaddr_in);
sCli = accept(gTCPsocketControl.sSrv, (struct sockaddr *)&cliAddr, (socklen_t*)&cliAddrSize);
if (sCli < 0) goto end;
// run the pump
for (;;) {
// ok we are connected
// read start message
r = recv(sCli , (char *)contrlMsg , sizeof(contrlMsg), 0);
if (r < 0) goto end;
if (r != sizeof(contrlMsg)) {
sendResult(sCli, eStjTCPSocketControlMsgErrorID_amount);
continue;
}
if (contrlMsg[0] != eStjTCPSocketControlMsgID_start) {
sendResult(sCli, eStjTCPSocketControlMsgErrorID_msgID);
continue;
}
dataSize = contrlMsg[1];
// check if we have to realloc the rx buffer
if (gTCPsocketControl.msgBufferSize < dataSize) {
uint8_t *pNB = realloc(gTCPsocketControl.pMsgBuffer, dataSize);
if (!pNB) {
sendResult(sCli, eStjTCPSocketControlMsgErrorID_realloc);
continue;
}
gTCPsocketControl.pMsgBuffer = pNB;
gTCPsocketControl.msgBufferSize = dataSize;
}
sendResult(sCli, eStjTCPSocketControlMsgErrorID_noError);
// recv data
r = recv(sCli , gTCPsocketControl.pMsgBuffer , gTCPsocketControl.msgBufferSize, 0);
if (r < 0) goto end;
if (r != dataSize) {
sendResult(sCli, eStjTCPSocketControlMsgErrorID_amount);
continue;
}
sendResult(sCli, eStjTCPSocketControlMsgErrorID_noError);
// handle message
gTCPsocketControl.rxCB(gTCPsocketControl.pMsgBuffer , gTCPsocketControl.msgBufferSize);
continue;
}
end:
sem_post(&gTCPsocketControl.serverSign);
return (void *) -1;
}
//! init
int TCPcontrolInit (
int serverPort, //!< server tx port number - best over 1000
const char * szClient, //!< "family-PC" or "192.168.1.3"
int clientPort, //!< client tx port number
TfkpTCPcallback rxCB, //!< the rx data callback
long timeOut, //!< the time out of the rx operation in ms
size_t rxBufferSize, //!< the size of the rx buffer
size_t maxTCPdataSize //!< maximum size of a TCP datagram (400 Bytes seems a good size)
) {
#ifdef WIN32
// local data
WSADATA wsaData;
// start sockets
if ((WSAStartup(MAKEWORD(2, 2), &wsaData))) {
perror("WSAStartup failed!");
return -1;
}
#endif
char * szIPserver;
char * szIPclient;
struct hostent * pHostDescr;
struct sockaddr_in sAddr;
//if (serverPort == clientPort) return -1;
// -----------------
// get ip strings
// get ip of the server
pHostDescr = gethostbyname("localhost");
// check if found a host
if (!pHostDescr) {
return -11;
}
szIPserver = inet_ntoa(*(struct in_addr*)*pHostDescr->h_addr_list);
// get ip of the client
if (strcmp(szClient, "")) {
pHostDescr = gethostbyname(szClient);
} else {
pHostDescr = gethostbyname("localhost");
}
// check if found a host
if (!pHostDescr) {
return -12;
}
szIPclient = inet_ntoa(*(struct in_addr*)*pHostDescr->h_addr_list);
// -----------------
// try to create sockets
// try to create socket for the server
gTCPsocketControl.sSrv = socket(PF_INET , SOCK_STREAM, IPPROTO_TCP);
if (-1 == gTCPsocketControl.sSrv) return -21;
// try to create socket for the client
gTCPsocketControl.sCli = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (-1 == gTCPsocketControl.sCli) return -22;
// -----------------
// bind input to IP and port
memset(&sAddr,0,sizeof(sAddr));
sAddr.sin_family = PF_INET;
sAddr.sin_addr.s_addr = INADDR_ANY;
sAddr.sin_port = htons( serverPort );
// bind server socket to address
if (bind(gTCPsocketControl.sSrv, (SOCKADDR *)&sAddr, sizeof(SOCKADDR_IN))) {
return -31;
}
// and listen for incoming connections
if (listen(gTCPsocketControl.sSrv , 3)) {
return -32;
}
// -----------------
// connect output to IP and port
memset(&gTCPsocketControl.sAddrCli,0,sizeof(sAddr));
gTCPsocketControl.sAddrCli.sin_family = PF_INET;
gTCPsocketControl.sAddrCli.sin_addr.s_addr = inet_addr(szIPclient);
gTCPsocketControl.sAddrCli.sin_port = htons( clientPort );
if (connect(gTCPsocketControl.sCli , (struct sockaddr *)&gTCPsocketControl.sAddrCli , sizeof(gTCPsocketControl.sAddrCli)) < 0){
gTCPsocketControl.cliConnectedFlag = 0;
} else {
gTCPsocketControl.cliConnectedFlag = 1;
}
// create sign semaphore
sem_init(&gTCPsocketControl.serverSign, 0, 0);
// create buffers
gTCPsocketControl.pMsgBuffer = malloc(rxBufferSize);
if (!gTCPsocketControl.pMsgBuffer) {
return -32;
}
gTCPsocketControl.msgBufferSize = rxBufferSize;
// set callback
gTCPsocketControl.rxCB = rxCB;
// start rx thread
if(pthread_create(&gTCPsocketControl.srvThr , NULL, TCPcontrolMsgPump, NULL)) {
return -40;
}
// wait till rx server is running
sem_wait(&gTCPsocketControl.serverSign);
return 0;
}
//! closes the TCP server and client
void TCPcontrolClose () {
closesocket (gTCPsocketControl.sSrv);
closesocket (gTCPsocketControl.sCli);
free(gTCPsocketControl.pMsgBuffer);
memset(&gTCPsocketControl, 0, sizeof(TstjTCPSocketControl));
#ifdef WIN32
WSACleanup();
#endif
}
// -----------------------------------------
// test
int stFlag = 0;
#define dSTsize (1024 * 1024)
uint8_t STB[dSTsize];
int rxCB (uint8_t * pData, size_t amount) {
if (!stFlag) {
pData[amount] = 0;
printf("rx: %s\n",pData);
} else {
size_t i;
for (i = 0; i < dSTsize; i++) {
if (pData[i] != (uint8_t)((size_t)i & 0xFF)) {
fprintf(stderr, "stress test error at position %i\n",(int) i);
return 0;
}
}
printf("rx: stress test successful\n");
}
fflush(stdout);
return 0;
}
int main(void) {
int srvPort;
int clientPort;
const size_t ipLen = 256;
char szIP[ipLen];
const size_t dummyStrLen = 1024;
char szDummy[dummyStrLen];
size_t i;
int r;
// pre init for the stress test
for (i = 0; i < dSTsize; i++) {
STB[i] = (uint8_t)((size_t)i & 0xFF);
}
printf("TCP demo\n");
printf("enter server port: ");
fgets(szDummy, dummyStrLen, stdin);
szDummy[strcspn(szDummy, "\r\n")] = 0;
srvPort = atoi(szDummy);
printf("enter IP address of the other server: ");
fgets(szIP, 255, stdin);
szIP[strcspn(szIP, "\r\n")] = 0;
printf("enter client port: ");
fgets(szDummy, dummyStrLen, stdin);
szDummy[strcspn(szDummy, "\r\n")] = 0;
clientPort = atoi(szDummy);
if (TCPcontrolInit (
srvPort, //!< server port number - best over 1000
szIP, //!< "family-PC" or "192.168.1.3"
clientPort, //!< client port number
rxCB, //!< the rx data callback
100, //!< the time out of the rx operation in ms
10,//!< the size of the rx buffer
400 //!< maximum size of a TCP datagram (400 Bytes seems a good size)
) < 0 ){
fprintf(stderr, "TCP control setup failed!");
goto errorExit;
}
printf("commands:\n s - send\n t - tx stress test\n a - activate/deactivate rx for stress test\n h - help\n e - exit\n");
for(;;) {
printf("command: ");
fgets(szDummy, dummyStrLen, stdin);
switch(tolower(szDummy[0])) {
case 's':
fgets(szDummy, dummyStrLen, stdin);
szDummy[strcspn(szDummy, "\r\n")] = 0;
r = TCPcontrolSend((uint8_t *)szDummy, strlen(szDummy)+1);
if(r) {
fprintf(stderr,"sending data failed with code %i(%s)\n", r, strerror(errno));
}
break;
case 't':
r = TCPcontrolSend(STB, dSTsize);
if (r) {
fprintf(stderr,"stress test sending data failed with code %i\n", r);
}
break;
case 'a':
stFlag = (!stFlag) ? 1 : 0;
if (stFlag) {
printf("stress test RX now active\n");
} else {
printf("stress test RX deactivated\n");
}
break;
case 'h':
printf("commands:\n s - send\n t - tx stress test\n a - activate/deactivate rx for stress test\n h - help\n e - exit\n");
break;
case 'e':
goto stdExit;
}
}
stdExit:
TCPcontrolClose ();
return EXIT_SUCCESS;
errorExit:
TCPcontrolClose ();
return EXIT_FAILURE;
}
If you need a UDP file transfer application, try UFTP.
I wrote it primarily for multicast, but it works just as well with unicast. Give it a try, and let me know how it goes.
The TCP approach works fine. With the code below a full duplex connection with asynchronous RX TX works fine. Its tested in Linux and Windows:
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <pthread.h>
#include <semaphore.h>
#include <errno.h>
// sockets
#ifdef WIN32
#ifndef WINVER
// set min win version to Win XP
#define WINVER 0x0501
#endif
//use lib: ws2_32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/un.h>
#include <unistd.h>
#include <arpa/inet.h>
#define ADDR_ANY INADDR_ANY
#define SOCKET_ERROR (-1)
#define INVALID_SOCKET (SOCKET)(~0)
#define closesocket(x) (close(x))
typedef int SOCKET;
typedef struct sockaddr_in SOCKADDR_IN;
typedef struct sockaddr SOCKADDR;
#endif
typedef int (* TfkpTCPcallback) (uint8_t * pData, size_t amount);
// size of the header
#define dStjTCPSocketControlMsg (sizeof(uint_32))
// start data msg struct
// <uint_32> id = 's'
// <uint_32> len
// res struct
// <uint_32> id = 'r'
// <uint_32> error code (0 = no error)
enum eStjTCPSocketControlMsgIDs {
eStjTCPSocketControlMsgID_start = 's',
eStjTCPSocketControlMsgID_packet = 'p',
eStjTCPSocketControlMsgID_result = 'r'
};
enum eStjTCPSocketControlMsgErrorIDs {
eStjTCPSocketControlMsgErrorID_noError = 0,
eStjTCPSocketControlMsgErrorID_otherError,
eStjTCPSocketControlMsgErrorID_socket,
eStjTCPSocketControlMsgErrorID_msgID,
eStjTCPSocketControlMsgErrorID_realloc,
eStjTCPSocketControlMsgErrorID_amount,
eStjTCPSocketControlMsgErrorID_wrongPacket,
};
//! type to control a udp socket based message communication
typedef struct SstjTCPSocketControl {
pthread_t srvThr;
SOCKET sCli; //!< socket for the input
SOCKET sSrv; //!< socket for the output
struct sockaddr_in sAddrCli; //!< client address
int cliConnectedFlag; //!< <>0 if the client is connected
uint8_t * pMsgBuffer;
size_t msgBufferSize;
sem_t serverSign;
TfkpTCPcallback rxCB;
int maxTXsize;
} TstjTCPSocketControl;
//! a global variable to control a udp message based communication
TstjTCPSocketControl gTCPsocketControl = {
.srvThr = NULL,
.sCli = -1,
.sSrv = -1,
.cliConnectedFlag = 0,
.pMsgBuffer = NULL,
.msgBufferSize = 0,
};
static inline int _TCPcontrolRecvResult(SOCKET s) {
int r;
uint32_t contrlMsg[2];
// recv that the server is ready to transmit
r = recv(s , (char *)contrlMsg , sizeof(contrlMsg) , 0);
if(r < 0) {
return eStjTCPSocketControlMsgErrorID_socket;
}
if (r != sizeof(contrlMsg)) {
return eStjTCPSocketControlMsgErrorID_amount;
}
if (contrlMsg[0] != eStjTCPSocketControlMsgID_result) {
return eStjTCPSocketControlMsgErrorID_msgID;
}
return contrlMsg[1];
}
static inline int _TCPcontrolSendResult(SOCKET s, uint32_t errorCode) {
uint32_t contrlMsg[2];
int r;
contrlMsg[0] = eStjTCPSocketControlMsgID_result;
contrlMsg[1] = errorCode;
r = send(s , (char *)contrlMsg , sizeof(contrlMsg) , 0);
if (r < 0) return eStjTCPSocketControlMsgErrorID_socket;
return eStjTCPSocketControlMsgErrorID_noError;
}
//! sends a block of data
int TCPcontrolSend(uint8_t * pD, size_t dataSize) {
int r;
uint32_t contrlMsg[2];
uint32_t p;
uint32_t packets;
uint8_t * pB;
size_t am, amTotal;
// check if we have to connect
if (!gTCPsocketControl.cliConnectedFlag) {
if (connect(gTCPsocketControl.sCli , (struct sockaddr *)&gTCPsocketControl.sAddrCli , sizeof(gTCPsocketControl.sAddrCli)) < 0){
gTCPsocketControl.cliConnectedFlag = 0;
return -1;
} else {
gTCPsocketControl.cliConnectedFlag = 1;
}
}
// ok we are connected - lets send the data
start:
contrlMsg[0] = eStjTCPSocketControlMsgID_start;
contrlMsg[1] = dataSize;
// send that we what to transmit some data
r = send(gTCPsocketControl.sCli , (char *)contrlMsg , sizeof(contrlMsg) , 0);
if(r < 0) {
return -2;
}
// recv that the server is ready to transmit
r = _TCPcontrolRecvResult(gTCPsocketControl.sCli);
if (eStjTCPSocketControlMsgErrorID_socket == r) return -3;
if (eStjTCPSocketControlMsgErrorID_amount == r) goto start;
// ok let's send
packets = dataSize / gTCPsocketControl.maxTXsize;
if (dataSize % gTCPsocketControl.maxTXsize) packets++;
pB = pD;
amTotal = dataSize;
for (p = 0; p < packets; p++) {
// send packet pre header
contrlMsg[0] = eStjTCPSocketControlMsgID_packet;
contrlMsg[1] = p;
r = send(gTCPsocketControl.sCli , (char *)contrlMsg , sizeof(contrlMsg) , 0);
if(r < 0) {
return -4;
}
r = _TCPcontrolRecvResult(gTCPsocketControl.sCli);
if (eStjTCPSocketControlMsgErrorID_socket == r) return -5;
if (eStjTCPSocketControlMsgErrorID_amount == r) goto start;
am = (amTotal > gTCPsocketControl.maxTXsize) ? gTCPsocketControl.maxTXsize : amTotal;
sendPacket:
r = send(gTCPsocketControl.sCli ,(char *) pB ,am , 0);
if(r < 0) {
return -5;
}
// get ack from the server
r = _TCPcontrolRecvResult(gTCPsocketControl.sCli);
if (eStjTCPSocketControlMsgErrorID_socket == r) return -3;
if (eStjTCPSocketControlMsgErrorID_amount == r) goto sendPacket;
pB += am;
amTotal -= am;
}
return r;
}
//! the message pump
void * TCPcontrolMsgPump (void *pParams) {
int r;
uint32_t contrlMsg[2];
struct sockaddr_in cliAddr;
SOCKET sCli;
uint32_t dataSize;
socklen_t cliAddrSize;
uint32_t packets;
uint8_t * pB;
size_t am, amTotal;
uint32_t p;
sem_post(&gTCPsocketControl.serverSign);
//accept connection from an incoming client
cliAddrSize = sizeof(struct sockaddr_in);
sCli = accept(gTCPsocketControl.sSrv, (struct sockaddr *)&cliAddr, (socklen_t*)&cliAddrSize);
if (sCli < 0) goto end;
// run the pump
for (;;) {
// ok we are connected
// read start message
r = recv(sCli , (char *)contrlMsg , sizeof(contrlMsg), 0);
if (r < 0) goto end;
if (r != sizeof(contrlMsg)) {
_TCPcontrolSendResult(sCli, eStjTCPSocketControlMsgErrorID_amount);
continue;
}
if (contrlMsg[0] != eStjTCPSocketControlMsgID_start) {
_TCPcontrolSendResult(sCli, eStjTCPSocketControlMsgErrorID_msgID);
continue;
}
dataSize = contrlMsg[1];
// check if we have to realloc the rx buffer
if (gTCPsocketControl.msgBufferSize < dataSize) {
uint8_t *pNB = realloc(gTCPsocketControl.pMsgBuffer, dataSize);
if (!pNB) {
_TCPcontrolSendResult(sCli, eStjTCPSocketControlMsgErrorID_realloc);
continue;
}
gTCPsocketControl.pMsgBuffer = pNB;
gTCPsocketControl.msgBufferSize = dataSize;
}
_TCPcontrolSendResult(sCli, eStjTCPSocketControlMsgErrorID_noError);
// recv data
packets = dataSize / gTCPsocketControl.maxTXsize;
if (dataSize % gTCPsocketControl.maxTXsize) packets++;
pB = gTCPsocketControl.pMsgBuffer;
amTotal = dataSize;
for (p = 0; p < packets; p++) {
// receive packet header
r = recv(sCli , (char *)contrlMsg , sizeof(contrlMsg), 0);
if (r < 0) goto end;
if (r != sizeof(contrlMsg)) {
_TCPcontrolSendResult(sCli, eStjTCPSocketControlMsgErrorID_amount);
continue;
}
if (contrlMsg[0] != eStjTCPSocketControlMsgID_packet) {
_TCPcontrolSendResult(sCli, eStjTCPSocketControlMsgErrorID_msgID);
continue;
}
if (contrlMsg[1] != p) {
_TCPcontrolSendResult(sCli, eStjTCPSocketControlMsgErrorID_wrongPacket);
continue;
}
_TCPcontrolSendResult(sCli, eStjTCPSocketControlMsgErrorID_noError);
am = (amTotal > gTCPsocketControl.maxTXsize) ? gTCPsocketControl.maxTXsize : amTotal;
// ok the next message will contain the data
recvPacket:
r = recv(sCli , (char *)pB , am, 0);
if (r < 0) goto end;
if (r != am) {
_TCPcontrolSendResult(sCli, eStjTCPSocketControlMsgErrorID_amount);
goto recvPacket;
}
_TCPcontrolSendResult(sCli, eStjTCPSocketControlMsgErrorID_noError);
pB += am;
amTotal -= am;
}
// handle message
gTCPsocketControl.rxCB(gTCPsocketControl.pMsgBuffer , dataSize);
continue;
}
end:
sem_post(&gTCPsocketControl.serverSign);
return (void *) -1;
}
//! init
int TCPcontrolInit (
int serverPort, //!< server tx port number - best over 1000
const char * szClient, //!< "family-PC" or "192.168.1.3"
int clientPort, //!< client tx port number
TfkpTCPcallback rxCB, //!< the rx data callback
size_t rxBufferSize, //!< the size of the rx buffer
size_t maxTCPdataSize //!< maximum size of a TCP datagram (400 Bytes seems a good size)
) {
#ifdef WIN32
// local data
WSADATA wsaData;
// start sockets
if ((WSAStartup(MAKEWORD(2, 2), &wsaData))) {
perror("WSAStartup failed!");
return -1;
}
#endif
char * szIPserver;
char * szIPclient;
struct hostent * pHostDescr;
struct sockaddr_in sAddr;
// -----------------
// get ip strings
// get ip of the server
pHostDescr = gethostbyname("localhost");
// check if found a host
if (!pHostDescr) {
return -11;
}
szIPserver = inet_ntoa(*(struct in_addr*)*pHostDescr->h_addr_list);
// get ip of the client
if (strcmp(szClient, "")) {
pHostDescr = gethostbyname(szClient);
} else {
pHostDescr = gethostbyname("localhost");
}
// check if found a host
if (!pHostDescr) {
return -12;
}
szIPclient = inet_ntoa(*(struct in_addr*)*pHostDescr->h_addr_list);
// -----------------
// try to create sockets
// try to create socket for the server
gTCPsocketControl.sSrv = socket(PF_INET , SOCK_STREAM, IPPROTO_TCP);
if (-1 == gTCPsocketControl.sSrv) return -21;
// try to create socket for the client
gTCPsocketControl.sCli = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (-1 == gTCPsocketControl.sCli) return -22;
// -----------------
// bind input to IP and port
memset(&sAddr,0,sizeof(sAddr));
sAddr.sin_family = PF_INET;
sAddr.sin_addr.s_addr = INADDR_ANY;
sAddr.sin_port = htons( serverPort );
// bind server socket to address
if (bind(gTCPsocketControl.sSrv, (SOCKADDR *)&sAddr, sizeof(SOCKADDR_IN))) {
return -31;
}
// and listen for incoming connections
if (listen(gTCPsocketControl.sSrv , 3)) {
return -32;
}
// -----------------
// connect output to IP and port
memset(&gTCPsocketControl.sAddrCli,0,sizeof(sAddr));
gTCPsocketControl.sAddrCli.sin_family = PF_INET;
gTCPsocketControl.sAddrCli.sin_addr.s_addr = inet_addr(szIPclient);
gTCPsocketControl.sAddrCli.sin_port = htons( clientPort );
if (connect(gTCPsocketControl.sCli , (struct sockaddr *)&gTCPsocketControl.sAddrCli , sizeof(gTCPsocketControl.sAddrCli)) < 0){
gTCPsocketControl.cliConnectedFlag = 0;
} else {
gTCPsocketControl.cliConnectedFlag = 1;
}
// create sign semaphore
sem_init(&gTCPsocketControl.serverSign, 0, 0);
// create buffers
gTCPsocketControl.pMsgBuffer = malloc(rxBufferSize);
if (!gTCPsocketControl.pMsgBuffer) {
return -32;
}
gTCPsocketControl.msgBufferSize = rxBufferSize;
// set callback
gTCPsocketControl.rxCB = rxCB;
gTCPsocketControl.maxTXsize = maxTCPdataSize;
// start rx thread
if(pthread_create(&gTCPsocketControl.srvThr , NULL, TCPcontrolMsgPump, NULL)) {
return -40;
}
// wait till rx server is running
sem_wait(&gTCPsocketControl.serverSign);
return 0;
}
//! closes the TCP server and client
void TCPcontrolClose () {
closesocket (gTCPsocketControl.sSrv);
closesocket (gTCPsocketControl.sCli);
free(gTCPsocketControl.pMsgBuffer);
memset(&gTCPsocketControl, 0, sizeof(TstjTCPSocketControl));
#ifdef WIN32
WSACleanup();
#endif
}
//! inits the TCP control via stdin inputs
int TCPcontrolInitFromStdIn (
TfkpTCPcallback rxCB, //!< the rx data callback
size_t rxBufferSize, //!< the size of the rx buffer
size_t maxTCPdataSize //!< maximum size of a TCP datagram (400 Bytes seems a good size)
) {
int srvPort;
int clientPort;
const size_t ipLen = 256;
char szIP[ipLen];
const size_t dummyStrLen = 100;
char szDummy[dummyStrLen];
int r;
printf("====| TCP client/server setup |====\n");
printf("server listen port: ");
fgets(szDummy, dummyStrLen, stdin);
szDummy[strcspn(szDummy, "\r\n")] = 0;
srvPort = atoi(szDummy);
printf("client send IP address or name: ");
fgets(szIP, 255, stdin);
szIP[strcspn(szIP, "\r\n")] = 0;
printf("client port: ");
fgets(szDummy, dummyStrLen, stdin);
szDummy[strcspn(szDummy, "\r\n")] = 0;
clientPort = atoi(szDummy);
r = TCPcontrolInit (
srvPort, //!< server port number - best over 1000
szIP, //!< "family-PC" or "192.168.1.3"
clientPort, //!< client port number
rxCB, //!< the rx data callback
rxBufferSize, //!< the size of the rx buffer
maxTCPdataSize //!< maximum size of a TCP datagram (400 Bytes seems a good size)
);
if (!r) {
printf("setup finished successfully!\n");
printf("===================================\n");
} else {
printf("setup error: %i \n", r);
printf("===================================\n");
}
return r;
}
// -----------------------------------------
// test
enum eStates {
eState_std = 0,
eState_stressTest = 1,
eState_multiTX = 2
};
int stateID = eState_std;
#define dSTsize (1024 * 1024)
uint8_t STB[dSTsize];
int rxCB (uint8_t * pData, size_t amount) {
size_t i;
switch (stateID) {
case eState_std:
pData[amount] = 0;
printf("rx: %s\n",pData);
break;
case eState_stressTest:
for (i = 0; i < dSTsize; i++) {
if (pData[i] != (uint8_t)((size_t)i & 0xFF)) {
fprintf(stderr, "stress test error at position %i\n",(int) i);
fflush(stdout);
return 0;
}
}
printf("rx: stress test successful\n");
break;
case eState_multiTX:
printf("rx %iBytes\n", (int)amount);
break;
}
fflush(stdout);
return 0;
}
int main(void) {
const size_t dummyStrLen = 1024;
char szDummy[dummyStrLen];
size_t i;
int r, am, j;
// pre init for the stress test
for (i = 0; i < dSTsize; i++) {
STB[i] = (uint8_t)((size_t)i & 0xFF);
}
printf("TCP demo\n");
if (TCPcontrolInitFromStdIn(rxCB, 4096, 500)) goto errorExit;
printf("commands:\n s - send\n t - tx stress test\n a - activate/deactivate rx for stress test\n m - multi tx test\n h - help\n e - exit\n");
for(;;) {
printf("command: ");
fgets(szDummy, dummyStrLen, stdin);
switch(tolower(szDummy[0])) {
case 's':
stateID = eState_std;
fgets(szDummy, dummyStrLen, stdin);
szDummy[strcspn(szDummy, "\r\n")] = 0;
r = TCPcontrolSend((uint8_t *)szDummy, strlen(szDummy)+1);
if(r) {
fprintf(stderr,"sending data failed with code %i(%s)\n", r, strerror(errno));
} else {
printf("succeeded\n");
}
break;
case 't':
printf("sending packets...\n");
r = TCPcontrolSend(STB, dSTsize);
if (r) {
fprintf(stderr,"stress test sending data failed with code %i\n", r);
} else {
printf("succeeded\n");
}
break;
case 'a':
stateID = eState_stressTest;
printf("stress test RX now active\n");
break;
case 'm':
stateID = eState_multiTX;
printf("amount of transmissions: ");
fgets(szDummy, dummyStrLen, stdin);
szDummy[strcspn(szDummy, "\r\n")] = 0;
am = atoi(szDummy);
for (j = 0; j < am; j++) {
printf("tm %i...", j);
sprintf(szDummy,"tm %i",j);
r = TCPcontrolSend((uint8_t *)szDummy, strlen(szDummy)+1);
if (!r) printf("successful\n");
else printf("failed\n");
}
break;
case 'h':
printf("commands:\n s - send\n t - tx stress test\n a - activate/deactivate rx for stress test\n m - multi tx test\n h - help\n e - exit\n");
break;
case 'e':
goto stdExit;
}
}
stdExit:
TCPcontrolClose ();
return EXIT_SUCCESS;
errorExit:
TCPcontrolClose ();
return EXIT_FAILURE;
}
a note to the stress test and the initial connection. Under weak WLAN connections it could took some time.

socket programming FD_ISSET() method usage

so I'm new to socket programming, and I was asked to write the server side that sends data to a client according to a certain request. I'm trying to server multiple clients at the same time. When a client first connects, the server accepts with no troubles whatsoever, but when a client sends a certain request, I get stuck in an infinity loop and it's not clear at all to me why the server keeps sending the same info to the client over and over and over again, below is my code for the server side:
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <math.h>
#include <windows.h>
#include <winsock.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <io.h>
#include <fstream>
#include <vector>
#define MAX_CONNECTION 100
#ifndef max
#define max(a,b) ((a) < (b) ? (a) : (b))
#endif
typedef struct connection{
char ipaddr[16];
int port;
int sd;
} connection;
static unsigned short SERVER_PORT = 4118;
int main(int argc, char* argv[])
{
int maxfd =-1;
fd_set rset, allset;
connection client[MAX_CONNECTION];
int passiveSock; /* Main Server Socket */
struct sockaddr_in servSock_in;
#ifdef WIN32
WSADATA wsaData;
WSAStartup(0x0101, &wsaData);
#endif
int port;
if (argc > 1)
port = atoi(argv[1]);
else
port = SERVER_PORT;
for(int i=0; i < MAX_CONNECTION ; i++)
client[i].sd = -1;
memset((char *)&servSock_in, 0, sizeof(servSock_in));
servSock_in.sin_family = PF_INET;
servSock_in.sin_addr.s_addr = htonl(INADDR_ANY);
servSock_in.sin_port = htons((u_short)port);
passiveSock = socket(PF_INET, SOCK_STREAM, 0);
if (passiveSock < 0) {
fprintf(stderr, "I am too tired... I failed to open gate...\n");
return -1;
}
if (bind(passiveSock, (struct sockaddr *)&servSock_in, sizeof(servSock_in)) < 0){
fprintf(stderr, "I couldn't attach gate to port...\n");
return -1;
}
if (listen(passiveSock, 5) < 0) {
fprintf(stderr, "I am not hearing anything...\n");
return -1;
}
FD_SET(passiveSock, &allset);
maxfd = max(maxfd, passiveSock);
struct sockaddr_in cliSock_in;
int cliSockLen;
int connectedSock;
cliSockLen = sizeof(cliSock_in);
printf("\n Waiting for an incoming connection at port number %d", port);
int bytesread = 0;
for (;;)
{
//FD_ZERO(&allset);
rset = allset;
int nready = select(maxfd+1,&rset, NULL, NULL, NULL);
if(FD_ISSET(passiveSock, &rset)){
printf("In the first if");
connectedSock = accept(passiveSock, (struct sockaddr *)&cliSock_in, &cliSockLen);
/* if an error occurs while accepting */
if (connectedSock == -1) {
printf("\n Server: Accept error (errno = %d: %s)\n", errno, strerror(errno));
continue;
}
for (int i=0; i<MAX_CONNECTION; i++)
if (client[i].sd < 0){
client[i].sd=connectedSock;
strcpy(client[i].ipaddr, inet_ntoa(cliSock_in.sin_addr));
client[i].port= ntohs(cliSock_in.sin_port);
printf("\n Server: connection established with %s:%d\n",
client[i].ipaddr, client[i].port);
break;
}
FD_SET(connectedSock, &allset);
maxfd = max(maxfd, connectedSock);
}
else{
for(int j = 0 ; j < MAX_CONNECTION; j++){
connectedSock = client[j].sd;
printf("connectedSock is %d", connectedSock);
if(connectedSock < 0)
continue;
if(FD_ISSET(client[j].sd, &rset)){
unsigned char buffer[66000];
int index = 0;
bytesread = recv(connectedSock, (char *)buffer, 66000, 0);
int type;
type = (buffer[0] & 0xE0) >> 5;
if (type == 0)
{
char fname[100];
int i = 0;
int length = (buffer[i++] & 0x1F);
memcpy(&fname[0], &buffer[i], length);
fname[length] = '\0';
i += length;
int fs = 0;
fs += (buffer[i++] << 8) & 0xff00;
fs += buffer[i++];
char* filedata = (char*)malloc(fs*sizeof(char));
memcpy(&filedata[0], &buffer[i], fs);
filedata[fs] = '\0';
for (int i = 0; i < fs; i++)
printf("%c", filedata[i]);
printf("type=%d,length=%d,data=%s,fs=%d,filedata=%s", type, length, fname, fs, filedata);
std::ofstream of;
of.open(fname, std::ios::binary);
for (int i = 0; i < fs; i++)
of.write(filedata + i, 1);
of.close();
unsigned char rep;
int reptype = 0;
rep = (unsigned char)(reptype & 0x07);
send(connectedSock, (char*)(&rep), 1, 0);
}
else if (type == 1)
{
char fname[100];
int i = 0;
int length = (buffer[i++] & 0x1F);
memcpy(&fname[0], &buffer[i], length);
fname[length] = '\0';
i += length;
std::ifstream t;
int fs;
t.open(fname, std::ios::binary);
std::vector<char> vec((
std::istreambuf_iterator<char>(t)),
(std::istreambuf_iterator<char>()));// open input file
t.close();
fs = vec.size();
char* filedata = (char*)malloc(fs*sizeof(char)); // allocate memory for a buffer of appropriate dimension
filedata = &vec[0];
filedata[fs] = '\0';
i = 0;
unsigned char* repbuffer = (unsigned char*)malloc(3 + length + fs);
repbuffer[i] = (unsigned char)(type & 0x07);
repbuffer[i] = repbuffer[i] << 5;
repbuffer[i] = repbuffer[i] | (length & 0x0000003F);
i++;
memcpy(&repbuffer[i], fname, length);
i = i + length;
printf("sizeof fs=%d", sizeof(fs));
repbuffer[i++] = (unsigned char)((fs & 0xff00) >> 8);
repbuffer[i++] = (unsigned char)(fs & 0xff);
memcpy(&repbuffer[i], filedata, fs);
printf("sizeof buffer=%d", sizeof(repbuffer));
i = i + fs;
// printf("the buffer contains %s\n",&repbuffer[11]);
if (send(connectedSock, (char*)repbuffer, i, 0) == -1)
{
printf("A local error was detected while sending data! (errno = %d: %s)\n", errno, strerror(errno));
return -1;
}
}
else if (type == 2)
{
char fname[100],nfname[100];
int i = 0;
int length = (buffer[i++] & 0x1F);
memcpy(&fname[0], &buffer[i], length);
fname[length] = '\0';
i += length;
int nlength = (buffer[i++] & 0x1F);
memcpy(&nfname[0], &buffer[i], nlength);
nfname[nlength] = '\0';
rename(fname,nfname);
}
else if (type == 3)
{
char rep[32];
strcpy(rep, "bye change get help put");
int length = strlen(rep);
unsigned char* repbuffer = (unsigned char*)malloc(1 + length);
int type = 6;
repbuffer[0] = (unsigned char)(type & 0x07);
repbuffer[0] = repbuffer[0] << 5;
repbuffer[0] = repbuffer[0] | (length & 0x0000003F);
memcpy(&repbuffer[1], rep, length);
if (send(connectedSock, (char*)repbuffer, length+1, 0) == -1)
{
perror("A local error was detected while sending data!!");
return -1;
}
}
else if (type == 4)
{
closesocket(connectedSock);
}
break;
}
}
}
}
closesocket(passiveSock);
WSACleanup();
return 0;
}
I feel there's something wrong with the usage of FD_ISSET() method, I've been trying to figure out the error for 2 hours now, pleeassse help

C/PCAP : ARP packet values all 0

I'm trying to improve my C/network knowledge implementing a ARP spoofing tool with Pcap library.
I'm stuck with sniffing arp packets. I can detect the ARP type in the ethertype field of Ethernet frame. But when I "read" the ARP packet, all values are 0 (null) but hardware addr(MAC) & protocol address(ip) are very weird 8 byte number repeated (like 20e54ef12:20e54ef12:20e54ef12...). I just can't figure it out.
Here is what I've done so far :
packet_struct.h (the different structures used for eth, arp, ip...)
#ifndef DEF_PACKET_STRUCT
#define DEF_PACKET_STRUCT
#include <sys/types.h>
#define BUFF_SIZE 1518
#define ETH_SIZE 14
#define ARP_SIZE 28
/* in bytes */
#define ETH_ADDR_SIZE 6
#define IP_ADDR_SIZE 4
typedef struct pkt_eth {
unsigned char dest[ETH_ADDR_SIZE];
unsigned char src[ETH_ADDR_SIZE];
unsigned short type;
} pkt_eth;
#define ETHERTYPE_ARP 0x0806
#define ARP_REQUEST 1
#define ARP_REPLY 2
typedef struct pkt_arp {
unsigned short htype;/* hardware type => ethernet , etc */
unsigned short ptype; /*protocol type => ipv4 or ipv6 */
unsigned char hard_addr_len; /* usually 6 bytes for ethernet */
unsigned char proto_addr_len; /*usually 8 bytes for ipv4 */
unsigned short opcode; /* type of arp */
unsigned char hard_addr_send[ETH_ADDR_SIZE];
unsigned char proto_addr_send[IP_ADDR_SIZE];
unsigned char hard_addr_dest[ETH_ADDR_SIZE];
unsigned char proto_addr_dest[IP_ADDR_SIZE];
} pkt_arp;
#define ETHERTYPE_IP 0x0800
typedef struct pkt_ip {
unsigned char vhl;
unsigned char tos;
unsigned short len;
unsigned short id;
unsigned short off;
unsigned char ttl;
unsigned char proto;
unsigned short crc;
unsigned int addr_src;
unsigned int addr_dest;
} pkt_ip;
#endif
packet_print.c (utilities to print packet information )
#include "packet_struct.h"
#include <stdio.h>
#include <stdlib.h>
char * to_addr(unsigned char * addr, int length) {
int i = 0;
char string[length];
for(i=0; i< length; i++)
sprintf(string,"%02x:",addr[i]);
return string;
}
void print_pkt_eth(pkt_eth * eth) {
int i = 0;
fprintf(stdout,"Ethernet Layer \n");
fprintf(stdout,"\tSource:\t");
for(i=0;i<ETH_ADDR_SIZE;i++)
fprintf(stdout,"%02x:",eth->src[i]);
//fprintf(stdout,"%s",to_addr(eth->src,ETH_ADDR_SIZE));
fprintf(stdout,"\n\tDest:\t");
for(i=0;i<ETH_ADDR_SIZE;i++)
fprintf(stdout,"%02X:",eth->dest[i]);
if(ntohs(eth->type) == ETHERTYPE_IP)
fprintf(stdout,"\n\tType:\t IPv4");
else if(ntohs(eth->type) == ETHERTYPE_ARP)
fprintf(stdout,"\n\tType:\t ARP");
printf("\n");
}
void print_pkt_arp(pkt_arp * arp) {
int op = 0;
int i = 0;
printf("ARP Layer \n");
printf("\tHardware type:\t%02d\n",arp->htype);
printf("\tProtocol type:\t%02d\n",arp->ptype);
printf("\tHardware addresses length:\t%01d\n",arp->hard_addr_len);
printf("\tProtocol addresses length:\t%01d\n",arp->proto_addr_len);
op = ntohs(arp->opcode);
printf("\tOperation code:\t%01u\n",op);
printf("\tHardware sender:\t");
for(i=0;i<ETH_ADDR_SIZE;i++)
printf("%02x:",arp->hard_addr_send);
printf("\n\tSoftware sender:\t");
for(i=0;i<IP_ADDR_SIZE;i++)
printf("%02x:",arp->proto_addr_send);
printf("\n");
}
void print_pkt_ip(pkt_ip * ip) {
}
sniffer.c ( the tool itself )
#include<stdio.h>
#include<stdlib.h>
#include<netinet/in.h> // for addresses translation
#include<errno.h>
// for ntohs etc
// can also be necessary to include netinet/in
#include <arpa/inet.h>
#include "packet_struct.h"
#include <pcap.h>
#define SNAP_LEN 1518
int packet_count = 0;
void handleARP(const struct pkt_eth * eth) {
const struct pkt_arp * arp = (const struct pkt_arp *) (eth + ETH_SIZE);
print_pkt_arp(arp);
if(ntohs(arp->htype) != 1) {
fprintf(stderr, "Error : ARP packet does not contain a Hardware type Ethernet -> %d\n",ntohs(arp->htype));
return;
}
// check protocol type
if(ntohs(arp->ptype) != 0x800) {
fprintf(stderr,"Error : ARP packet does not contain a IPv4 type\n");
return;
}
}
void sniff_callback(u_char * user, const struct pcap_pkthdr * h,const u_char * bytes) {
int i = 0;
for(i=0; i < 25; i++) { printf("-"); }; printf("\n");
printf("Received packet number %d ==> %d\n",packet_count++,h->len);
const struct pkt_eth * eth;
unsigned short eth_type;
unsigned int captureLength = h->caplen;
unsigned int packetLength = h->len;
if(captureLength != packetLength) {
fprintf(stderr,"Error : received packet with %d available instead of %d \n",captureLength,packetLength);
return;
}
if(captureLength < ETH_SIZE) {
fprintf(stderr,"Error : received too small packet , %d bytes",captureLength);
return;
}
eth = (struct pkt_eth*)(bytes);
// print the packet
print_pkt_eth(eth);
eth_type = ntohs(eth->type);
if(eth_type == ETHERTYPE_ARP) {
handleARP(eth);
}
for(i=0; i < 25; i++) { printf("-"); }; printf("\n");
return;
}
/* returns 0 if everything went well */
int set_options(pcap_t * handle) {
int ret = 0;
ret = pcap_set_promisc(handle,1);
if(ret != 0) {
fprintf(stderr,"Error setting promiscuous mode\n");
return ret;
}
ret = pcap_set_snaplen(handle,SNAP_LEN);
if(ret != 0) {
fprintf(stderr,"Error setting snapshot length\n");
return ret;
}
ret = pcap_set_timeout(handle,1000);
if(ret != 0) {
fprintf(stderr,"Error setting timeout\n");
return ret;
}
return ret;
}
int activate(pcap_t * handle) {
int ret = pcap_activate(handle);
switch(ret) {
case 0:
fprintf(stdout,"Activation complete\n");
break;
case PCAP_WARNING_PROMISC_NOTSUP:
fprintf(stderr,"Promiscuous mode not supported\n");
return ret;
case PCAP_ERROR_PERM_DENIED:
fprintf(stderr,"Not have the permission required\n");
return ret;
case PCAP_ERROR_PROMISC_PERM_DENIED:
fprintf(stderr,"Not have the permission required for promiscuous\n");
return ret;
default:
fprintf(stderr,"Error occured during activation, see code\n");
return ret;
}
return ret;
}
/* Will activate device , filter & call the sniffing loop */
int sniffing_method(char * interface, char * filter,int packet_count) {
char err[PCAP_ERRBUF_SIZE]; //error buffer
pcap_t * handle; // handler of the interface by pcap
struct bpf_program bpf;
bpf_u_int32 mask; // network mask
bpf_u_int32 ip; // network ip
struct in_addr addr; // network number
int ret;
/* get mask & ip */
if(pcap_lookupnet(interface, &ip, &mask, err) == -1) {
fprintf(stderr, "Couldn't get netmask for device %s: %s\n",interface,err);
exit(EXIT_FAILURE);
}
handle = pcap_create(interface,err);
if (handle == NULL) {
fprintf(stderr,"Error pcap_create() : %s \n",err);
exit(EXIT_FAILURE);
}
if(set_options(handle) != 0) {
fprintf(stderr,"Exiting\n");
exit(EXIT_FAILURE);
}
if (activate(handle) != 0) {
fprintf(stderr,"Exiting\n");
exit(EXIT_FAILURE);
}
/* FILTER PART */
if(filter != NULL) {
if(pcap_compile(handle,&bpf,filter,0,ip) == -1){
fprintf(stderr,"Couldn't compile filter expr %s : %s\n",filter,pcap_geterr(handle));
exit(EXIT_FAILURE);
}
if(pcap_setfilter(handle, &bpf) == -1) {
fprintf(stderr,"Couldn't install filter %s : %s\n",filter,pcap_geterr(handle));
exit(EXIT_FAILURE);
}
}
/* SNIFF starts */
printf("Sniffing starting on %s ...\n",interface);
pcap_loop(handle,packet_count,sniff_callback,NULL);
pcap_freecode(&bpf);
pcap_close(handle);
return EXIT_SUCCESS;
}
void usage() {
printf("sniff interface [filter] [count]");
printf("interface is the interface you want to listen on. It will try to put it in monitor mode");
printf("filter can be a filter for libpcap to apply for packets it reads");
}
int main(int argc, char * argv[])
{
int i = 0; // counter
int ret;
char * default_filter = "ip";
char * filter;
int pcount = -1; //take all packet by defaults
char * interface;
if(argc < 2) {
fprintf(stderr, "No interfaces specified in arguments\n");
usage();
exit(EXIT_FAILURE);
}
// take command line filter
if(argc > 2) {
filter = argv[2];
} else {
filter = default_filter;
}
// take command line packet count limit
if(argc > 3) {
pcount = atoi(argv[3]);
}
fprintf(stdout,"Args : ");
for(i = 0; i < argc; i++) {
fprintf(stdout,"\t%s",argv[i]);
}
printf("\n");
interface = argv[1];
sniffing_method(interface,filter,pcount);
}
And here is one output (all tries giving the same output anyway)
Received packet number 2 ==> 42
Ethernet Layer
Source: 00:ee:bd:aa:f4:98:
Dest: FF:FF:FF:FF:FF:FF:
Type: ARP
ARP Layer
Hardware type: 00
Protocol type: 00
Hardware addresses length: 0
Protocol addresses length: 0
Operation code: 0
Hardware sender: 20e9a152:20e9a152:20e9a152:20e9a152:20e9a152:20e9a152:
Software sender: 20e9a158:20e9a158:20e9a158:20e9a158:
Error : ARP packet does not contain a Hardware type Ethernet -> 0
-------------------------
-------------------------
Received packet number 3 ==> 42
Ethernet Layer
Source: 00:ee:bd:aa:f4:98:
Dest: FF:FF:FF:FF:FF:FF:
Type: ARP
ARP Layer
Hardware type: 00
Protocol type: 00
Hardware addresses length: 0
Protocol addresses length: 0
Operation code: 0
Hardware sender: 20e5a152:20e5a152:20e5a152:20e5a152:20e5a152:20e5a152:
Software sender: 20e5a158:20e5a158:20e5a158:20e5a158:
This part is wrong:
void handleARP(const struct pkt_eth * eth) {
const struct pkt_arp * arp = (const struct pkt_arp *) (eth + ETH_SIZE);
Here you're passing in a struct pkt_eth*, to which you add ETH_SIZE. Pointer arithmetic advances to the next element, not to the next byte. You're essentially looking sizeof(struct pkt_eth) * ETH_SIZE bytes past the pointer passed in.
You should just do
const struct pkt_arp * arp = (const struct pkt_arp *) (eth + 1);
(Or pass in an unsigned char * that already starts at the layer you want to decode.)

Resources