Why is my non blocking raw sockets program running so slowly? - c

I have a program which uses PF_PACKET raw sockets to send TCP SYN packets to a list of web servers. The program reads in a file which has an IPv4 address on each line of a web server. The program is the beginnings of an attempt to connect to multiple servers in a high performance manner. However, currently the program is only sending about 10 packets/second. This despite the program using non blocking socket. It should be running orders of magnitude faster. Any ideas why it could be running so slowly.
I include a full code listing below. Warning - the code is quite long. That's because it takes a surprisingly large amount of code to get the IP and MAC address of the gateway router. The good news is you can skip all the functions before main because they just do the necessary work of getting the IP and MAC address of the router as well as the local IP address. Anyway, here's the code:
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/tcp.h> //Provides declarations for tcp header
#include <netinet/ip.h> //Provides declarations for ip header
#include <netinet/ether.h>
#include <ifaddrs.h>
#include <asm/types.h>
#include <linux/if_ether.h>
//#include <linux/if_arp.h>
#include <arpa/inet.h> //htons etc
#include <time.h>
#include <linux/rtnetlink.h>
#include <sys/resource.h>
#define PROTO_ARP 0x0806
#define ETH2_HEADER_LEN 14
#define HW_TYPE 1
#define MAC_LENGTH 6
#define IPV4_LENGTH 4
#define ARP_REQUEST 0x01
#define ARP_REPLY 0x02
#define BUF_SIZE 60
#define MAX_CONNECTIONS 10000
#define debug(x...) printf(x);printf("\n");
#define info(x...) printf(x);printf("\n");
#define warn(x...) printf(x);printf("\n");
#define err(x...) printf(x);printf("\n");
static char * str_devname= NULL;
static int mode_loss = 0;
static int c_packet_sz = 150;
static int c_buffer_sz = 1024*8;
static int c_buffer_nb = 1024;
static int c_sndbuf_sz = 0;
static int c_send_mask = 127;
static int c_error = 0;
static int c_mtu = 0;
static int mode_thread = 0;
volatile int fd_socket;
volatile int data_offset = 0;
volatile struct tpacket_hdr * ps_header_start;
volatile struct sockaddr_ll *ps_sockaddr = NULL;
volatile int shutdown_flag = 0;
int done = 0;
struct tpacket_req s_packet_req;
unsigned char buffer[BUF_SIZE];
struct arp_header *arp_resp = (struct arp_header *) (buffer + ETH2_HEADER_LEN);
char ifname[512];
char ip[512];
/*
96 bit (12 bytes) pseudo header needed for tcp header checksum calculation
*/
struct pseudo_header
{
u_int32_t source_address;
u_int32_t dest_address;
u_int8_t placeholder;
u_int8_t protocol;
u_int16_t tcp_length;
};
struct arp_header {
unsigned short hardware_type;
unsigned short protocol_type;
unsigned char hardware_len;
unsigned char protocol_len;
unsigned short opcode;
unsigned char sender_mac[MAC_LENGTH];
unsigned char sender_ip[IPV4_LENGTH];
unsigned char target_mac[MAC_LENGTH];
unsigned char target_ip[IPV4_LENGTH];
};
int rtnl_receive(int fd, struct msghdr *msg, int flags)
{
int len;
do {
len = recvmsg(fd, msg, flags);
} while (len < 0 && (errno == EINTR || errno == EAGAIN));
if (len < 0) {
perror("Netlink receive failed");
return -errno;
}
if (len == 0) {
perror("EOF on netlink");
return -ENODATA;
}
return len;
}
static int rtnl_recvmsg(int fd, struct msghdr *msg, char **answer)
{
struct iovec *iov = msg->msg_iov;
char *buf;
int len;
iov->iov_base = NULL;
iov->iov_len = 0;
len = rtnl_receive(fd, msg, MSG_PEEK | MSG_TRUNC);
if (len < 0) {
return len;
}
buf = malloc(len);
if (!buf) {
perror("malloc failed");
return -ENOMEM;
}
iov->iov_base = buf;
iov->iov_len = len;
len = rtnl_receive(fd, msg, 0);
if (len < 0) {
free(buf);
return len;
}
*answer = buf;
return len;
}
void parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
{
memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
while (RTA_OK(rta, len)) {
if (rta->rta_type <= max) {
tb[rta->rta_type] = rta;
}
rta = RTA_NEXT(rta,len);
}
}
static inline int rtm_get_table(struct rtmsg *r, struct rtattr **tb)
{
__u32 table = r->rtm_table;
if (tb[RTA_TABLE]) {
table = *(__u32 *)RTA_DATA(tb[RTA_TABLE]);
}
return table;
}
void print_route(struct nlmsghdr* nl_header_answer)
{
struct rtmsg* r = NLMSG_DATA(nl_header_answer);
int len = nl_header_answer->nlmsg_len;
struct rtattr* tb[RTA_MAX+1];
int table;
char buf[256];
len -= NLMSG_LENGTH(sizeof(*r));
if (len < 0) {
perror("Wrong message length");
return;
}
parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
table = rtm_get_table(r, tb);
if (r->rtm_family != AF_INET && table != RT_TABLE_MAIN) {
return;
}
if (tb[RTA_DST]) {
if ((r->rtm_dst_len != 24) && (r->rtm_dst_len != 16)) {
return;
}
printf("%s/%u ", inet_ntop(r->rtm_family, RTA_DATA(tb[RTA_DST]), buf, sizeof(buf)), r->rtm_dst_len);
} else if (r->rtm_dst_len) {
printf("0/%u ", r->rtm_dst_len);
} else {
printf("default ");
}
if (tb[RTA_GATEWAY]) {
printf("via %s", inet_ntop(r->rtm_family, RTA_DATA(tb[RTA_GATEWAY]), buf, sizeof(buf)));
strcpy(ip, inet_ntop(r->rtm_family, RTA_DATA(tb[RTA_GATEWAY]), buf, sizeof(buf)));
}
if (tb[RTA_OIF]) {
char if_nam_buf[IF_NAMESIZE];
int ifidx = *(__u32 *)RTA_DATA(tb[RTA_OIF]);
printf(" dev %s", if_indextoname(ifidx, if_nam_buf));
}
if (tb[RTA_GATEWAY] && tb[RTA_OIF]) {
char if_nam_buf[IF_NAMESIZE];
int ifidx = *(__u32 *)RTA_DATA(tb[RTA_OIF]);
strcpy(ifname, if_indextoname(ifidx, if_nam_buf));
}
if (tb[RTA_SRC]) {
printf("src %s", inet_ntop(r->rtm_family, RTA_DATA(tb[RTA_SRC]), buf, sizeof(buf)));
}
printf("\n");
}
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));
saddr.nl_family = AF_NETLINK;
saddr.nl_pid = getpid();
if (bind(sock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
perror("Failed to bind to netlink socket");
close(sock);
return -1;
}
return sock;
}
int do_route_dump_requst(int sock)
{
struct {
struct nlmsghdr nlh;
struct rtmsg rtm;
} nl_request;
nl_request.nlh.nlmsg_type = RTM_GETROUTE;
nl_request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
nl_request.nlh.nlmsg_len = sizeof(nl_request);
nl_request.nlh.nlmsg_seq = time(NULL);
nl_request.rtm.rtm_family = AF_INET;
return send(sock, &nl_request, sizeof(nl_request), 0);
}
int get_route_dump_response(int sock)
{
struct sockaddr_nl nladdr;
struct iovec iov;
struct msghdr msg = {
.msg_name = &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1,
};
char *buf;
int dump_intr = 0;
int status = rtnl_recvmsg(sock, &msg, &buf);
struct nlmsghdr *h = (struct nlmsghdr *)buf;
int msglen = status;
printf("Main routing table IPv4\n");
while (NLMSG_OK(h, msglen)) {
if (h->nlmsg_flags & NLM_F_DUMP_INTR) {
fprintf(stderr, "Dump was interrupted\n");
free(buf);
return -1;
}
if (nladdr.nl_pid != 0) {
continue;
}
if (h->nlmsg_type == NLMSG_ERROR) {
perror("netlink reported error");
free(buf);
}
print_route(h);
h = NLMSG_NEXT(h, msglen);
}
free(buf);
return status;
}
/*
* Converts struct sockaddr with an IPv4 address to network byte order uin32_t.
* Returns 0 on success.
*/
int int_ip4(struct sockaddr *addr, uint32_t *ip)
{
if (addr->sa_family == AF_INET) {
struct sockaddr_in *i = (struct sockaddr_in *) addr;
*ip = i->sin_addr.s_addr;
return 0;
} else {
err("Not AF_INET");
return 1;
}
}
/*
* Formats sockaddr containing IPv4 address as human readable string.
* Returns 0 on success.
*/
int format_ip4(struct sockaddr *addr, char *out)
{
if (addr->sa_family == AF_INET) {
struct sockaddr_in *i = (struct sockaddr_in *) addr;
const char *ip = inet_ntoa(i->sin_addr);
if (!ip) {
return -2;
} else {
strcpy(out, ip);
return 0;
}
} else {
return -1;
}
}
/*
* Writes interface IPv4 address as network byte order to ip.
* Returns 0 on success.
*/
int get_if_ip4(int fd, const char *ifname, uint32_t *ip) {
int err = -1;
struct ifreq ifr;
memset(&ifr, 0, sizeof(struct ifreq));
if (strlen(ifname) > (IFNAMSIZ - 1)) {
err("Too long interface name");
goto out;
}
strcpy(ifr.ifr_name, ifname);
if (ioctl(fd, SIOCGIFADDR, &ifr) == -1) {
perror("SIOCGIFADDR");
goto out;
}
if (int_ip4(&ifr.ifr_addr, ip)) {
goto out;
}
err = 0;
out:
return err;
}
/*
* Sends an ARP who-has request to dst_ip
* on interface ifindex, using source mac src_mac and source ip src_ip.
*/
int send_arp(int fd, int ifindex, const unsigned char *src_mac, uint32_t src_ip, uint32_t dst_ip)
{
int err = -1;
unsigned char buffer[BUF_SIZE];
memset(buffer, 0, sizeof(buffer));
struct sockaddr_ll socket_address;
socket_address.sll_family = AF_PACKET;
socket_address.sll_protocol = htons(ETH_P_ARP);
socket_address.sll_ifindex = ifindex;
socket_address.sll_hatype = htons(ARPHRD_ETHER);
socket_address.sll_pkttype = (PACKET_BROADCAST);
socket_address.sll_halen = MAC_LENGTH;
socket_address.sll_addr[6] = 0x00;
socket_address.sll_addr[7] = 0x00;
struct ethhdr *send_req = (struct ethhdr *) buffer;
struct arp_header *arp_req = (struct arp_header *) (buffer + ETH2_HEADER_LEN);
int index;
ssize_t ret, length = 0;
//Broadcast
memset(send_req->h_dest, 0xff, MAC_LENGTH);
//Target MAC zero
memset(arp_req->target_mac, 0x00, MAC_LENGTH);
//Set source mac to our MAC address
memcpy(send_req->h_source, src_mac, MAC_LENGTH);
memcpy(arp_req->sender_mac, src_mac, MAC_LENGTH);
memcpy(socket_address.sll_addr, src_mac, MAC_LENGTH);
/* Setting protocol of the packet */
send_req->h_proto = htons(ETH_P_ARP);
/* Creating ARP request */
arp_req->hardware_type = htons(HW_TYPE);
arp_req->protocol_type = htons(ETH_P_IP);
arp_req->hardware_len = MAC_LENGTH;
arp_req->protocol_len = IPV4_LENGTH;
arp_req->opcode = htons(ARP_REQUEST);
debug("Copy IP address to arp_req");
memcpy(arp_req->sender_ip, &src_ip, sizeof(uint32_t));
memcpy(arp_req->target_ip, &dst_ip, sizeof(uint32_t));
ret = sendto(fd, buffer, 42, 0, (struct sockaddr *) &socket_address, sizeof(socket_address));
if (ret == -1) {
perror("sendto():");
goto out;
}
err = 0;
out:
return err;
}
/*
* Gets interface information by name:
* IPv4
* MAC
* ifindex
*/
int get_if_info(const char *ifname, uint32_t *ip, char *mac, int *ifindex)
{
debug("get_if_info for %s", ifname);
int err = -1;
struct ifreq ifr;
int sd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
if (sd <= 0) {
perror("socket()");
goto out;
}
if (strlen(ifname) > (IFNAMSIZ - 1)) {
printf("Too long interface name, MAX=%i\n", IFNAMSIZ - 1);
goto out;
}
strcpy(ifr.ifr_name, ifname);
//Get interface index using name
if (ioctl(sd, SIOCGIFINDEX, &ifr) == -1) {
perror("SIOCGIFINDEX");
goto out;
}
*ifindex = ifr.ifr_ifindex;
printf("interface index is %d\n", *ifindex);
//Get MAC address of the interface
if (ioctl(sd, SIOCGIFHWADDR, &ifr) == -1) {
perror("SIOCGIFINDEX");
goto out;
}
//Copy mac address to output
memcpy(mac, ifr.ifr_hwaddr.sa_data, MAC_LENGTH);
if (get_if_ip4(sd, ifname, ip)) {
goto out;
}
debug("get_if_info OK");
err = 0;
out:
if (sd > 0) {
debug("Clean up temporary socket");
close(sd);
}
return err;
}
/*
* Creates a raw socket that listens for ARP traffic on specific ifindex.
* Writes out the socket's FD.
* Return 0 on success.
*/
int bind_arp(int ifindex, int *fd)
{
debug("bind_arp: ifindex=%i", ifindex);
int ret = -1;
// Submit request for a raw socket descriptor.
*fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
if (*fd < 1) {
perror("socket()");
goto out;
}
debug("Binding to ifindex %i", ifindex);
struct sockaddr_ll sll;
memset(&sll, 0, sizeof(struct sockaddr_ll));
sll.sll_family = AF_PACKET;
sll.sll_ifindex = ifindex;
if (bind(*fd, (struct sockaddr*) &sll, sizeof(struct sockaddr_ll)) < 0) {
perror("bind");
goto out;
}
ret = 0;
out:
if (ret && *fd > 0) {
debug("Cleanup socket");
close(*fd);
}
return ret;
}
/*
* Reads a single ARP reply from fd.
* Return 0 on success.
*/
int read_arp(int fd)
{
debug("read_arp");
int ret = -1;
ssize_t length = recvfrom(fd, buffer, BUF_SIZE, 0, NULL, NULL);
int index;
if (length == -1) {
perror("recvfrom()");
goto out;
}
struct ethhdr *rcv_resp = (struct ethhdr *) buffer;
if (ntohs(rcv_resp->h_proto) != PROTO_ARP) {
debug("Not an ARP packet");
goto out;
}
if (ntohs(arp_resp->opcode) != ARP_REPLY) {
debug("Not an ARP reply");
goto out;
}
debug("received ARP len=%ld", length);
struct in_addr sender_a;
memset(&sender_a, 0, sizeof(struct in_addr));
memcpy(&sender_a.s_addr, arp_resp->sender_ip, sizeof(uint32_t));
debug("Sender IP: %s", inet_ntoa(sender_a));
debug("Sender MAC: %02X:%02X:%02X:%02X:%02X:%02X",
arp_resp->sender_mac[0],
arp_resp->sender_mac[1],
arp_resp->sender_mac[2],
arp_resp->sender_mac[3],
arp_resp->sender_mac[4],
arp_resp->sender_mac[5]);
ret = 0;
out:
return ret;
}
/*
*
* Sample code that sends an ARP who-has request on
* interface <ifname> to IPv4 address <ip>.
* Returns 0 on success.
*/
int test_arping(const char *ifname, const char *ip) {
int ret = -1;
uint32_t dst = inet_addr(ip);
if (dst == 0 || dst == 0xffffffff) {
printf("Invalid source IP\n");
return 1;
}
int src;
int ifindex;
char mac[MAC_LENGTH];
if (get_if_info(ifname, &src, mac, &ifindex)) {
err("get_if_info failed, interface %s not found or no IP set?", ifname);
goto out;
}
int arp_fd;
if (bind_arp(ifindex, &arp_fd)) {
err("Failed to bind_arp()");
goto out;
}
if (send_arp(arp_fd, ifindex, mac, src, dst)) {
err("Failed to send_arp");
goto out;
}
while(1) {
int r = read_arp(arp_fd);
if (r == 0) {
info("Got reply, break out");
break;
}
}
ret = 0;
out:
if (arp_fd) {
close(arp_fd);
arp_fd = 0;
}
return ret;
}
unsigned short checksum2(const char *buf, unsigned size)
{
unsigned long long sum = 0;
const unsigned long long *b = (unsigned long long *) buf;
unsigned t1, t2;
unsigned short t3, t4;
/* Main loop - 8 bytes at a time */
while (size >= sizeof(unsigned long long))
{
unsigned long long s = *b++;
sum += s;
if (sum < s) sum++;
size -= 8;
}
/* Handle tail less than 8-bytes long */
buf = (const char *) b;
if (size & 4)
{
unsigned s = *(unsigned *)buf;
sum += s;
if (sum < s) sum++;
buf += 4;
}
if (size & 2)
{
unsigned short s = *(unsigned short *) buf;
sum += s;
if (sum < s) sum++;
buf += 2;
}
if (size)
{
unsigned char s = *(unsigned char *) buf;
sum += s;
if (sum < s) sum++;
}
/* Fold down to 16 bits */
t1 = sum;
t2 = sum >> 32;
t1 += t2;
if (t1 < t2) t1++;
t3 = t1;
t4 = t1 >> 16;
t3 += t4;
if (t3 < t4) t3++;
return ~t3;
}
int main( int argc, char ** argv )
{
uint32_t size;
size_t len;
struct sockaddr_ll my_addr, peer_addr;
int i_ifindex;
int ec;
struct ifreq s_ifr; /* points to one interface returned from ioctl */
int tmp;
FILE * fp;
char server[254];
int count = 0;
int first_time = 1;
int z;
int first_mmap = 1;
#define HWADDR_len 6
#define IP_len 4
int s,s2,i;
struct ifreq ifr,ifr2;
int ret = -1;
struct rlimit lim;
if (argc != 2) {
printf("Usage: %s <INPUT_FILE>\n", argv[0]);
return 1;
}
getrlimit(RLIMIT_NOFILE, &lim);
printf("Soft: %d Hard: %d\n", (int)lim.rlim_cur, (int)lim.rlim_max);
lim.rlim_cur = lim.rlim_max;
if (setrlimit(RLIMIT_NOFILE, &lim) == -1) {
printf("rlimit failed\n");
return -1;
}
getrlimit(RLIMIT_NOFILE, &lim);
printf("New Soft: %d New Hard: %d\n", (int)lim.rlim_cur, (int)lim.rlim_max);
int nl_sock = open_netlink();
if (do_route_dump_requst(nl_sock) < 0) {
perror("Failed to perfom request");
close(nl_sock);
return -1;
}
get_route_dump_response(nl_sock);
close (nl_sock);
test_arping(ifname, ip);
s = socket(AF_INET, SOCK_DGRAM, 0);
s2 = socket(AF_INET, SOCK_DGRAM, 0);
strcpy(ifr.ifr_name, ifname);
strcpy(ifr2.ifr_name, ifname);
ioctl(s, SIOCGIFHWADDR, &ifr);
ioctl(s2, SIOCGIFADDR, &ifr2);
struct sockaddr_in* ipaddr = (struct sockaddr_in*)&ifr2.ifr_addr;
close(s);
fp = fopen(argv[1], "r");
if (!fp)
exit(EXIT_FAILURE);
while (!done)
{
fd_socket = socket(PF_PACKET, SOCK_RAW|SOCK_NONBLOCK, htons(ETH_P_ALL));
if(fd_socket == -1)
{
perror("socket");
return EXIT_FAILURE;
}
/* clear structure */
memset(&my_addr, 0, sizeof(struct sockaddr_ll));
my_addr.sll_family = PF_PACKET;
my_addr.sll_protocol = htons(ETH_P_ALL);
str_devname = ifname;
//strcpy (str_devname, ifname);
/* initialize interface struct */
strncpy (s_ifr.ifr_name, str_devname, sizeof(s_ifr.ifr_name));
/* Get the broad cast address */
ec = ioctl(fd_socket, SIOCGIFINDEX, &s_ifr);
if(ec == -1)
{
perror("iotcl");
return EXIT_FAILURE;
}
/* update with interface index */
i_ifindex = s_ifr.ifr_ifindex;
s_ifr.ifr_mtu = 7200;
/* update the mtu through ioctl */
ec = ioctl(fd_socket, SIOCSIFMTU, &s_ifr);
if(ec == -1)
{
perror("iotcl");
return EXIT_FAILURE;
}
/* set sockaddr info */
memset(&my_addr, 0, sizeof(struct sockaddr_ll));
my_addr.sll_family = AF_PACKET;
my_addr.sll_protocol = ETH_P_ALL;
my_addr.sll_ifindex = i_ifindex;
/* bind port */
if (bind(fd_socket, (struct sockaddr *)&my_addr, sizeof(struct sockaddr_ll)) == -1)
{
perror("bind");
return EXIT_FAILURE;
}
/* prepare Tx ring request */
s_packet_req.tp_block_size = c_buffer_sz;
s_packet_req.tp_frame_size = c_buffer_sz;
s_packet_req.tp_block_nr = c_buffer_nb;
s_packet_req.tp_frame_nr = c_buffer_nb;
/* calculate memory to mmap in the kernel */
size = s_packet_req.tp_block_size * s_packet_req.tp_block_nr;
/* set packet loss option */
tmp = mode_loss;
if (setsockopt(fd_socket, SOL_PACKET, PACKET_LOSS, (char *)&tmp, sizeof(tmp))<0)
{
perror("setsockopt: PACKET_LOSS");
return EXIT_FAILURE;
}
/* send TX ring request */
if (setsockopt(fd_socket, SOL_PACKET, PACKET_TX_RING, (char *)&s_packet_req, sizeof(s_packet_req))<0)
{
perror("setsockopt: PACKET_TX_RING");
return EXIT_FAILURE;
}
/* change send buffer size */
if(c_sndbuf_sz) {
printf("send buff size = %d\n", c_sndbuf_sz);
if (setsockopt(fd_socket, SOL_SOCKET, SO_SNDBUF, &c_sndbuf_sz, sizeof(c_sndbuf_sz))< 0)
{
perror("getsockopt: SO_SNDBUF");
return EXIT_FAILURE;
}
}
/* get data offset */
data_offset = TPACKET_HDRLEN - sizeof(struct sockaddr_ll);
/* mmap Tx ring buffers memory */
ps_header_start = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd_socket, 0);
if (ps_header_start == (void*)-1)
{
perror("mmap");
return EXIT_FAILURE;
}
int i,j;
int i_index = 0;
char * data;
int first_loop = 1;
struct tpacket_hdr * ps_header;
int ec_send = 0;
int i_index_start = i_index;
ps_header = ((struct tpacket_hdr *)((void *)ps_header_start + (c_buffer_sz*i_index)));
data = ((void*) ps_header) + data_offset;
//Datagram to represent the packet
char datagram[4096] , source_ip[32] , *pseudogram;
//zero out the packet buffer
memset (datagram, 0, 4096);
//Ethernet header
struct ether_header *eh = (struct ether_header *) datagram;
//IP header
struct iphdr *iph = (struct iphdr *) (datagram + sizeof (struct ether_header));
//TCP header
struct tcphdr *tcph = (struct tcphdr *) (datagram + sizeof (struct ether_header) + sizeof (struct ip));
struct sockaddr_in sin;
struct pseudo_header psh;
//some address resolution
strcpy(source_ip , inet_ntoa(ipaddr->sin_addr));
sin.sin_family = AF_INET;
sin.sin_port = htons(80);
if (fscanf(fp, "%253s", server) == 1)
sin.sin_addr.s_addr = inet_addr (server);
else
{
done = 1;
break;
}
//Fill in the Ethernet Header
eh->ether_dhost[0] = arp_resp->sender_mac[0];
eh->ether_dhost[1] = arp_resp->sender_mac[1];
eh->ether_dhost[2] = arp_resp->sender_mac[2];
eh->ether_dhost[3] = arp_resp->sender_mac[3];
eh->ether_dhost[4] = arp_resp->sender_mac[4];
eh->ether_dhost[5] = arp_resp->sender_mac[5];
memcpy(eh->ether_shost, ifr.ifr_hwaddr.sa_data, HWADDR_len);
eh->ether_type = htons(0x0800);
//Fill in the IP Header
iph->ihl = 5;
iph->version = 4;
iph->tos = 0;
iph->tot_len = htons(sizeof (struct iphdr) + sizeof (struct tcphdr));
iph->id = htons (54321); //Id of this packet
iph->frag_off = 0;
iph->ttl = 255;
iph->protocol = IPPROTO_TCP;
iph->check = 0; //Set to 0 before calculating checksum
iph->saddr = inet_addr ( source_ip );
iph->daddr = sin.sin_addr.s_addr;
//Ip checksum
iph->check = checksum2 (datagram + sizeof (struct ether_header), sizeof (struct iphdr));
//TCP Header
tcph->source = htons (1234);
tcph->dest = htons (80);
tcph->seq = 0;
tcph->ack_seq = 0;
tcph->doff = 5; //tcp header size
tcph->fin=0;
tcph->syn=1;
tcph->rst=0;
tcph->psh=0;
tcph->ack=0;
tcph->urg=0;
tcph->window = htons (5840); // maximum allowed window size
tcph->check = 0; //leave checksum 0 now, filled later by pseudo header
tcph->urg_ptr = 0;
//Now the TCP checksum
psh.source_address = inet_addr( source_ip );
psh.dest_address = sin.sin_addr.s_addr;
psh.placeholder = 0;
psh.protocol = IPPROTO_TCP;
psh.tcp_length = htons(sizeof(struct tcphdr));
int psize = sizeof(struct pseudo_header) + sizeof(struct tcphdr);
pseudogram = malloc(psize);
memcpy(pseudogram , (char*) &psh , sizeof (struct pseudo_header));
memcpy(pseudogram + sizeof(struct pseudo_header) , tcph , sizeof(struct tcphdr));
tcph->check = checksum2(pseudogram , psize);
memcpy(data, datagram, (sizeof(struct ether_header) + sizeof(struct iphdr) + sizeof(struct tcphdr)));
free(pseudogram);
len = sizeof(struct ether_header) + sizeof(struct iphdr) + sizeof(struct tcphdr);
i_index ++;
if(i_index >= c_buffer_nb)
{
i_index = 0;
first_loop = 0;
}
/* update packet len */
//ps_header->tp_len = c_packet_sz;
ps_header->tp_len = len;
/* set header flag to USER (trigs xmit)*/
ps_header->tp_status = TP_STATUS_SEND_REQUEST;
//int ec_send;
static int total=0;
//int blocking = 1;
/* send all buffers with TP_STATUS_SEND_REQUEST */
/* Wait end of transfer */
//ec_send = sendto(fd_socket,NULL,0,(blocking? 0 : MSG_DONTWAIT),(struct sockaddr *) ps_sockaddr,sizeof(struct sockaddr_ll));
ec_send = sendto(fd_socket,NULL,len,MSG_DONTWAIT,(struct sockaddr *) ps_sockaddr,sizeof(struct sockaddr_ll));
if(ec_send < 0) {
perror("sendto");
}
else if ( ec_send == 0 ) {
/* nothing to do => schedule : useful if no SMP */
printf("Sleeping\n");
usleep(0);
}
else {
total += ec_send/(len);
printf("send %d packets (+%d bytes)\n",total, ec_send);
fflush(0);
}
//ps_header_start = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd_socket, 0);
if (munmap(ps_header_start, size) == -1)
{
perror("munmap");
exit(EXIT_FAILURE);
}
close(fd_socket);
}
return 1;
}
Here is the output of strace -c for just over 5,000 packets sent:
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
48.11 3.962165 395 10012 setsockopt
16.69 1.374748 274 5014 mmap
14.85 1.222565 244 5007 munmap
10.91 0.898695 179 5016 close
3.15 0.259055 25 10022 ioctl
2.04 0.167613 33 5016 socket
1.70 0.139623 27 5008 sendto
1.41 0.116430 23 5025 write
1.14 0.093826 18 5008 bind
0.01 0.000505 26 19 read
0.00 0.000000 0 4 mprotect
0.00 0.000000 0 3 brk
0.00 0.000000 0 4 pread64
0.00 0.000000 0 3 1 access
0.00 0.000000 0 1 getpid
0.00 0.000000 0 1 recvfrom
0.00 0.000000 0 2 recvmsg
0.00 0.000000 0 1 execve
0.00 0.000000 0 2 1 arch_prctl
0.00 0.000000 0 1 set_tid_address
0.00 0.000000 0 3 openat
0.00 0.000000 0 4 newfstatat
0.00 0.000000 0 1 set_robust_list
0.00 0.000000 0 4 prlimit64
0.00 0.000000 0 1 getrandom
------ ----------- ----------- --------- --------- ----------------
100.00 8.235225 149 55182 2 total

If I follow the code correctly, you're redoing a ton of work for every IP address that doesn't need to be redone. Every time through the main loop you're:
creating a new packet socket
binding it
setting up a tx packet ring buffer
mmap'ing it
sending a single packet
unmapping
closing the socket
That's a huge amount of work you're causing the system to do for one packet.
You should only create one packet socket at the beginning, set up the tx buffer and mmap once, and leave it open until the program is done. You can send any number of packets through the interface without closing/re-opening.
This is why your top time users are setsockopt, mmap, unmap, etc. All of those operations are heavy in the kernel.
Also, the point of PACKET_TX_RING is that you can set up a large buffer and create one packet after another within the buffer without making a send system call for each packet. By using the packet header's tp_status field you're telling the kernel that this frame is ready to be sent. You then advance your pointer within the ring buffer to the next available slot and build another packet. When you have no more packets to build (or you've filled the available space in the buffer [i.e. wrapped around to your oldest still-in-flight frame]), you can then make one send/sendto call to tell the kernel to go look at your buffer and (start) sending all those packets.
You can then start building more packets (being careful to ensure they are not still in use by the kernel -- through the tp_status field).
That said, if this were a project I were doing, I would simplify a lot - at least for the first pass: create a packet socket, bind it to the interface, build packets one at a time, and use send once per frame (i.e. not bothering with PACKET_TX_RING). If (and only if) performance requirements are so tight that it needs to send faster would I bother setting up and using the ring buffer. I doubt you'll need that. This should go a ton faster without the excess setsockopt and mmap calls.
Finally, a non-blocking socket is only useful if you have something else to do while you're waiting. In this case, if you have the socket set to be non-blocking and the packet can't be sent because the call would block, the send call will fail and if you don't do something about that (enqueue the packet somewhere, and retry later, say), the packet will be lost. In this program, I can't see any benefit whatsoever to using a non-blocking socket. If the socket blocks, it's because the device transmit queue is full. After that, there's no point in you continuing to produce packets to be sent, you won't be able send those packets either. Much simpler to just block at that point until the queue drains.

Related

Why is recvfrom blocking forever with PF_PACKET socket?

The following code reads an IPv4 address from an input file which has an IPv4 address of a known web server on each line. It sends a TCP SYN with a PF_PACKET type socket. The main function launches two threads. One to send the TCP SYN packet and one thread to receive responses. The receive thread is blocking forever on recvfom call. This is not the desired behaviour. A screenshot of Wireshark capture shows that TCP SYN/ACK responses are being received by the machine:
So why isn't my program returning from recvfrom?
EDIT:
I include a cut down listing of the code below. MAC address and IP address and interface name are hard coded. You will have to change them to your setup to get the code to run.
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/tcp.h> //Provides declarations for tcp header
#include <netinet/ip.h> //Provides declarations for ip header
#include <netinet/ether.h>
#include <ifaddrs.h>
#include <asm/types.h>
#include <linux/if_ether.h>
//#include <linux/if_arp.h>
#include <arpa/inet.h> //htons etc
#include <time.h>
#include <linux/rtnetlink.h>
#include <sys/resource.h>
#include <pthread.h>
#define MAX_CONNECTIONS 10000
#define HWADDR_len 6
#define debug(x...) printf(x);printf("\n");
#define info(x...) printf(x);printf("\n");
#define warn(x...) printf(x);printf("\n");
#define err(x...) printf(x);printf("\n");
static char * str_devname= NULL;
volatile int fd_socket;
volatile struct sockaddr_ll *ps_sockaddr = NULL;
int done = 0;
char ifname[512];
char ip[512];
struct timespec startTime, stopTime;
long total_bytes = 0;
int sent = 0;
int lines_read = 0;
FILE * fp;
char server[254];
size_t len;
struct ifreq ifr,ifr2;
struct sockaddr_in* ipaddr = (struct sockaddr_in*)&ifr2.ifr_addr;
int count = 0;
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
/*
96 bit (12 bytes) pseudo header needed for tcp header checksum calculation
*/
struct pseudo_header
{
u_int32_t source_address;
u_int32_t dest_address;
u_int8_t placeholder;
u_int8_t protocol;
u_int16_t tcp_length;
};
unsigned short checksum2(const char *buf, unsigned size)
{
unsigned long long sum = 0;
const unsigned long long *b = (unsigned long long *) buf;
unsigned t1, t2;
unsigned short t3, t4;
/* Main loop - 8 bytes at a time */
while (size >= sizeof(unsigned long long))
{
unsigned long long s = *b++;
sum += s;
if (sum < s) sum++;
size -= 8;
}
/* Handle tail less than 8-bytes long */
buf = (const char *) b;
if (size & 4)
{
unsigned s = *(unsigned *)buf;
sum += s;
if (sum < s) sum++;
buf += 4;
}
if (size & 2)
{
unsigned short s = *(unsigned short *) buf;
sum += s;
if (sum < s) sum++;
buf += 2;
}
if (size)
{
unsigned char s = *(unsigned char *) buf;
sum += s;
if (sum < s) sum++;
}
/* Fold down to 16 bits */
t1 = sum;
t2 = sum >> 32;
t1 += t2;
if (t1 < t2) t1++;
t3 = t1;
t4 = t1 >> 16;
t3 += t4;
if (t3 < t4) t3++;
return ~t3;
}
void *my_send (){
while (!done)
{
if (count >= MAX_CONNECTIONS)
{
usleep(100);
continue;
}
char * data;
int first_loop = 1;
struct tpacket_hdr * ps_header;
int ec_send = 0;
//Datagram to represent the packet
char datagram[4096] , source_ip[32] , *pseudogram;
//zero out the packet buffer
memset (datagram, 0, 4096);
//Ethernet header
struct ether_header *eh = (struct ether_header *) datagram;
//IP header
struct iphdr *iph = (struct iphdr *) (datagram + sizeof (struct ether_header));
//TCP header
struct tcphdr *tcph = (struct tcphdr *) (datagram + sizeof (struct ether_header) + sizeof (struct ip));
struct sockaddr_in sin;
struct pseudo_header psh;
//some address resolution
strcpy(source_ip , "192.168.1.70");
sin.sin_family = AF_INET;
sin.sin_port = htons(80);
if (fscanf(fp, "%253s", server) == 1)
{
sin.sin_addr.s_addr = inet_addr (server);
lines_read++;
//printf("%s\n", server);
}
else
{
done = 1;
break;
}
//Fill in the Ethernet Header
eh->ether_dhost[0] = 0x62;
eh->ether_dhost[1] = 0x97;
eh->ether_dhost[2] = 0x41;
eh->ether_dhost[3] = 0x4b;
eh->ether_dhost[4] = 0x1e;
eh->ether_dhost[5] = 0xc0;
eh->ether_shost[0] = 0xc4;
eh->ether_shost[1] = 0x65;
eh->ether_shost[2] = 0x16;
eh->ether_shost[3] = 0x24;
eh->ether_shost[4] = 0xd5;
eh->ether_shost[5] = 0x9a;
eh->ether_type = htons(0x0800);
//Fill in the IP Header
iph->ihl = 5;
iph->version = 4;
iph->tos = 0;
iph->tot_len = htons(sizeof (struct iphdr) + sizeof (struct tcphdr));
iph->id = htons (54321); //Id of this packet
iph->frag_off = 0;
iph->ttl = 255;
iph->protocol = IPPROTO_TCP;
iph->check = 0; //Set to 0 before calculating checksum
iph->saddr = inet_addr ( source_ip );
iph->daddr = sin.sin_addr.s_addr;
//Ip checksum
iph->check = checksum2 (datagram + sizeof (struct ether_header), sizeof (struct iphdr));
//TCP Header
tcph->source = htons (1234);
tcph->dest = htons (80);
tcph->seq = 0;
tcph->ack_seq = 0;
tcph->doff = 5; //tcp header size
tcph->fin=0;
tcph->syn=1;
tcph->rst=0;
tcph->psh=0;
tcph->ack=0;
tcph->urg=0;
tcph->window = htons (5840); // maximum allowed window size
tcph->check = 0; //leave checksum 0 now, filled later by pseudo header
tcph->urg_ptr = 0;
//Now the TCP checksum
psh.source_address = inet_addr( source_ip );
psh.dest_address = sin.sin_addr.s_addr;
psh.placeholder = 0;
psh.protocol = IPPROTO_TCP;
psh.tcp_length = htons(sizeof(struct tcphdr));
int psize = sizeof(struct pseudo_header) + sizeof(struct tcphdr);
pseudogram = malloc(psize);
memcpy(pseudogram , (char*) &psh , sizeof (struct pseudo_header));
memcpy(pseudogram + sizeof(struct pseudo_header) , tcph , sizeof(struct tcphdr));
tcph->check = checksum2(pseudogram , psize);
//memcpy(data, datagram, (sizeof(struct ether_header) + sizeof(struct iphdr) + sizeof(struct tcphdr)));
free(pseudogram);
len = sizeof(struct ether_header) + sizeof(struct iphdr) + sizeof(struct tcphdr);
static int total=0;
/* send all buffers with TP_STATUS_SEND_REQUEST */
/* Wait end of transfer */
ec_send = sendto(fd_socket,datagram,len,0,(struct sockaddr *) ps_sockaddr,sizeof(struct sockaddr_ll));
if(ec_send < 0) {
perror("sendto");
}
else if ( ec_send == 0 ) {
/* nothing to do => schedule : useful if no SMP */
//printf("Sleeping\n");
usleep(0);
}
else {
total += ec_send/(len);
total_bytes += ec_send;
sent++;
pthread_mutex_lock( &mutex1 );
count++;
pthread_mutex_unlock( &mutex1 );
clock_gettime(CLOCK_MONOTONIC, &stopTime);
uint64_t msElapsed = (stopTime.tv_nsec - startTime.tv_nsec) / 1000000 + (stopTime.tv_sec - startTime.tv_sec) * 1000;
double seconds = (double)msElapsed / 1000.0;
printf("Lines read=%d Packets sent=%d, Bytes=%ld, Time=%fseconds, MBit/s=%f Packets/s=%f\r",
lines_read, sent, total_bytes, seconds, 8*total_bytes/1024/1024/seconds, sent/seconds);
//printf("send %d packets (+%d bytes)\n",total, ec_send);
fflush(0);
}
}
}
void * my_recv (){
char buffer[8192];
int ec_recv;
struct sockaddr_ll from;
socklen_t fromlen = sizeof(from);
while (1){
ec_recv = recvfrom(fd_socket, buffer, sizeof(buffer), 0, (struct sockaddr *) &from, &fromlen);
printf("recv completed\n");
if (ec_recv > 0){
printf("Received something\n");
pthread_mutex_lock( &mutex1 );
count--;
pthread_mutex_unlock( &mutex1 );
}
if (ec_recv == 0){
printf("Received nothing\n");
}
if (ec_recv < 0){
perror("recv");
}
}
}
int main( int argc, char ** argv )
{
uint32_t size;
struct sockaddr_ll my_addr;
int i_ifindex;
int ec;
struct ifreq s_ifr; /* points to one interface returned from ioctl */
pthread_t t_send, t_recv;
int s,s2,i;
int ret = -1;
struct rlimit lim;
if (argc != 2) {
printf("Usage: %s <INPUT_FILE>\n", argv[0]);
return 1;
}
getrlimit(RLIMIT_NOFILE, &lim);
printf("Soft: %d Hard: %d\n", (int)lim.rlim_cur, (int)lim.rlim_max);
lim.rlim_cur = lim.rlim_max;
if (setrlimit(RLIMIT_NOFILE, &lim) == -1) {
printf("rlimit failed\n");
return -1;
}
getrlimit(RLIMIT_NOFILE, &lim);
printf("New Soft: %d New Hard: %d\n", (int)lim.rlim_cur, (int)lim.rlim_max);
s = socket(AF_INET, SOCK_DGRAM, 0);
s2 = socket(AF_INET, SOCK_DGRAM, 0);
strcpy(ifr.ifr_name, ifname);
strcpy(ifr2.ifr_name, ifname);
ioctl(s, SIOCGIFHWADDR, &ifr);
ioctl(s2, SIOCGIFADDR, &ifr2);
close(s);
fp = fopen(argv[1], "r");
if (!fp)
exit(EXIT_FAILURE);
fd_socket = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if(fd_socket == -1)
{
perror("socket");
return EXIT_FAILURE;
}
/* clear structure */
memset(&my_addr, 0, sizeof(struct sockaddr_ll));
my_addr.sll_family = PF_PACKET;
my_addr.sll_protocol = htons(ETH_P_ALL);
str_devname = "enp3s0";
/* initialize interface struct */
strncpy (s_ifr.ifr_name, str_devname, sizeof(s_ifr.ifr_name));
/* Get the broad cast address */
ec = ioctl(fd_socket, SIOCGIFINDEX, &s_ifr);
if(ec == -1)
{
perror("iotcl");
return EXIT_FAILURE;
}
/* update with interface index */
i_ifindex = s_ifr.ifr_ifindex;
s_ifr.ifr_mtu = 7200;
/* update the mtu through ioctl */
ec = ioctl(fd_socket, SIOCSIFMTU, &s_ifr);
if(ec == -1)
{
perror("iotcl");
return EXIT_FAILURE;
}
/* set sockaddr info */
memset(&my_addr, 0, sizeof(struct sockaddr_ll));
my_addr.sll_family = AF_PACKET;
my_addr.sll_protocol = ETH_P_ALL;
my_addr.sll_ifindex = i_ifindex;
/* bind port */
if (bind(fd_socket, (struct sockaddr *)&my_addr, sizeof(struct sockaddr_ll)) == -1)
{
perror("bind");
return EXIT_FAILURE;
}
clock_gettime(CLOCK_MONOTONIC, &startTime);
if (pthread_create(&t_recv, NULL, my_recv, NULL) != 0){
perror("pthread_create()");
exit(errno);
}
if (pthread_create(&t_send, NULL, my_send, NULL) != 0){
perror("pthread_create()");
exit(errno);
}
pthread_join (t_recv, NULL);
pthread_join (t_send, NULL);
close(fd_socket);
printf("\nFinished without error.\n");
return 1;
}
Here is a file of IPv4 addresses to test with:
IPv4 addresses

Why is sendto returning zero with raw sockets?

The following program uses a PF_PACKET socket to send a TCP SYN packet to web server read from a file which is a list of web server IPv4 addresses - one address per line. The code is quite long because it takes a lot of code to obtain the gateway router MAC and IP address necessary for filling in the ethernet and IP headers. The good news is you can just skip all the functions in the preamble and just go to main which is where the problem is.
My program works perfectly for the first iteration of the while loop in main. Here is the output:
$ sudo ./mmap/my_mmap_with_one_socket ip
Soft: 1024 Hard: 1048576
New Soft: 1048576 New Hard: 1048576
Main routing table IPv4
default via 192.168.1.254 dev enp3s0
10.8.0.0/24 dev tun0
169.254.0.0/16 dev enp3s0
192.168.1.0/24 dev enp3s0
get_if_info for enp3s0
interface index is 2
get_if_info OK
Clean up temporary socket
bind_arp: ifindex=2
Binding to ifindex 2
Copy IP address to arp_req
read_arp
received ARP len=60
Sender IP: 192.168.1.254
Sender MAC: 70:97:41:4B:1E:C2
Got reply, break out
send 1 packets (+54 bytes)
But then the program doesn't make any progress because sendto keeps returning zero on every subsequent iteration. Why? How can I fix this?
Here's the code:
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/tcp.h> //Provides declarations for tcp header
#include <netinet/ip.h> //Provides declarations for ip header
#include <netinet/ether.h>
#include <ifaddrs.h>
#include <asm/types.h>
#include <linux/if_ether.h>
//#include <linux/if_arp.h>
#include <arpa/inet.h> //htons etc
#include <time.h>
#include <linux/rtnetlink.h>
#include <sys/resource.h>
#define PROTO_ARP 0x0806
#define ETH2_HEADER_LEN 14
#define HW_TYPE 1
#define MAC_LENGTH 6
#define IPV4_LENGTH 4
#define ARP_REQUEST 0x01
#define ARP_REPLY 0x02
#define BUF_SIZE 60
#define MAX_CONNECTIONS 10000
#define debug(x...) printf(x);printf("\n");
#define info(x...) printf(x);printf("\n");
#define warn(x...) printf(x);printf("\n");
#define err(x...) printf(x);printf("\n");
static char * str_devname= NULL;
static int mode_loss = 0;
static int c_packet_sz = 150;
static int c_buffer_sz = 1024*8;
static int c_buffer_nb = 1024;
static int c_sndbuf_sz = 0;
static int c_send_mask = 127;
static int c_error = 0;
static int c_mtu = 0;
static int mode_thread = 0;
volatile int fd_socket;
volatile int data_offset = 0;
volatile struct tpacket_hdr * ps_header_start;
volatile struct sockaddr_ll *ps_sockaddr = NULL;
volatile int shutdown_flag = 0;
int done = 0;
struct tpacket_req s_packet_req;
unsigned char buffer[BUF_SIZE];
struct arp_header *arp_resp = (struct arp_header *) (buffer + ETH2_HEADER_LEN);
char ifname[512];
char ip[512];
/*
96 bit (12 bytes) pseudo header needed for tcp header checksum calculation
*/
struct pseudo_header
{
u_int32_t source_address;
u_int32_t dest_address;
u_int8_t placeholder;
u_int8_t protocol;
u_int16_t tcp_length;
};
struct arp_header {
unsigned short hardware_type;
unsigned short protocol_type;
unsigned char hardware_len;
unsigned char protocol_len;
unsigned short opcode;
unsigned char sender_mac[MAC_LENGTH];
unsigned char sender_ip[IPV4_LENGTH];
unsigned char target_mac[MAC_LENGTH];
unsigned char target_ip[IPV4_LENGTH];
};
int rtnl_receive(int fd, struct msghdr *msg, int flags)
{
int len;
do {
len = recvmsg(fd, msg, flags);
} while (len < 0 && (errno == EINTR || errno == EAGAIN));
if (len < 0) {
perror("Netlink receive failed");
return -errno;
}
if (len == 0) {
perror("EOF on netlink");
return -ENODATA;
}
return len;
}
static int rtnl_recvmsg(int fd, struct msghdr *msg, char **answer)
{
struct iovec *iov = msg->msg_iov;
char *buf;
int len;
iov->iov_base = NULL;
iov->iov_len = 0;
len = rtnl_receive(fd, msg, MSG_PEEK | MSG_TRUNC);
if (len < 0) {
return len;
}
buf = malloc(len);
if (!buf) {
perror("malloc failed");
return -ENOMEM;
}
iov->iov_base = buf;
iov->iov_len = len;
len = rtnl_receive(fd, msg, 0);
if (len < 0) {
free(buf);
return len;
}
*answer = buf;
return len;
}
void parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
{
memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
while (RTA_OK(rta, len)) {
if (rta->rta_type <= max) {
tb[rta->rta_type] = rta;
}
rta = RTA_NEXT(rta,len);
}
}
static inline int rtm_get_table(struct rtmsg *r, struct rtattr **tb)
{
__u32 table = r->rtm_table;
if (tb[RTA_TABLE]) {
table = *(__u32 *)RTA_DATA(tb[RTA_TABLE]);
}
return table;
}
void print_route(struct nlmsghdr* nl_header_answer)
{
struct rtmsg* r = NLMSG_DATA(nl_header_answer);
int len = nl_header_answer->nlmsg_len;
struct rtattr* tb[RTA_MAX+1];
int table;
char buf[256];
len -= NLMSG_LENGTH(sizeof(*r));
if (len < 0) {
perror("Wrong message length");
return;
}
parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
table = rtm_get_table(r, tb);
if (r->rtm_family != AF_INET && table != RT_TABLE_MAIN) {
return;
}
if (tb[RTA_DST]) {
if ((r->rtm_dst_len != 24) && (r->rtm_dst_len != 16)) {
return;
}
printf("%s/%u ", inet_ntop(r->rtm_family, RTA_DATA(tb[RTA_DST]), buf, sizeof(buf)), r->rtm_dst_len);
} else if (r->rtm_dst_len) {
printf("0/%u ", r->rtm_dst_len);
} else {
printf("default ");
}
if (tb[RTA_GATEWAY]) {
printf("via %s", inet_ntop(r->rtm_family, RTA_DATA(tb[RTA_GATEWAY]), buf, sizeof(buf)));
strcpy(ip, inet_ntop(r->rtm_family, RTA_DATA(tb[RTA_GATEWAY]), buf, sizeof(buf)));
}
if (tb[RTA_OIF]) {
char if_nam_buf[IF_NAMESIZE];
int ifidx = *(__u32 *)RTA_DATA(tb[RTA_OIF]);
printf(" dev %s", if_indextoname(ifidx, if_nam_buf));
}
if (tb[RTA_GATEWAY] && tb[RTA_OIF]) {
char if_nam_buf[IF_NAMESIZE];
int ifidx = *(__u32 *)RTA_DATA(tb[RTA_OIF]);
strcpy(ifname, if_indextoname(ifidx, if_nam_buf));
}
if (tb[RTA_SRC]) {
printf("src %s", inet_ntop(r->rtm_family, RTA_DATA(tb[RTA_SRC]), buf, sizeof(buf)));
}
printf("\n");
}
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));
saddr.nl_family = AF_NETLINK;
saddr.nl_pid = getpid();
if (bind(sock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
perror("Failed to bind to netlink socket");
close(sock);
return -1;
}
return sock;
}
int do_route_dump_requst(int sock)
{
struct {
struct nlmsghdr nlh;
struct rtmsg rtm;
} nl_request;
nl_request.nlh.nlmsg_type = RTM_GETROUTE;
nl_request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
nl_request.nlh.nlmsg_len = sizeof(nl_request);
nl_request.nlh.nlmsg_seq = time(NULL);
nl_request.rtm.rtm_family = AF_INET;
return send(sock, &nl_request, sizeof(nl_request), 0);
}
int get_route_dump_response(int sock)
{
struct sockaddr_nl nladdr;
struct iovec iov;
struct msghdr msg = {
.msg_name = &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1,
};
char *buf;
int dump_intr = 0;
int status = rtnl_recvmsg(sock, &msg, &buf);
struct nlmsghdr *h = (struct nlmsghdr *)buf;
int msglen = status;
printf("Main routing table IPv4\n");
while (NLMSG_OK(h, msglen)) {
if (h->nlmsg_flags & NLM_F_DUMP_INTR) {
fprintf(stderr, "Dump was interrupted\n");
free(buf);
return -1;
}
if (nladdr.nl_pid != 0) {
continue;
}
if (h->nlmsg_type == NLMSG_ERROR) {
perror("netlink reported error");
free(buf);
}
print_route(h);
h = NLMSG_NEXT(h, msglen);
}
free(buf);
return status;
}
/*
* Converts struct sockaddr with an IPv4 address to network byte order uin32_t.
* Returns 0 on success.
*/
int int_ip4(struct sockaddr *addr, uint32_t *ip)
{
if (addr->sa_family == AF_INET) {
struct sockaddr_in *i = (struct sockaddr_in *) addr;
*ip = i->sin_addr.s_addr;
return 0;
} else {
err("Not AF_INET");
return 1;
}
}
/*
* Formats sockaddr containing IPv4 address as human readable string.
* Returns 0 on success.
*/
int format_ip4(struct sockaddr *addr, char *out)
{
if (addr->sa_family == AF_INET) {
struct sockaddr_in *i = (struct sockaddr_in *) addr;
const char *ip = inet_ntoa(i->sin_addr);
if (!ip) {
return -2;
} else {
strcpy(out, ip);
return 0;
}
} else {
return -1;
}
}
/*
* Writes interface IPv4 address as network byte order to ip.
* Returns 0 on success.
*/
int get_if_ip4(int fd, const char *ifname, uint32_t *ip) {
int err = -1;
struct ifreq ifr;
memset(&ifr, 0, sizeof(struct ifreq));
if (strlen(ifname) > (IFNAMSIZ - 1)) {
err("Too long interface name");
goto out;
}
strcpy(ifr.ifr_name, ifname);
if (ioctl(fd, SIOCGIFADDR, &ifr) == -1) {
perror("SIOCGIFADDR");
goto out;
}
if (int_ip4(&ifr.ifr_addr, ip)) {
goto out;
}
err = 0;
out:
return err;
}
/*
* Sends an ARP who-has request to dst_ip
* on interface ifindex, using source mac src_mac and source ip src_ip.
*/
int send_arp(int fd, int ifindex, const unsigned char *src_mac, uint32_t src_ip, uint32_t dst_ip)
{
int err = -1;
unsigned char buffer[BUF_SIZE];
memset(buffer, 0, sizeof(buffer));
struct sockaddr_ll socket_address;
socket_address.sll_family = AF_PACKET;
socket_address.sll_protocol = htons(ETH_P_ARP);
socket_address.sll_ifindex = ifindex;
socket_address.sll_hatype = htons(ARPHRD_ETHER);
socket_address.sll_pkttype = (PACKET_BROADCAST);
socket_address.sll_halen = MAC_LENGTH;
socket_address.sll_addr[6] = 0x00;
socket_address.sll_addr[7] = 0x00;
struct ethhdr *send_req = (struct ethhdr *) buffer;
struct arp_header *arp_req = (struct arp_header *) (buffer + ETH2_HEADER_LEN);
int index;
ssize_t ret, length = 0;
//Broadcast
memset(send_req->h_dest, 0xff, MAC_LENGTH);
//Target MAC zero
memset(arp_req->target_mac, 0x00, MAC_LENGTH);
//Set source mac to our MAC address
memcpy(send_req->h_source, src_mac, MAC_LENGTH);
memcpy(arp_req->sender_mac, src_mac, MAC_LENGTH);
memcpy(socket_address.sll_addr, src_mac, MAC_LENGTH);
/* Setting protocol of the packet */
send_req->h_proto = htons(ETH_P_ARP);
/* Creating ARP request */
arp_req->hardware_type = htons(HW_TYPE);
arp_req->protocol_type = htons(ETH_P_IP);
arp_req->hardware_len = MAC_LENGTH;
arp_req->protocol_len = IPV4_LENGTH;
arp_req->opcode = htons(ARP_REQUEST);
debug("Copy IP address to arp_req");
memcpy(arp_req->sender_ip, &src_ip, sizeof(uint32_t));
memcpy(arp_req->target_ip, &dst_ip, sizeof(uint32_t));
ret = sendto(fd, buffer, 42, 0, (struct sockaddr *) &socket_address, sizeof(socket_address));
if (ret == -1) {
perror("sendto():");
goto out;
}
err = 0;
out:
return err;
}
/*
* Gets interface information by name:
* IPv4
* MAC
* ifindex
*/
int get_if_info(const char *ifname, uint32_t *ip, char *mac, int *ifindex)
{
debug("get_if_info for %s", ifname);
int err = -1;
struct ifreq ifr;
int sd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
if (sd <= 0) {
perror("socket()");
goto out;
}
if (strlen(ifname) > (IFNAMSIZ - 1)) {
printf("Too long interface name, MAX=%i\n", IFNAMSIZ - 1);
goto out;
}
strcpy(ifr.ifr_name, ifname);
//Get interface index using name
if (ioctl(sd, SIOCGIFINDEX, &ifr) == -1) {
perror("SIOCGIFINDEX");
goto out;
}
*ifindex = ifr.ifr_ifindex;
printf("interface index is %d\n", *ifindex);
//Get MAC address of the interface
if (ioctl(sd, SIOCGIFHWADDR, &ifr) == -1) {
perror("SIOCGIFINDEX");
goto out;
}
//Copy mac address to output
memcpy(mac, ifr.ifr_hwaddr.sa_data, MAC_LENGTH);
if (get_if_ip4(sd, ifname, ip)) {
goto out;
}
debug("get_if_info OK");
err = 0;
out:
if (sd > 0) {
debug("Clean up temporary socket");
close(sd);
}
return err;
}
/*
* Creates a raw socket that listens for ARP traffic on specific ifindex.
* Writes out the socket's FD.
* Return 0 on success.
*/
int bind_arp(int ifindex, int *fd)
{
debug("bind_arp: ifindex=%i", ifindex);
int ret = -1;
// Submit request for a raw socket descriptor.
*fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
if (*fd < 1) {
perror("socket()");
goto out;
}
debug("Binding to ifindex %i", ifindex);
struct sockaddr_ll sll;
memset(&sll, 0, sizeof(struct sockaddr_ll));
sll.sll_family = AF_PACKET;
sll.sll_ifindex = ifindex;
if (bind(*fd, (struct sockaddr*) &sll, sizeof(struct sockaddr_ll)) < 0) {
perror("bind");
goto out;
}
ret = 0;
out:
if (ret && *fd > 0) {
debug("Cleanup socket");
close(*fd);
}
return ret;
}
/*
* Reads a single ARP reply from fd.
* Return 0 on success.
*/
int read_arp(int fd)
{
debug("read_arp");
int ret = -1;
ssize_t length = recvfrom(fd, buffer, BUF_SIZE, 0, NULL, NULL);
int index;
if (length == -1) {
perror("recvfrom()");
goto out;
}
struct ethhdr *rcv_resp = (struct ethhdr *) buffer;
if (ntohs(rcv_resp->h_proto) != PROTO_ARP) {
debug("Not an ARP packet");
goto out;
}
if (ntohs(arp_resp->opcode) != ARP_REPLY) {
debug("Not an ARP reply");
goto out;
}
debug("received ARP len=%ld", length);
struct in_addr sender_a;
memset(&sender_a, 0, sizeof(struct in_addr));
memcpy(&sender_a.s_addr, arp_resp->sender_ip, sizeof(uint32_t));
debug("Sender IP: %s", inet_ntoa(sender_a));
debug("Sender MAC: %02X:%02X:%02X:%02X:%02X:%02X",
arp_resp->sender_mac[0],
arp_resp->sender_mac[1],
arp_resp->sender_mac[2],
arp_resp->sender_mac[3],
arp_resp->sender_mac[4],
arp_resp->sender_mac[5]);
ret = 0;
out:
return ret;
}
/*
*
* Sample code that sends an ARP who-has request on
* interface <ifname> to IPv4 address <ip>.
* Returns 0 on success.
*/
int test_arping(const char *ifname, const char *ip) {
int ret = -1;
uint32_t dst = inet_addr(ip);
if (dst == 0 || dst == 0xffffffff) {
printf("Invalid source IP\n");
return 1;
}
int src;
int ifindex;
char mac[MAC_LENGTH];
if (get_if_info(ifname, &src, mac, &ifindex)) {
err("get_if_info failed, interface %s not found or no IP set?", ifname);
goto out;
}
int arp_fd;
if (bind_arp(ifindex, &arp_fd)) {
err("Failed to bind_arp()");
goto out;
}
if (send_arp(arp_fd, ifindex, mac, src, dst)) {
err("Failed to send_arp");
goto out;
}
while(1) {
int r = read_arp(arp_fd);
if (r == 0) {
info("Got reply, break out");
break;
}
}
ret = 0;
out:
if (arp_fd) {
close(arp_fd);
arp_fd = 0;
}
return ret;
}
unsigned short checksum2(const char *buf, unsigned size)
{
unsigned long long sum = 0;
const unsigned long long *b = (unsigned long long *) buf;
unsigned t1, t2;
unsigned short t3, t4;
/* Main loop - 8 bytes at a time */
while (size >= sizeof(unsigned long long))
{
unsigned long long s = *b++;
sum += s;
if (sum < s) sum++;
size -= 8;
}
/* Handle tail less than 8-bytes long */
buf = (const char *) b;
if (size & 4)
{
unsigned s = *(unsigned *)buf;
sum += s;
if (sum < s) sum++;
buf += 4;
}
if (size & 2)
{
unsigned short s = *(unsigned short *) buf;
sum += s;
if (sum < s) sum++;
buf += 2;
}
if (size)
{
unsigned char s = *(unsigned char *) buf;
sum += s;
if (sum < s) sum++;
}
/* Fold down to 16 bits */
t1 = sum;
t2 = sum >> 32;
t1 += t2;
if (t1 < t2) t1++;
t3 = t1;
t4 = t1 >> 16;
t3 += t4;
if (t3 < t4) t3++;
return ~t3;
}
int main( int argc, char ** argv )
{
uint32_t size;
size_t len;
struct sockaddr_ll my_addr, peer_addr;
int i_ifindex;
int ec;
struct ifreq s_ifr; /* points to one interface returned from ioctl */
int tmp;
FILE * fp;
char server[254];
int count = 0;
int first_time = 1;
int z;
int first_mmap = 1;
#define HWADDR_len 6
#define IP_len 4
int s,s2,i;
struct ifreq ifr,ifr2;
int ret = -1;
struct rlimit lim;
if (argc != 2) {
printf("Usage: %s <INPUT_FILE>\n", argv[0]);
return 1;
}
getrlimit(RLIMIT_NOFILE, &lim);
printf("Soft: %d Hard: %d\n", (int)lim.rlim_cur, (int)lim.rlim_max);
lim.rlim_cur = lim.rlim_max;
if (setrlimit(RLIMIT_NOFILE, &lim) == -1) {
printf("rlimit failed\n");
return -1;
}
getrlimit(RLIMIT_NOFILE, &lim);
printf("New Soft: %d New Hard: %d\n", (int)lim.rlim_cur, (int)lim.rlim_max);
int nl_sock = open_netlink();
if (do_route_dump_requst(nl_sock) < 0) {
perror("Failed to perfom request");
close(nl_sock);
return -1;
}
get_route_dump_response(nl_sock);
close (nl_sock);
test_arping(ifname, ip);
s = socket(AF_INET, SOCK_DGRAM, 0);
s2 = socket(AF_INET, SOCK_DGRAM, 0);
strcpy(ifr.ifr_name, ifname);
strcpy(ifr2.ifr_name, ifname);
ioctl(s, SIOCGIFHWADDR, &ifr);
ioctl(s2, SIOCGIFADDR, &ifr2);
struct sockaddr_in* ipaddr = (struct sockaddr_in*)&ifr2.ifr_addr;
close(s);
fp = fopen(argv[1], "r");
if (!fp)
exit(EXIT_FAILURE);
fd_socket = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if(fd_socket == -1)
{
perror("socket");
return EXIT_FAILURE;
}
/* clear structure */
memset(&my_addr, 0, sizeof(struct sockaddr_ll));
my_addr.sll_family = PF_PACKET;
my_addr.sll_protocol = htons(ETH_P_ALL);
str_devname = ifname;
//strcpy (str_devname, ifname);
/* initialize interface struct */
strncpy (s_ifr.ifr_name, str_devname, sizeof(s_ifr.ifr_name));
/* Get the broad cast address */
ec = ioctl(fd_socket, SIOCGIFINDEX, &s_ifr);
if(ec == -1)
{
perror("iotcl");
return EXIT_FAILURE;
}
/* update with interface index */
i_ifindex = s_ifr.ifr_ifindex;
s_ifr.ifr_mtu = 7200;
/* update the mtu through ioctl */
ec = ioctl(fd_socket, SIOCSIFMTU, &s_ifr);
if(ec == -1)
{
perror("iotcl");
return EXIT_FAILURE;
}
/* set sockaddr info */
memset(&my_addr, 0, sizeof(struct sockaddr_ll));
my_addr.sll_family = AF_PACKET;
my_addr.sll_protocol = ETH_P_ALL;
my_addr.sll_ifindex = i_ifindex;
/* bind port */
if (bind(fd_socket, (struct sockaddr *)&my_addr, sizeof(struct sockaddr_ll)) == -1)
{
perror("bind");
return EXIT_FAILURE;
}
/* prepare Tx ring request */
s_packet_req.tp_block_size = c_buffer_sz;
s_packet_req.tp_frame_size = c_buffer_sz;
s_packet_req.tp_block_nr = c_buffer_nb;
s_packet_req.tp_frame_nr = c_buffer_nb;
/* calculate memory to mmap in the kernel */
size = s_packet_req.tp_block_size * s_packet_req.tp_block_nr;
/* set packet loss option */
tmp = mode_loss;
if (setsockopt(fd_socket, SOL_PACKET, PACKET_LOSS, (char *)&tmp, sizeof(tmp))<0)
{
perror("setsockopt: PACKET_LOSS");
return EXIT_FAILURE;
}
/* send TX ring request */
if (setsockopt(fd_socket, SOL_PACKET, PACKET_TX_RING, (char *)&s_packet_req, sizeof(s_packet_req))<0)
{
perror("setsockopt: PACKET_TX_RING");
return EXIT_FAILURE;
}
/* change send buffer size */
if(c_sndbuf_sz) {
printf("send buff size = %d\n", c_sndbuf_sz);
if (setsockopt(fd_socket, SOL_SOCKET, SO_SNDBUF, &c_sndbuf_sz, sizeof(c_sndbuf_sz))< 0)
{
perror("getsockopt: SO_SNDBUF");
return EXIT_FAILURE;
}
}
/* get data offset */
data_offset = TPACKET_HDRLEN - sizeof(struct sockaddr_ll);
/* mmap Tx ring buffers memory */
ps_header_start = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd_socket, 0);
if (ps_header_start == (void*)-1)
{
perror("mmap");
return EXIT_FAILURE;
}
while (!done)
{
char * data;
int first_loop = 1;
struct tpacket_hdr * ps_header;
int ec_send = 0;
ps_header = ((struct tpacket_hdr *)((void *)ps_header_start));
data = ((void*) ps_header) + data_offset;
//Datagram to represent the packet
char datagram[4096] , source_ip[32] , *pseudogram;
//zero out the packet buffer
memset (datagram, 0, 4096);
//Ethernet header
struct ether_header *eh = (struct ether_header *) datagram;
//IP header
struct iphdr *iph = (struct iphdr *) (datagram + sizeof (struct ether_header));
//TCP header
struct tcphdr *tcph = (struct tcphdr *) (datagram + sizeof (struct ether_header) + sizeof (struct ip));
struct sockaddr_in sin;
struct pseudo_header psh;
//some address resolution
strcpy(source_ip , inet_ntoa(ipaddr->sin_addr));
sin.sin_family = AF_INET;
sin.sin_port = htons(80);
if (fscanf(fp, "%253s", server) == 1)
{
sin.sin_addr.s_addr = inet_addr (server);
//printf("%s\n", server);
}
else
{
done = 1;
break;
}
//Fill in the Ethernet Header
eh->ether_dhost[0] = arp_resp->sender_mac[0];
eh->ether_dhost[1] = arp_resp->sender_mac[1];
eh->ether_dhost[2] = arp_resp->sender_mac[2];
eh->ether_dhost[3] = arp_resp->sender_mac[3];
eh->ether_dhost[4] = arp_resp->sender_mac[4];
eh->ether_dhost[5] = arp_resp->sender_mac[5];
memcpy(eh->ether_shost, ifr.ifr_hwaddr.sa_data, HWADDR_len);
eh->ether_type = htons(0x0800);
//Fill in the IP Header
iph->ihl = 5;
iph->version = 4;
iph->tos = 0;
iph->tot_len = htons(sizeof (struct iphdr) + sizeof (struct tcphdr));
iph->id = htons (54321); //Id of this packet
iph->frag_off = 0;
iph->ttl = 255;
iph->protocol = IPPROTO_TCP;
iph->check = 0; //Set to 0 before calculating checksum
iph->saddr = inet_addr ( source_ip );
iph->daddr = sin.sin_addr.s_addr;
//Ip checksum
iph->check = checksum2 (datagram + sizeof (struct ether_header), sizeof (struct iphdr));
//TCP Header
tcph->source = htons (1234);
tcph->dest = htons (80);
tcph->seq = 0;
tcph->ack_seq = 0;
tcph->doff = 5; //tcp header size
tcph->fin=0;
tcph->syn=1;
tcph->rst=0;
tcph->psh=0;
tcph->ack=0;
tcph->urg=0;
tcph->window = htons (5840); // maximum allowed window size
tcph->check = 0; //leave checksum 0 now, filled later by pseudo header
tcph->urg_ptr = 0;
//Now the TCP checksum
psh.source_address = inet_addr( source_ip );
psh.dest_address = sin.sin_addr.s_addr;
psh.placeholder = 0;
psh.protocol = IPPROTO_TCP;
psh.tcp_length = htons(sizeof(struct tcphdr));
int psize = sizeof(struct pseudo_header) + sizeof(struct tcphdr);
pseudogram = malloc(psize);
memcpy(pseudogram , (char*) &psh , sizeof (struct pseudo_header));
memcpy(pseudogram + sizeof(struct pseudo_header) , tcph , sizeof(struct tcphdr));
tcph->check = checksum2(pseudogram , psize);
memcpy(data, datagram, (sizeof(struct ether_header) + sizeof(struct iphdr) + sizeof(struct tcphdr)));
free(pseudogram);
len = sizeof(struct ether_header) + sizeof(struct iphdr) + sizeof(struct tcphdr);
//printf("len=%d data=%s\n", len, data);
//for (int k = 0; k < len; k++)
// printf("%c ", data + k);
//printf("\n");
/* update packet len */
ps_header->tp_len = len;
/* set header flag to USER (trigs xmit)*/
ps_header->tp_status = TP_STATUS_SEND_REQUEST;
//int ec_send;
static int total=0;
/* send all buffers with TP_STATUS_SEND_REQUEST */
/* Wait end of transfer */
ec_send = sendto(fd_socket,NULL,0,0,(struct sockaddr *) ps_sockaddr,sizeof(struct sockaddr_ll));
if(ec_send < 0) {
perror("sendto");
}
else if ( ec_send == 0 ) {
/* nothing to do => schedule : useful if no SMP */
//printf("Sleeping\n");
usleep(0);
}
else {
total += ec_send/(len);
printf("send %d packets (+%d bytes)\n",total, ec_send);
fflush(0);
}
}
//ps_header_start = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd_socket, 0);
if (munmap(ps_header_start, size) == -1)
{
perror("munmap");
exit(EXIT_FAILURE);
}
close(fd_socket);
return 1;
}
If you are going to use PACKET_TX_RING, then you must play by its rules: that means you can't use the same buffer spot over and over: you must advance to the next buffer location within the ring buffer: build the first packet in slot 0, the second in slot 1, etc. (wrapping when you get to the end of the buffer).
But you're building every packet in slot 0 (i.e. at ps_header_start) but after sending the first packet, the kernel is expecting the next frame in the subsequent slot. It will never find the (second) packet you created in slot 0 until it has processed all the other slots. But it's looking at slot 1 and seeing that the tp_status in that slot hasn't been set to TP_STATUS_SEND_REQUEST yet so... nothing to do.
Note that your sendto call is not providing a buffer address to the kernel. That's because the kernel knows where the next packet will come from, it must be in the next slot in the ring buffer following the one you just sent.
This is why I suggested in your other question that you not use PACKET_TX_RING unless you really need it: it's more complicated to do correctly. It's much easier to just create your frame in a static buffer and call sendto(fd, buffer_address, buffer_len, ...). And if you are going to call sendto for each created frame, there is literally no advantage to using PACKET_TX_RING anyway.

how do i get the gateway MAC address in C on linux?

I have an application that uses raw sockets and I need to construct the ethernet header. Currently I am hard coding the MAC address of my gateway like so:
struct ether_header *eh
...
eh->ether_dhost[0] = 0x70;
eh->ether_dhost[1] = 0x97;
eh->ether_dhost[2] = 0x41;
eh->ether_dhost[3] = 0x4b;
eh->ether_dhost[4] = 0x1e;
eh->ether_dhost[5] = 0xc2;
I want my code to run on multiple Linux machines so I need an automated way of getting the gateway's MAC address. Can this be done in C? I understand this problem is platform specific. That's not a problem. I don't need a portable solution. I just need it to work on Linux.
I am posting how I finally solved this. Following the suggestion to use a netlink socket to get the default route I implemented something that gets the default route ip address and then gets the MAC address through an ARP request. The code below does all this and also gets the interface name and does some network communication (send SYN to IP address obtained from a file which is a list of IP addresses - one per line). Here's the code:
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/tcp.h> //Provides declarations for tcp header
#include <netinet/ip.h> //Provides declarations for ip header
#include <netinet/ether.h>
#include <ifaddrs.h>
#include <asm/types.h>
#include <linux/if_ether.h>
//#include <linux/if_arp.h>
#include <arpa/inet.h> //htons etc
#include <time.h>
#include <linux/rtnetlink.h>
#define PROTO_ARP 0x0806
#define ETH2_HEADER_LEN 14
#define HW_TYPE 1
#define MAC_LENGTH 6
#define IPV4_LENGTH 4
#define ARP_REQUEST 0x01
#define ARP_REPLY 0x02
#define BUF_SIZE 60
#define MAX_CONNECTIONS 10000
#define debug(x...) printf(x);printf("\n");
#define info(x...) printf(x);printf("\n");
#define warn(x...) printf(x);printf("\n");
#define err(x...) printf(x);printf("\n");
static char * str_devname= NULL;
static int mode_loss = 0;
static int c_packet_sz = 150;
static int c_packet_nb = 1000;
static int c_buffer_sz = 1024*8;
static int c_buffer_nb = 1024;
static int c_sndbuf_sz = 0;
static int c_send_mask = 127;
static int c_error = 0;
static int c_mtu = 0;
static int mode_thread = 0;
volatile int fd_socket[MAX_CONNECTIONS];
volatile int data_offset = 0;
volatile struct tpacket_hdr * ps_header_start;
volatile struct sockaddr_ll *ps_sockaddr = NULL;
volatile int shutdown_flag = 0;
int done = 0;
struct tpacket_req s_packet_req;
unsigned char buffer[BUF_SIZE];
struct arp_header *arp_resp = (struct arp_header *) (buffer + ETH2_HEADER_LEN);
char ifname[512];
char ip[512];
/*
96 bit (12 bytes) pseudo header needed for tcp header checksum calculation
*/
struct pseudo_header
{
u_int32_t source_address;
u_int32_t dest_address;
u_int8_t placeholder;
u_int8_t protocol;
u_int16_t tcp_length;
};
struct arp_header {
unsigned short hardware_type;
unsigned short protocol_type;
unsigned char hardware_len;
unsigned char protocol_len;
unsigned short opcode;
unsigned char sender_mac[MAC_LENGTH];
unsigned char sender_ip[IPV4_LENGTH];
unsigned char target_mac[MAC_LENGTH];
unsigned char target_ip[IPV4_LENGTH];
};
int rtnl_receive(int fd, struct msghdr *msg, int flags)
{
int len;
do {
len = recvmsg(fd, msg, flags);
} while (len < 0 && (errno == EINTR || errno == EAGAIN));
if (len < 0) {
perror("Netlink receive failed");
return -errno;
}
if (len == 0) {
perror("EOF on netlink");
return -ENODATA;
}
return len;
}
static int rtnl_recvmsg(int fd, struct msghdr *msg, char **answer)
{
struct iovec *iov = msg->msg_iov;
char *buf;
int len;
iov->iov_base = NULL;
iov->iov_len = 0;
len = rtnl_receive(fd, msg, MSG_PEEK | MSG_TRUNC);
if (len < 0) {
return len;
}
buf = malloc(len);
if (!buf) {
perror("malloc failed");
return -ENOMEM;
}
iov->iov_base = buf;
iov->iov_len = len;
len = rtnl_receive(fd, msg, 0);
if (len < 0) {
free(buf);
return len;
}
*answer = buf;
return len;
}
void parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
{
memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
while (RTA_OK(rta, len)) {
if (rta->rta_type <= max) {
tb[rta->rta_type] = rta;
}
rta = RTA_NEXT(rta,len);
}
}
static inline int rtm_get_table(struct rtmsg *r, struct rtattr **tb)
{
__u32 table = r->rtm_table;
if (tb[RTA_TABLE]) {
table = *(__u32 *)RTA_DATA(tb[RTA_TABLE]);
}
return table;
}
void print_route(struct nlmsghdr* nl_header_answer)
{
struct rtmsg* r = NLMSG_DATA(nl_header_answer);
int len = nl_header_answer->nlmsg_len;
struct rtattr* tb[RTA_MAX+1];
int table;
char buf[256];
len -= NLMSG_LENGTH(sizeof(*r));
if (len < 0) {
perror("Wrong message length");
return;
}
parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
table = rtm_get_table(r, tb);
if (r->rtm_family != AF_INET && table != RT_TABLE_MAIN) {
return;
}
if (tb[RTA_DST]) {
if ((r->rtm_dst_len != 24) && (r->rtm_dst_len != 16)) {
return;
}
printf("%s/%u ", inet_ntop(r->rtm_family, RTA_DATA(tb[RTA_DST]), buf, sizeof(buf)), r->rtm_dst_len);
} else if (r->rtm_dst_len) {
printf("0/%u ", r->rtm_dst_len);
} else {
printf("default ");
}
if (tb[RTA_GATEWAY]) {
printf("via %s", inet_ntop(r->rtm_family, RTA_DATA(tb[RTA_GATEWAY]), buf, sizeof(buf)));
strcpy(ip, inet_ntop(r->rtm_family, RTA_DATA(tb[RTA_GATEWAY]), buf, sizeof(buf)));
}
if (tb[RTA_OIF]) {
char if_nam_buf[IF_NAMESIZE];
int ifidx = *(__u32 *)RTA_DATA(tb[RTA_OIF]);
printf(" dev %s", if_indextoname(ifidx, if_nam_buf));
}
if (tb[RTA_GATEWAY] && tb[RTA_OIF]) {
char if_nam_buf[IF_NAMESIZE];
int ifidx = *(__u32 *)RTA_DATA(tb[RTA_OIF]);
strcpy(ifname, if_indextoname(ifidx, if_nam_buf));
}
if (tb[RTA_SRC]) {
printf("src %s", inet_ntop(r->rtm_family, RTA_DATA(tb[RTA_SRC]), buf, sizeof(buf)));
}
printf("\n");
}
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));
saddr.nl_family = AF_NETLINK;
saddr.nl_pid = getpid();
if (bind(sock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
perror("Failed to bind to netlink socket");
close(sock);
return -1;
}
return sock;
}
int do_route_dump_requst(int sock)
{
struct {
struct nlmsghdr nlh;
struct rtmsg rtm;
} nl_request;
nl_request.nlh.nlmsg_type = RTM_GETROUTE;
nl_request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
nl_request.nlh.nlmsg_len = sizeof(nl_request);
nl_request.nlh.nlmsg_seq = time(NULL);
nl_request.rtm.rtm_family = AF_INET;
return send(sock, &nl_request, sizeof(nl_request), 0);
}
int get_route_dump_response(int sock)
{
struct sockaddr_nl nladdr;
struct iovec iov;
struct msghdr msg = {
.msg_name = &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1,
};
char *buf;
int dump_intr = 0;
int status = rtnl_recvmsg(sock, &msg, &buf);
struct nlmsghdr *h = (struct nlmsghdr *)buf;
int msglen = status;
printf("Main routing table IPv4\n");
while (NLMSG_OK(h, msglen)) {
if (h->nlmsg_flags & NLM_F_DUMP_INTR) {
fprintf(stderr, "Dump was interrupted\n");
free(buf);
return -1;
}
if (nladdr.nl_pid != 0) {
continue;
}
if (h->nlmsg_type == NLMSG_ERROR) {
perror("netlink reported error");
free(buf);
}
print_route(h);
h = NLMSG_NEXT(h, msglen);
}
free(buf);
return status;
}
/*
* Converts struct sockaddr with an IPv4 address to network byte order uin32_t.
* Returns 0 on success.
*/
int int_ip4(struct sockaddr *addr, uint32_t *ip)
{
if (addr->sa_family == AF_INET) {
struct sockaddr_in *i = (struct sockaddr_in *) addr;
*ip = i->sin_addr.s_addr;
return 0;
} else {
err("Not AF_INET");
return 1;
}
}
/*
* Formats sockaddr containing IPv4 address as human readable string.
* Returns 0 on success.
*/
int format_ip4(struct sockaddr *addr, char *out)
{
if (addr->sa_family == AF_INET) {
struct sockaddr_in *i = (struct sockaddr_in *) addr;
const char *ip = inet_ntoa(i->sin_addr);
if (!ip) {
return -2;
} else {
strcpy(out, ip);
return 0;
}
} else {
return -1;
}
}
/*
* Writes interface IPv4 address as network byte order to ip.
* Returns 0 on success.
*/
int get_if_ip4(int fd, const char *ifname, uint32_t *ip) {
int err = -1;
struct ifreq ifr;
memset(&ifr, 0, sizeof(struct ifreq));
if (strlen(ifname) > (IFNAMSIZ - 1)) {
err("Too long interface name");
goto out;
}
strcpy(ifr.ifr_name, ifname);
if (ioctl(fd, SIOCGIFADDR, &ifr) == -1) {
perror("SIOCGIFADDR");
goto out;
}
if (int_ip4(&ifr.ifr_addr, ip)) {
goto out;
}
err = 0;
out:
return err;
}
/*
* Sends an ARP who-has request to dst_ip
* on interface ifindex, using source mac src_mac and source ip src_ip.
*/
int send_arp(int fd, int ifindex, const unsigned char *src_mac, uint32_t src_ip, uint32_t dst_ip)
{
int err = -1;
unsigned char buffer[BUF_SIZE];
memset(buffer, 0, sizeof(buffer));
struct sockaddr_ll socket_address;
socket_address.sll_family = AF_PACKET;
socket_address.sll_protocol = htons(ETH_P_ARP);
socket_address.sll_ifindex = ifindex;
socket_address.sll_hatype = htons(ARPHRD_ETHER);
socket_address.sll_pkttype = (PACKET_BROADCAST);
socket_address.sll_halen = MAC_LENGTH;
socket_address.sll_addr[6] = 0x00;
socket_address.sll_addr[7] = 0x00;
struct ethhdr *send_req = (struct ethhdr *) buffer;
struct arp_header *arp_req = (struct arp_header *) (buffer + ETH2_HEADER_LEN);
int index;
ssize_t ret, length = 0;
//Broadcast
memset(send_req->h_dest, 0xff, MAC_LENGTH);
//Target MAC zero
memset(arp_req->target_mac, 0x00, MAC_LENGTH);
//Set source mac to our MAC address
memcpy(send_req->h_source, src_mac, MAC_LENGTH);
memcpy(arp_req->sender_mac, src_mac, MAC_LENGTH);
memcpy(socket_address.sll_addr, src_mac, MAC_LENGTH);
/* Setting protocol of the packet */
send_req->h_proto = htons(ETH_P_ARP);
/* Creating ARP request */
arp_req->hardware_type = htons(HW_TYPE);
arp_req->protocol_type = htons(ETH_P_IP);
arp_req->hardware_len = MAC_LENGTH;
arp_req->protocol_len = IPV4_LENGTH;
arp_req->opcode = htons(ARP_REQUEST);
debug("Copy IP address to arp_req");
memcpy(arp_req->sender_ip, &src_ip, sizeof(uint32_t));
memcpy(arp_req->target_ip, &dst_ip, sizeof(uint32_t));
ret = sendto(fd, buffer, 42, 0, (struct sockaddr *) &socket_address, sizeof(socket_address));
if (ret == -1) {
perror("sendto():");
goto out;
}
err = 0;
out:
return err;
}
/*
* Gets interface information by name:
* IPv4
* MAC
* ifindex
*/
int get_if_info(const char *ifname, uint32_t *ip, char *mac, int *ifindex)
{
debug("get_if_info for %s", ifname);
int err = -1;
struct ifreq ifr;
int sd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
if (sd <= 0) {
perror("socket()");
goto out;
}
if (strlen(ifname) > (IFNAMSIZ - 1)) {
printf("Too long interface name, MAX=%i\n", IFNAMSIZ - 1);
goto out;
}
strcpy(ifr.ifr_name, ifname);
//Get interface index using name
if (ioctl(sd, SIOCGIFINDEX, &ifr) == -1) {
perror("SIOCGIFINDEX");
goto out;
}
*ifindex = ifr.ifr_ifindex;
printf("interface index is %d\n", *ifindex);
//Get MAC address of the interface
if (ioctl(sd, SIOCGIFHWADDR, &ifr) == -1) {
perror("SIOCGIFINDEX");
goto out;
}
//Copy mac address to output
memcpy(mac, ifr.ifr_hwaddr.sa_data, MAC_LENGTH);
if (get_if_ip4(sd, ifname, ip)) {
goto out;
}
debug("get_if_info OK");
err = 0;
out:
if (sd > 0) {
debug("Clean up temporary socket");
close(sd);
}
return err;
}
/*
* Creates a raw socket that listens for ARP traffic on specific ifindex.
* Writes out the socket's FD.
* Return 0 on success.
*/
int bind_arp(int ifindex, int *fd)
{
debug("bind_arp: ifindex=%i", ifindex);
int ret = -1;
// Submit request for a raw socket descriptor.
*fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
if (*fd < 1) {
perror("socket()");
goto out;
}
debug("Binding to ifindex %i", ifindex);
struct sockaddr_ll sll;
memset(&sll, 0, sizeof(struct sockaddr_ll));
sll.sll_family = AF_PACKET;
sll.sll_ifindex = ifindex;
if (bind(*fd, (struct sockaddr*) &sll, sizeof(struct sockaddr_ll)) < 0) {
perror("bind");
goto out;
}
ret = 0;
out:
if (ret && *fd > 0) {
debug("Cleanup socket");
close(*fd);
}
return ret;
}
/*
* Reads a single ARP reply from fd.
* Return 0 on success.
*/
int read_arp(int fd)
{
debug("read_arp");
int ret = -1;
ssize_t length = recvfrom(fd, buffer, BUF_SIZE, 0, NULL, NULL);
int index;
if (length == -1) {
perror("recvfrom()");
goto out;
}
struct ethhdr *rcv_resp = (struct ethhdr *) buffer;
if (ntohs(rcv_resp->h_proto) != PROTO_ARP) {
debug("Not an ARP packet");
goto out;
}
if (ntohs(arp_resp->opcode) != ARP_REPLY) {
debug("Not an ARP reply");
goto out;
}
debug("received ARP len=%ld", length);
struct in_addr sender_a;
memset(&sender_a, 0, sizeof(struct in_addr));
memcpy(&sender_a.s_addr, arp_resp->sender_ip, sizeof(uint32_t));
debug("Sender IP: %s", inet_ntoa(sender_a));
debug("Sender MAC: %02X:%02X:%02X:%02X:%02X:%02X",
arp_resp->sender_mac[0],
arp_resp->sender_mac[1],
arp_resp->sender_mac[2],
arp_resp->sender_mac[3],
arp_resp->sender_mac[4],
arp_resp->sender_mac[5]);
ret = 0;
out:
return ret;
}
/*
*
* Sample code that sends an ARP who-has request on
* interface <ifname> to IPv4 address <ip>.
* Returns 0 on success.
*/
int test_arping(const char *ifname, const char *ip) {
int ret = -1;
uint32_t dst = inet_addr(ip);
if (dst == 0 || dst == 0xffffffff) {
printf("Invalid source IP\n");
return 1;
}
int src;
int ifindex;
char mac[MAC_LENGTH];
if (get_if_info(ifname, &src, mac, &ifindex)) {
err("get_if_info failed, interface %s not found or no IP set?", ifname);
goto out;
}
int arp_fd;
if (bind_arp(ifindex, &arp_fd)) {
err("Failed to bind_arp()");
goto out;
}
if (send_arp(arp_fd, ifindex, mac, src, dst)) {
err("Failed to send_arp");
goto out;
}
while(1) {
int r = read_arp(arp_fd);
if (r == 0) {
info("Got reply, break out");
break;
}
}
ret = 0;
out:
if (arp_fd) {
close(arp_fd);
arp_fd = 0;
}
return ret;
}
/*
Generic checksum calculation function
*/
unsigned short csum(unsigned short *ptr,int nbytes)
{
register long sum;
unsigned short oddbyte;
register short answer;
sum=0;
while(nbytes>1) {
sum+=*ptr++;
nbytes-=2;
}
if(nbytes==1) {
oddbyte=0;
*((u_char*)&oddbyte)=*(u_char*)ptr;
sum+=oddbyte;
}
sum = (sum>>16)+(sum & 0xffff);
sum = sum + (sum>>16);
answer=(short)~sum;
return(answer);
}
int main( int argc, char ** argv )
{
uint32_t size;
struct sockaddr_ll my_addr[MAX_CONNECTIONS], peer_addr;
int i_ifindex;
int ec;
struct ifreq s_ifr; /* points to one interface returned from ioctl */
int tmp;
FILE * fp;
char server[254];
int count = 0;
int first_time = 1;
int z;
int first_mmap = 1;
#define HWADDR_len 6
#define IP_len 4
int s,s2,i;
struct ifreq ifr,ifr2;
int ret = -1;
if (argc != 2) {
printf("Usage: %s <INPUT_FILE>\n", argv[0]);
return 1;
}
// const char *ifname = argv[2];
// const char *ip = argv[3];
int nl_sock = open_netlink();
if (do_route_dump_requst(nl_sock) < 0) {
perror("Failed to perfom request");
close(nl_sock);
return -1;
}
get_route_dump_response(nl_sock);
close (nl_sock);
test_arping(ifname, ip);
s = socket(AF_INET, SOCK_DGRAM, 0);
s2 = socket(AF_INET, SOCK_DGRAM, 0);
strcpy(ifr.ifr_name, ifname);
strcpy(ifr2.ifr_name, ifname);
ioctl(s, SIOCGIFHWADDR, &ifr);
ioctl(s2, SIOCGIFADDR, &ifr2);
struct sockaddr_in* ipaddr = (struct sockaddr_in*)&ifr2.ifr_addr;
close(s);
while (!done)
{
if (first_time)
{
if (count < MAX_CONNECTIONS) z = count;
}
count++;
if (count == 10000) break;
fp = fopen(argv[1], "r");
if (!fp)
exit(EXIT_FAILURE);
fd_socket[z] = socket(PF_PACKET, SOCK_RAW|SOCK_NONBLOCK, htons(ETH_P_ALL));
if(fd_socket[z] == -1)
{
perror("socket");
return EXIT_FAILURE;
}
/* clear structure */
memset(&my_addr[z], 0, sizeof(struct sockaddr_ll));
my_addr[z].sll_family = PF_PACKET;
my_addr[z].sll_protocol = htons(ETH_P_ALL);
str_devname = ifname;
//strcpy (str_devname, ifname);
/* initialize interface struct */
strncpy (s_ifr.ifr_name, str_devname, sizeof(s_ifr.ifr_name));
/* Get the broad cast address */
ec = ioctl(fd_socket[z], SIOCGIFINDEX, &s_ifr);
if(ec == -1)
{
perror("iotcl");
return EXIT_FAILURE;
}
/* update with interface index */
i_ifindex = s_ifr.ifr_ifindex;
s_ifr.ifr_mtu = 7200;
/* update the mtu through ioctl */
ec = ioctl(fd_socket[z], SIOCSIFMTU, &s_ifr);
if(ec == -1)
{
perror("iotcl");
return EXIT_FAILURE;
}
/* set sockaddr info */
memset(&my_addr[z], 0, sizeof(struct sockaddr_ll));
my_addr[z].sll_family = AF_PACKET;
my_addr[z].sll_protocol = ETH_P_ALL;
my_addr[z].sll_ifindex = i_ifindex;
/* bind port */
if (bind(fd_socket[z], (struct sockaddr *)&my_addr[z], sizeof(struct sockaddr_ll)) == -1)
{
perror("bind");
return EXIT_FAILURE;
}
/* prepare Tx ring request */
s_packet_req.tp_block_size = c_buffer_sz;
s_packet_req.tp_frame_size = c_buffer_sz;
s_packet_req.tp_block_nr = c_buffer_nb;
s_packet_req.tp_frame_nr = c_buffer_nb;
/* calculate memory to mmap in the kernel */
size = s_packet_req.tp_block_size * s_packet_req.tp_block_nr;
/* set packet loss option */
tmp = mode_loss;
if (setsockopt(fd_socket[z], SOL_PACKET, PACKET_LOSS, (char *)&tmp, sizeof(tmp))<0)
{
perror("setsockopt: PACKET_LOSS");
return EXIT_FAILURE;
}
/* send TX ring request */
if (setsockopt(fd_socket[z], SOL_PACKET, PACKET_TX_RING, (char *)&s_packet_req, sizeof(s_packet_req))<0)
{
perror("setsockopt: PACKET_TX_RING");
return EXIT_FAILURE;
}
/* change send buffer size */
if(c_sndbuf_sz) {
printf("send buff size = %d\n", c_sndbuf_sz);
if (setsockopt(fd_socket[z], SOL_SOCKET, SO_SNDBUF, &c_sndbuf_sz, sizeof(c_sndbuf_sz))< 0)
{
perror("getsockopt: SO_SNDBUF");
return EXIT_FAILURE;
}
}
/* get data offset */
data_offset = TPACKET_HDRLEN - sizeof(struct sockaddr_ll);
/* mmap Tx ring buffers memory */
ps_header_start = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd_socket[z], 0);
if (ps_header_start == (void*)-1)
{
perror("mmap");
return EXIT_FAILURE;
}
int i,j;
int i_index = 0;
char * data;
int first_loop = 1;
struct tpacket_hdr * ps_header;
int ec_send = 0;
for(i=1; i <= c_packet_nb; i++)
{
int i_index_start = i_index;
int loop = 1;
/* get free buffer */
do {
ps_header = ((struct tpacket_hdr *)((void *)ps_header_start + (c_buffer_sz*i_index)));
data = ((void*) ps_header) + data_offset;
switch((volatile uint32_t)ps_header->tp_status)
{
case TP_STATUS_AVAILABLE:
/* fill data in buffer */
if(first_loop) {
//Datagram to represent the packet
char datagram[4096] , source_ip[32] , *data2, *pseudogram;
//zero out the packet buffer
memset (datagram, 0, 4096);
//Ethernet header
struct ether_header *eh = (struct ether_header *) datagram;
//IP header
struct iphdr *iph = (struct iphdr *) (datagram + sizeof (struct ether_header));
//TCP header
struct tcphdr *tcph = (struct tcphdr *) (datagram + sizeof (struct ether_header) + sizeof (struct ip));
struct sockaddr_in sin;
struct pseudo_header psh;
//Data part
data2 = datagram + sizeof(struct ether_header) + sizeof(struct iphdr) + sizeof(struct tcphdr);
strcpy(data2 , "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
//some address resolution
strcpy(source_ip , inet_ntoa(ipaddr->sin_addr));
sin.sin_family = AF_INET;
sin.sin_port = htons(80);
if (fscanf(fp, "%253s", server) == 1)
sin.sin_addr.s_addr = inet_addr (server);
else
{
done = 1;
break;
}
//Fill in the Ethernet Header
//eh->ether_dhost[0] = 0x70;
//eh->ether_dhost[1] = 0x97;
//eh->ether_dhost[2] = 0x41;
//eh->ether_dhost[3] = 0x4b;
//eh->ether_dhost[4] = 0x1e;
//eh->ether_dhost[5] = 0xc2;
eh->ether_dhost[0] = arp_resp->sender_mac[0];
eh->ether_dhost[1] = arp_resp->sender_mac[1];
eh->ether_dhost[2] = arp_resp->sender_mac[2];
eh->ether_dhost[3] = arp_resp->sender_mac[3];
eh->ether_dhost[4] = arp_resp->sender_mac[4];
eh->ether_dhost[5] = arp_resp->sender_mac[5];
memcpy(eh->ether_shost, ifr.ifr_hwaddr.sa_data, HWADDR_len);
eh->ether_type = 0x0008;
//Fill in the IP Header
iph->ihl = 5;
iph->version = 4;
iph->tos = 0;
iph->tot_len = sizeof (struct iphdr) + sizeof (struct tcphdr) + strlen(data);
iph->id = htonl (54321); //Id of this packet
iph->frag_off = 0;
iph->ttl = 255;
iph->protocol = IPPROTO_TCP;
iph->check = 0; //Set to 0 before calculating checksum
iph->saddr = inet_addr ( source_ip ); //Spoof the source ip address
iph->daddr = sin.sin_addr.s_addr;
//Ip checksum
iph->check = csum ((unsigned short *) datagram, iph->tot_len);
//TCP Header
tcph->source = htons (1234);
tcph->dest = htons (80);
tcph->seq = 0;
tcph->ack_seq = 0;
tcph->doff = 5; //tcp header size
tcph->fin=0;
tcph->syn=1;
tcph->rst=0;
tcph->psh=0;
tcph->ack=0;
tcph->urg=0;
tcph->window = htons (5840); // maximum allowed window size
tcph->check = 0; //leave checksum 0 now, filled later by pseudo header
tcph->urg_ptr = 0;
//Now the TCP checksum
psh.source_address = inet_addr( source_ip );
psh.dest_address = sin.sin_addr.s_addr;
psh.placeholder = 0;
psh.protocol = IPPROTO_TCP;
psh.tcp_length = htons(sizeof(struct tcphdr) + strlen(data) );
int psize = sizeof(struct pseudo_header) + sizeof(struct tcphdr) + strlen(data);
pseudogram = malloc(psize);
memcpy(pseudogram , (char*) &psh , sizeof (struct pseudo_header));
memcpy(pseudogram + sizeof(struct pseudo_header) , tcph , sizeof(struct tcphdr) + strlen(data));
tcph->check = csum( (unsigned short*) pseudogram , psize);
memcpy(data, datagram, 4096);
free(pseudogram);
// for(j=0;j<c_packet_sz;j++)
// data[j] = j;
}
loop = 0;
break;
case TP_STATUS_WRONG_FORMAT:
printf("An error has occured during transfer\n");
exit(EXIT_FAILURE);
break;
default:
/* nothing to do => schedule : useful if no SMP */
usleep(0);
break;
}
}
while(loop == 1);
i_index ++;
if(i_index >= c_buffer_nb)
{
i_index = 0;
first_loop = 0;
}
/* update packet len */
ps_header->tp_len = c_packet_sz;
/* set header flag to USER (trigs xmit)*/
ps_header->tp_status = TP_STATUS_SEND_REQUEST;
/* if smp mode selected */
if(!mode_thread)
{
/* send all packets */
if( ((i&c_send_mask)==0) || (ec_send < 0) || (i == c_packet_nb) )
{
/* send all buffers with TP_STATUS_SEND_REQUEST */
/* Don't wait end of transfer */
//ec_send = (int) task_send((void*)0);
}
}
else if(c_error) {
if(i == (c_packet_nb/2))
{
int ec_close;
if(c_error == 1) {
ec_close = close(fd_socket[z]);
}
if(c_error == 2) {
if (setsockopt(fd_socket[z], SOL_PACKET, PACKET_TX_RING, (char *)&s_packet_req, sizeof(s_packet_req))<0)
{
perror("setsockopt: PACKET_TX_RING");
//return EXIT_FAILURE;
}
}
break;
}
}
}
//int ec_send;
static int total=0;
int blocking = 1;
/* send all buffers with TP_STATUS_SEND_REQUEST */
/* Wait end of transfer */
ec_send = sendto(fd_socket[z],NULL,0,(blocking? 0 : MSG_DONTWAIT),(struct sockaddr *) ps_sockaddr,sizeof(struct sockaddr_ll));
if(ec_send < 0) {
perror("sendto");
}
else if ( ec_send == 0 ) {
/* nothing to do => schedule : useful if no SMP */
usleep(0);
}
else {
total += ec_send/(c_packet_sz);
printf("send %d packets (+%d bytes)\n",total, ec_send);
fflush(0);
}
//ps_header_start = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd_socket[z], 0);
if (munmap(ps_header_start, size) == -1)
{
perror("munmap");
exit(EXIT_FAILURE);
}
close(fd_socket[z]);
}
return 1;
}

How do I send to an IP address with PF_PACKET?

I'm trying to learn how to program PACKET_MMAP based application so I can get performance gains. I want to start by learning how to send packets to a specified IP address. I've been learning by borrowing sections of code from https://sites.google.com/site/packetmmap/ which uses PF_PACKET as first argument to socket().
I've managed to put together a program which sends one packet (I don't know where to). How do I set the IP address for sending with PF_PACKET?
Here is the code I have so far:
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <net/if.h>
static char * str_devname= NULL;
static int mode_loss = 0;
static int c_packet_sz = 150;
static int c_packet_nb = 1000;
static int c_buffer_sz = 1024*8;
static int c_buffer_nb = 1024;
static int c_sndbuf_sz = 0;
static int c_send_mask = 127;
static int c_error = 0;
static int mode_thread = 0;
volatile int fd_socket;
volatile int data_offset = 0;
volatile struct tpacket_hdr * ps_header_start;
volatile struct sockaddr_ll *ps_sockaddr = NULL;
volatile int shutdown_flag = 0;
struct tpacket_req s_packet_req;
int main( int argc, char ** argv )
{
uint32_t size;
struct sockaddr_ll my_addr, peer_addr;
int i_ifindex;
int ec;
struct ifreq s_ifr; /* points to one interface returned from ioctl */
int tmp;
fd_socket = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if(fd_socket == -1)
{
perror("socket");
return EXIT_FAILURE;
}
/* clear structure */
memset(&my_addr, 0, sizeof(struct sockaddr_ll));
my_addr.sll_family = PF_PACKET;
my_addr.sll_protocol = htons(ETH_P_ALL);
str_devname = "enp3s0";
/* initialize interface struct */
strncpy (s_ifr.ifr_name, str_devname, sizeof(s_ifr.ifr_name));
/* Get the broad cast address */
ec = ioctl(fd_socket, SIOCGIFINDEX, &s_ifr);
if(ec == -1)
{
perror("iotcl");
return EXIT_FAILURE;
}
/* update with interface index */
i_ifindex = s_ifr.ifr_ifindex;
/* set sockaddr info */
memset(&my_addr, 0, sizeof(struct sockaddr_ll));
my_addr.sll_family = AF_PACKET;
my_addr.sll_protocol = ETH_P_ALL;
my_addr.sll_ifindex = i_ifindex;
/* bind port */
if (bind(fd_socket, (struct sockaddr *)&my_addr, sizeof(struct sockaddr_ll)) == -1)
{
perror("bind");
return EXIT_FAILURE;
}
/* prepare Tx ring request */
s_packet_req.tp_block_size = c_buffer_sz;
s_packet_req.tp_frame_size = c_buffer_sz;
s_packet_req.tp_block_nr = c_buffer_nb;
s_packet_req.tp_frame_nr = c_buffer_nb;
/* calculate memory to mmap in the kernel */
size = s_packet_req.tp_block_size * s_packet_req.tp_block_nr;
/* set packet loss option */
tmp = mode_loss;
if (setsockopt(fd_socket, SOL_PACKET, PACKET_LOSS, (char *)&tmp, sizeof(tmp))<0)
{
perror("setsockopt: PACKET_LOSS");
return EXIT_FAILURE;
}
/* send TX ring request */
if (setsockopt(fd_socket, SOL_PACKET, PACKET_TX_RING, (char *)&s_packet_req, sizeof(s_packet_req))<0)
{
perror("setsockopt: PACKET_TX_RING");
return EXIT_FAILURE;
}
/* change send buffer size */
if(c_sndbuf_sz) {
printf("send buff size = %d\n", c_sndbuf_sz);
if (setsockopt(fd_socket, SOL_SOCKET, SO_SNDBUF, &c_sndbuf_sz, sizeof(c_sndbuf_sz))< 0)
{
perror("getsockopt: SO_SNDBUF");
return EXIT_FAILURE;
}
}
/* get data offset */
data_offset = TPACKET_HDRLEN - sizeof(struct sockaddr_ll);
/* mmap Tx ring buffers memory */
ps_header_start = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd_socket, 0);
if (ps_header_start == (void*)-1)
{
perror("mmap");
return EXIT_FAILURE;
}
int i,j;
int i_index = 0;
char * data;
int first_loop = 1;
struct tpacket_hdr * ps_header;
int ec_send = 0;
for(i=1; i <= c_packet_nb; i++)
{
int i_index_start = i_index;
int loop = 1;
/* get free buffer */
do {
ps_header = ((struct tpacket_hdr *)((void *)ps_header_start + (c_buffer_sz*i_index)));
data = ((void*) ps_header) + data_offset;
switch((volatile uint32_t)ps_header->tp_status)
{
case TP_STATUS_AVAILABLE:
/* fill data in buffer */
if(first_loop) {
for(j=0;j<c_packet_sz;j++)
data[j] = j;
}
loop = 0;
break;
case TP_STATUS_WRONG_FORMAT:
printf("An error has occured during transfer\n");
exit(EXIT_FAILURE);
break;
default:
/* nothing to do => schedule : useful if no SMP */
usleep(0);
break;
}
}
while(loop == 1);
i_index ++;
if(i_index >= c_buffer_nb)
{
i_index = 0;
first_loop = 0;
}
/* update packet len */
ps_header->tp_len = c_packet_sz;
/* set header flag to USER (trigs xmit)*/
ps_header->tp_status = TP_STATUS_SEND_REQUEST;
/* if smp mode selected */
if(!mode_thread)
{
/* send all packets */
if( ((i&c_send_mask)==0) || (ec_send < 0) || (i == c_packet_nb) )
{
/* send all buffers with TP_STATUS_SEND_REQUEST */
/* Don't wait end of transfer */
//ec_send = (int) task_send((void*)0);
}
}
else if(c_error) {
if(i == (c_packet_nb/2))
{
int ec_close;
if(c_error == 1) {
ec_close = close(fd_socket);
}
if(c_error == 2) {
if (setsockopt(fd_socket, SOL_PACKET, PACKET_TX_RING, (char *)&s_packet_req, sizeof(s_packet_req))<0)
{
perror("setsockopt: PACKET_TX_RING");
//return EXIT_FAILURE;
}
}
break;
}
}
}
//int ec_send;
static int total=0;
int blocking = 1;
do
{
/* send all buffers with TP_STATUS_SEND_REQUEST */
/* Wait end of transfer */
ec_send = sendto(fd_socket,NULL,0,(blocking? 0 : MSG_DONTWAIT),(struct sockaddr *) ps_sockaddr,sizeof(struct sockaddr_ll));
if(ec_send < 0) {
perror("send");
break;
}
else if ( ec_send == 0 ) {
/* nothing to do => schedule : useful if no SMP */
usleep(0);
}
else {
total += ec_send/(c_packet_sz);
printf("send %d packets (+%d bytes)\n",total, ec_send);
fflush(0);
}
} while(blocking && !shutdown_flag);
return 1;
}
The ethernet, ip and tcp headers have to be populated. Here is what I came up with:
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/tcp.h> //Provides declarations for tcp header
#include <netinet/ip.h> //Provides declarations for ip header
#include <netinet/ether.h>
static char * str_devname= NULL;
static int mode_loss = 0;
static int c_packet_sz = 150;
static int c_packet_nb = 1000;
static int c_buffer_sz = 1024*8;
static int c_buffer_nb = 1024;
static int c_sndbuf_sz = 0;
static int c_send_mask = 127;
static int c_error = 0;
static int c_mtu = 0;
static int mode_thread = 0;
volatile int fd_socket;
volatile int data_offset = 0;
volatile struct tpacket_hdr * ps_header_start;
volatile struct sockaddr_ll *ps_sockaddr = NULL;
volatile int shutdown_flag = 0;
int done = 0;
struct tpacket_req s_packet_req;
/*
96 bit (12 bytes) pseudo header needed for tcp header checksum calculation
*/
struct pseudo_header
{
u_int32_t source_address;
u_int32_t dest_address;
u_int8_t placeholder;
u_int8_t protocol;
u_int16_t tcp_length;
};
/*
Generic checksum calculation function
*/
unsigned short csum(unsigned short *ptr,int nbytes)
{
register long sum;
unsigned short oddbyte;
register short answer;
sum=0;
while(nbytes>1) {
sum+=*ptr++;
nbytes-=2;
}
if(nbytes==1) {
oddbyte=0;
*((u_char*)&oddbyte)=*(u_char*)ptr;
sum+=oddbyte;
}
sum = (sum>>16)+(sum & 0xffff);
sum = sum + (sum>>16);
answer=(short)~sum;
return(answer);
}
int main( int argc, char ** argv )
{
uint32_t size;
struct sockaddr_ll my_addr, peer_addr;
int i_ifindex;
int ec;
struct ifreq s_ifr; /* points to one interface returned from ioctl */
int tmp;
FILE * fp;
char server[254];
while (!done)
{
fp = fopen(argv[1], "r");
if (!fp)
exit(EXIT_FAILURE);
fd_socket = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if(fd_socket == -1)
{
perror("socket");
return EXIT_FAILURE;
}
/* clear structure */
memset(&my_addr, 0, sizeof(struct sockaddr_ll));
my_addr.sll_family = PF_PACKET;
my_addr.sll_protocol = htons(ETH_P_ALL);
str_devname = "enp3s0";
/* initialize interface struct */
strncpy (s_ifr.ifr_name, str_devname, sizeof(s_ifr.ifr_name));
/* Get the broad cast address */
ec = ioctl(fd_socket, SIOCGIFINDEX, &s_ifr);
if(ec == -1)
{
perror("iotcl");
return EXIT_FAILURE;
}
/* update with interface index */
i_ifindex = s_ifr.ifr_ifindex;
s_ifr.ifr_mtu = 7200;
/* update the mtu through ioctl */
ec = ioctl(fd_socket, SIOCSIFMTU, &s_ifr);
if(ec == -1)
{
perror("iotcl");
return EXIT_FAILURE;
}
/* set sockaddr info */
memset(&my_addr, 0, sizeof(struct sockaddr_ll));
my_addr.sll_family = AF_PACKET;
my_addr.sll_protocol = ETH_P_ALL;
my_addr.sll_ifindex = i_ifindex;
/* bind port */
if (bind(fd_socket, (struct sockaddr *)&my_addr, sizeof(struct sockaddr_ll)) == -1)
{
perror("bind");
return EXIT_FAILURE;
}
/* prepare Tx ring request */
s_packet_req.tp_block_size = c_buffer_sz;
s_packet_req.tp_frame_size = c_buffer_sz;
s_packet_req.tp_block_nr = c_buffer_nb;
s_packet_req.tp_frame_nr = c_buffer_nb;
/* calculate memory to mmap in the kernel */
size = s_packet_req.tp_block_size * s_packet_req.tp_block_nr;
/* set packet loss option */
tmp = mode_loss;
if (setsockopt(fd_socket, SOL_PACKET, PACKET_LOSS, (char *)&tmp, sizeof(tmp))<0)
{
perror("setsockopt: PACKET_LOSS");
return EXIT_FAILURE;
}
/* send TX ring request */
if (setsockopt(fd_socket, SOL_PACKET, PACKET_TX_RING, (char *)&s_packet_req, sizeof(s_packet_req))<0)
{
perror("setsockopt: PACKET_TX_RING");
return EXIT_FAILURE;
}
/* change send buffer size */
if(c_sndbuf_sz) {
printf("send buff size = %d\n", c_sndbuf_sz);
if (setsockopt(fd_socket, SOL_SOCKET, SO_SNDBUF, &c_sndbuf_sz, sizeof(c_sndbuf_sz))< 0)
{
perror("getsockopt: SO_SNDBUF");
return EXIT_FAILURE;
}
}
/* get data offset */
data_offset = TPACKET_HDRLEN - sizeof(struct sockaddr_ll);
/* mmap Tx ring buffers memory */
ps_header_start = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd_socket, 0);
if (ps_header_start == (void*)-1)
{
perror("mmap");
return EXIT_FAILURE;
}
int i,j;
int i_index = 0;
char * data;
int first_loop = 1;
struct tpacket_hdr * ps_header;
int ec_send = 0;
for(i=1; i <= c_packet_nb; i++)
{
int i_index_start = i_index;
int loop = 1;
/* get free buffer */
do {
ps_header = ((struct tpacket_hdr *)((void *)ps_header_start + (c_buffer_sz*i_index)));
data = ((void*) ps_header) + data_offset;
switch((volatile uint32_t)ps_header->tp_status)
{
case TP_STATUS_AVAILABLE:
/* fill data in buffer */
if(first_loop) {
//Datagram to represent the packet
char datagram[4096] , source_ip[32] , *data2, *pseudogram;
//zero out the packet buffer
memset (datagram, 0, 4096);
//Ethernet header
struct ether_header *eh = (struct ether_header *) datagram;
//IP header
struct iphdr *iph = (struct iphdr *) (datagram + sizeof (struct ether_header));
//TCP header
struct tcphdr *tcph = (struct tcphdr *) (datagram + sizeof (struct ether_header) + sizeof (struct ip));
struct sockaddr_in sin;
struct pseudo_header psh;
//Data part
data2 = datagram + sizeof(struct ether_header) + sizeof(struct iphdr) + sizeof(struct tcphdr);
strcpy(data2 , "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
//some address resolution
strcpy(source_ip , "192.168.1.70");
sin.sin_family = AF_INET;
sin.sin_port = htons(80);
if (fscanf(fp, "%253s", server) == 1)
sin.sin_addr.s_addr = inet_addr (server);
else
{
done = 1;
break;
}
//Fill in the Ethernet Header
eh->ether_dhost[0] = 0x70;
eh->ether_dhost[1] = 0x97;
eh->ether_dhost[2] = 0x41;
eh->ether_dhost[3] = 0x4b;
eh->ether_dhost[4] = 0x1e;
eh->ether_dhost[5] = 0xc2;
eh->ether_shost[0] = 0xc4;
eh->ether_shost[1] = 0x65;
eh->ether_shost[2] = 0x16;
eh->ether_shost[3] = 0x24;
eh->ether_shost[4] = 0xd5;
eh->ether_shost[5] = 0x9a;
eh->ether_type = 0x0008;
//Fill in the IP Header
iph->ihl = 5;
iph->version = 4;
iph->tos = 0;
iph->tot_len = sizeof (struct iphdr) + sizeof (struct tcphdr) + strlen(data);
iph->id = htonl (54321); //Id of this packet
iph->frag_off = 0;
iph->ttl = 255;
iph->protocol = IPPROTO_TCP;
iph->check = 0; //Set to 0 before calculating checksum
iph->saddr = inet_addr ( source_ip ); //Spoof the source ip address
iph->daddr = sin.sin_addr.s_addr;
//Ip checksum
iph->check = csum ((unsigned short *) datagram, iph->tot_len);
//TCP Header
tcph->source = htons (1234);
tcph->dest = htons (80);
tcph->seq = 0;
tcph->ack_seq = 0;
tcph->doff = 5; //tcp header size
tcph->fin=0;
tcph->syn=1;
tcph->rst=0;
tcph->psh=0;
tcph->ack=0;
tcph->urg=0;
tcph->window = htons (5840); // maximum allowed window size
tcph->check = 0; //leave checksum 0 now, filled later by pseudo header
tcph->urg_ptr = 0;
//Now the TCP checksum
psh.source_address = inet_addr( source_ip );
psh.dest_address = sin.sin_addr.s_addr;
psh.placeholder = 0;
psh.protocol = IPPROTO_TCP;
psh.tcp_length = htons(sizeof(struct tcphdr) + strlen(data) );
int psize = sizeof(struct pseudo_header) + sizeof(struct tcphdr) + strlen(data);
pseudogram = malloc(psize);
memcpy(pseudogram , (char*) &psh , sizeof (struct pseudo_header));
memcpy(pseudogram + sizeof(struct pseudo_header) , tcph , sizeof(struct tcphdr) + strlen(data));
tcph->check = csum( (unsigned short*) pseudogram , psize);
memcpy(data, datagram, 4096);
// for(j=0;j<c_packet_sz;j++)
// data[j] = j;
}
loop = 0;
break;
case TP_STATUS_WRONG_FORMAT:
printf("An error has occured during transfer\n");
exit(EXIT_FAILURE);
break;
default:
/* nothing to do => schedule : useful if no SMP */
usleep(0);
break;
}
}
while(loop == 1);
i_index ++;
if(i_index >= c_buffer_nb)
{
i_index = 0;
first_loop = 0;
}
/* update packet len */
ps_header->tp_len = c_packet_sz;
/* set header flag to USER (trigs xmit)*/
ps_header->tp_status = TP_STATUS_SEND_REQUEST;
/* if smp mode selected */
if(!mode_thread)
{
/* send all packets */
if( ((i&c_send_mask)==0) || (ec_send < 0) || (i == c_packet_nb) )
{
/* send all buffers with TP_STATUS_SEND_REQUEST */
/* Don't wait end of transfer */
//ec_send = (int) task_send((void*)0);
}
}
else if(c_error) {
if(i == (c_packet_nb/2))
{
int ec_close;
if(c_error == 1) {
ec_close = close(fd_socket);
}
if(c_error == 2) {
if (setsockopt(fd_socket, SOL_PACKET, PACKET_TX_RING, (char *)&s_packet_req, sizeof(s_packet_req))<0)
{
perror("setsockopt: PACKET_TX_RING");
//return EXIT_FAILURE;
}
}
break;
}
}
}
//int ec_send;
static int total=0;
int blocking = 1;
/* send all buffers with TP_STATUS_SEND_REQUEST */
/* Wait end of transfer */
ec_send = sendto(fd_socket,NULL,0,(blocking? 0 : MSG_DONTWAIT),(struct sockaddr *) ps_sockaddr,sizeof(struct sockaddr_ll));
if(ec_send < 0) {
perror("send");
}
else if ( ec_send == 0 ) {
/* nothing to do => schedule : useful if no SMP */
usleep(0);
}
else {
total += ec_send/(c_packet_sz);
printf("send %d packets (+%d bytes)\n",total, ec_send);
fflush(0);
}
close(fd_socket);
}
return 1;
}

arp request and reply using c socket programming

I am trying to receive and send arp packets using c programming in Linux (Ubuntu)
My program works fine (i.e. runs without any error), but I cannot trace the packets using Wireshark.
source code:
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <asm/types.h>
#include <math.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
#define BUF_SIZE 42
#define DEVICE "eth0"
#define ETH_P_NULL 0x0
#define ETH_MAC_LEN ETH_ALEN
#define ETH_ARP 0x0806
int s = 0; /*Socketdescriptor*/
void* buffer = NULL;
long total_packets = 0;
long answered_packets = 0;
void sigint(int signum);
struct __attribute__((packed)) arp_header
{
unsigned short arp_hd;
unsigned short arp_pr;
unsigned char arp_hdl;
unsigned char arp_prl;
unsigned short arp_op;
unsigned char arp_sha[6];
unsigned char arp_spa[4];
unsigned char arp_dha[6];
unsigned char arp_dpa[4];
};
int main(void) {
buffer = (void*)malloc(BUF_SIZE); /*Buffer for Ethernet Frame*/
unsigned char* etherhead = buffer; /*Pointer to Ethenet Header*/
struct ethhdr *eh = (struct ethhdr *)etherhead; /*Another pointer to
ethernet header*/
unsigned char* arphead = buffer + 14;
struct arp_header *ah;
unsigned char src_mac[6]; /*our MAC address*/
struct ifreq ifr;
struct sockaddr_ll socket_address;
int ifindex = 0; /*Ethernet Interface index*/
int i;
int length; /*length of received packet*/
int sent;
printf("Server started, entering initialiation phase...\n");
/*open socket*/
s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (s == -1) {
perror("socket():");
exit(1);
}
printf("Successfully opened socket: %i\n", s);
/*retrieve ethernet interface index*/
strncpy(ifr.ifr_name, DEVICE, IFNAMSIZ);
if (ioctl(s, SIOCGIFINDEX, &ifr) == -1) {
perror("SIOCGIFINDEX");
exit(1);
}
ifindex = ifr.ifr_ifindex;
printf("Successfully got interface index: %i\n", ifindex);
/*retrieve corresponding MAC*/
if (ioctl(s, SIOCGIFHWADDR, &ifr) == -1) {
perror("SIOCGIFINDEX");
exit(1);
}
for (i = 0; i < 6; i++) {
src_mac[i] = ifr.ifr_hwaddr.sa_data[i];
}
printf("Successfully got our MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
src_mac[0],src_mac[1],src_mac[2],src_mac[3],src_mac[4],src_mac[5]);
/*prepare sockaddr_ll*/
socket_address.sll_family = PF_PACKET;
socket_address.sll_protocol = htons(ETH_P_IP);
socket_address.sll_ifindex = ifindex;
socket_address.sll_hatype = ARPHRD_ETHER;
socket_address.sll_pkttype = PACKET_OTHERHOST;
socket_address.sll_halen = 0;
socket_address.sll_addr[6] = 0x00;
socket_address.sll_addr[7] = 0x00;
/*establish signal handler*/
signal(SIGINT, sigint);
printf("Successfully established signal handler for SIGINT\n");
printf("We are in production state, waiting for incoming packets....\n");
while (1) {
/*Wait for incoming packet...*/
length = recvfrom(s, buffer, BUF_SIZE, 0, NULL, NULL);
if (length == -1)
{
perror("recvfrom():");
exit(1);
}
if(htons(eh->h_proto) == 0x806)
{
unsigned char buf_arp_dha[6];
unsigned char buf_arp_dpa[4];
ah = (struct arp_header *)arphead;
if(htons(ah->arp_op) != 0x0001)
continue;
printf("buffer is---------------- %s \n",(char*)ah);
printf("H/D TYPE : %x PROTO TYPE : %x \n",ah->arp_hd,ah->arp_pr);
printf("H/D leng : %x PROTO leng : %x \n",ah->arp_hdl,ah->arp_prl);
printf("OPERATION : %x \n", ah->arp_op);
printf("SENDER MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
ah->arp_sha[0],
ah->arp_sha[1],
ah->arp_sha[2],
ah->arp_sha[3],
ah->arp_sha[4],
ah->arp_sha[5]
);
printf("SENDER IP address: %02d:%02d:%02d:%02d\n",
ah->arp_spa[0],
ah->arp_spa[1],
ah->arp_spa[2],
ah->arp_spa[3]
);
if(ah->arp_spa[0]==10&&ah->arp_spa[1]==00&&ah->arp_spa[2]==00&&ah->arp_spa[3]==01)
{
printf("Sender ip is .............bam bam..........................................\n");
system("sudo arp -s 10.0.0.1 00:1e:73:91:04:0d");
}
printf("TARGET MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
ah->arp_dha[0],
ah->arp_dha[1],
ah->arp_dha[2],
ah->arp_dha[3],
ah->arp_dha[4],
ah->arp_dha[5]
);
printf("TARGET IP address: %02d:%02d:%02d:%02d\n",
ah->arp_dpa[0],
ah->arp_dpa[1],
ah->arp_dpa[2],
ah->arp_dpa[3]
);
printf("+++++++++++++++++++++++++++++++++++++++\n" );
printf("ETHER DST MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
eh->h_dest[0],
eh->h_dest[1],
eh->h_dest[2],
eh->h_dest[3],
eh->h_dest[4],
eh->h_dest[5]
);
printf("ETHER SRC MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
eh->h_source[0],
eh->h_source[1],
eh->h_source[2],
eh->h_source[3],
eh->h_source[4],
eh->h_source[5]
);
memcpy( (void*)etherhead, (const void*)(etherhead+ETH_MAC_LEN),
ETH_MAC_LEN);
memcpy( (void*)(etherhead+ETH_MAC_LEN), (const void*)src_mac,
ETH_MAC_LEN);
eh->h_proto = ETH_ARP;
printf("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& \n");
printf("ETHER DST MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
eh->h_dest[0],
eh->h_dest[1],
eh->h_dest[2],
eh->h_dest[3],
eh->h_dest[4],
eh->h_dest[5]
);
printf("ETHER SRC MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
eh->h_source[0],
eh->h_source[1],
eh->h_source[2],
eh->h_source[3],
eh->h_source[4],
eh->h_source[5]
);
ah->arp_hd = ntohs(ah->arp_hd);
ah->arp_pr = ntohs(ah->arp_pr);
ah->arp_op = 0x0002;
buf_arp_dpa[0] = ah->arp_dpa[0];
buf_arp_dpa[1] = ah->arp_dpa[1];
buf_arp_dpa[2] = ah->arp_dpa[2];
buf_arp_dpa[3] = ah->arp_dpa[3];
ah->arp_dha[0] = ah->arp_sha[0];
ah->arp_dha[1] = ah->arp_sha[1];
ah->arp_dha[2] = ah->arp_sha[2];
ah->arp_dha[3] = ah->arp_sha[3];
ah->arp_dha[4] = ah->arp_sha[4];
ah->arp_dha[5] = ah->arp_sha[5];
ah->arp_dpa[0] = ah->arp_spa[0];
ah->arp_dpa[1] = ah->arp_spa[1];
ah->arp_dpa[2] = ah->arp_spa[2];
ah->arp_dpa[3] = ah->arp_spa[3];
ah->arp_spa[0] = buf_arp_dpa[0];
ah->arp_spa[1] = buf_arp_dpa[1];
ah->arp_spa[2] = buf_arp_dpa[2];
ah->arp_spa[3] = buf_arp_dpa[3];
//change the sender mac address
ah->arp_sha[0] = 0x00;
ah->arp_sha[1] = 0x1e;
ah->arp_sha[2] = 0x73;
ah->arp_sha[3] = 0x78;
ah->arp_sha[4] = 0x9a;
ah->arp_sha[5] = 0x0d;
socket_address.sll_addr[0] = eh->h_dest[0];
socket_address.sll_addr[1] = eh->h_dest[1];
socket_address.sll_addr[2] = eh->h_dest[2];
socket_address.sll_addr[3] = eh->h_dest[3];
socket_address.sll_addr[4] = eh->h_dest[4];
socket_address.sll_addr[5] = eh->h_dest[5];
printf("=======================================\n" );
printf("SENDER MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
ah->arp_sha[0],
ah->arp_sha[1],
ah->arp_sha[2],
ah->arp_sha[3],
ah->arp_sha[4],
ah->arp_sha[5]
);
printf("SENDER IP address: %02d:%02d:%02d:%02d\n",
ah->arp_spa[0],
ah->arp_spa[1],
ah->arp_spa[2],
ah->arp_spa[3]
);
if((ah->arp_spa[0]==10 && ah->arp_spa[1]==0 && ah->arp_spa[2]==0 && ah->arp_spa[3]==1))
printf("------------------------------------------10.0.0.1-----------------------------------------\n");
printf("TARGET MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
ah->arp_dha[0],
ah->arp_dha[1],
ah->arp_dha[2],
ah->arp_dha[3],
ah->arp_dha[4],
ah->arp_dha[5]
);
printf("TARGET IP address: %02d:%02d:%02d:%02d\n",
ah->arp_dpa[0],
ah->arp_dpa[1],
ah->arp_dpa[2],
ah->arp_dpa[3]
);
printf("H/D TYPE : %x PROTO TYPE : %x \n",ah->arp_hd,ah->arp_pr);
printf("H/D leng : %x PROTO leng : %x \n",ah->arp_hdl,ah->arp_prl);
printf("OPERATION : %x \n", ah->arp_op);
sent = sendto(s, buffer, BUF_SIZE, 0, (struct
sockaddr*)&socket_address, sizeof(socket_address));
if (sent == -1)
{
perror("sendto():");
exit(1);
}
answered_packets++;
}
total_packets++;
}
}
void sigint(int signum) {
/*Clean up.......*/
struct ifreq ifr;
if (s == -1)
return;
strncpy(ifr.ifr_name, DEVICE, IFNAMSIZ);
ioctl(s, SIOCGIFFLAGS, &ifr);
ifr.ifr_flags &= ~IFF_PROMISC;
ioctl(s, SIOCSIFFLAGS, &ifr);
close(s);
free(buffer);
printf("Server terminating....\n");
printf("Totally received: %ld packets\n", total_packets);
printf("Answered %ld packets\n", answered_packets);
exit(0);
}
I took user6343961's code, did some cleaning and splicing and implemented support for automatically getting interface IP address.
Also the parameters come from CLI instead of hardcoding.
bind() is also used to get only ARP from the interface we want.
Have fun. This code works for me.
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <asm/types.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include <arpa/inet.h> //htons etc
#define PROTO_ARP 0x0806
#define ETH2_HEADER_LEN 14
#define HW_TYPE 1
#define MAC_LENGTH 6
#define IPV4_LENGTH 4
#define ARP_REQUEST 0x01
#define ARP_REPLY 0x02
#define BUF_SIZE 60
#define debug(x...) printf(x);printf("\n");
#define info(x...) printf(x);printf("\n");
#define warn(x...) printf(x);printf("\n");
#define err(x...) printf(x);printf("\n");
struct arp_header {
unsigned short hardware_type;
unsigned short protocol_type;
unsigned char hardware_len;
unsigned char protocol_len;
unsigned short opcode;
unsigned char sender_mac[MAC_LENGTH];
unsigned char sender_ip[IPV4_LENGTH];
unsigned char target_mac[MAC_LENGTH];
unsigned char target_ip[IPV4_LENGTH];
};
/*
* Converts struct sockaddr with an IPv4 address to network byte order uin32_t.
* Returns 0 on success.
*/
int int_ip4(struct sockaddr *addr, uint32_t *ip)
{
if (addr->sa_family == AF_INET) {
struct sockaddr_in *i = (struct sockaddr_in *) addr;
*ip = i->sin_addr.s_addr;
return 0;
} else {
err("Not AF_INET");
return 1;
}
}
/*
* Formats sockaddr containing IPv4 address as human readable string.
* Returns 0 on success.
*/
int format_ip4(struct sockaddr *addr, char *out)
{
if (addr->sa_family == AF_INET) {
struct sockaddr_in *i = (struct sockaddr_in *) addr;
const char *ip = inet_ntoa(i->sin_addr);
if (!ip) {
return -2;
} else {
strcpy(out, ip);
return 0;
}
} else {
return -1;
}
}
/*
* Writes interface IPv4 address as network byte order to ip.
* Returns 0 on success.
*/
int get_if_ip4(int fd, const char *ifname, uint32_t *ip) {
int err = -1;
struct ifreq ifr;
memset(&ifr, 0, sizeof(struct ifreq));
if (strlen(ifname) > (IFNAMSIZ - 1)) {
err("Too long interface name");
goto out;
}
strcpy(ifr.ifr_name, ifname);
if (ioctl(fd, SIOCGIFADDR, &ifr) == -1) {
perror("SIOCGIFADDR");
goto out;
}
if (int_ip4(&ifr.ifr_addr, ip)) {
goto out;
}
err = 0;
out:
return err;
}
/*
* Sends an ARP who-has request to dst_ip
* on interface ifindex, using source mac src_mac and source ip src_ip.
*/
int send_arp(int fd, int ifindex, const unsigned char *src_mac, uint32_t src_ip, uint32_t dst_ip)
{
int err = -1;
unsigned char buffer[BUF_SIZE];
memset(buffer, 0, sizeof(buffer));
struct sockaddr_ll socket_address;
socket_address.sll_family = AF_PACKET;
socket_address.sll_protocol = htons(ETH_P_ARP);
socket_address.sll_ifindex = ifindex;
socket_address.sll_hatype = htons(ARPHRD_ETHER);
socket_address.sll_pkttype = (PACKET_BROADCAST);
socket_address.sll_halen = MAC_LENGTH;
socket_address.sll_addr[6] = 0x00;
socket_address.sll_addr[7] = 0x00;
struct ethhdr *send_req = (struct ethhdr *) buffer;
struct arp_header *arp_req = (struct arp_header *) (buffer + ETH2_HEADER_LEN);
int index;
ssize_t ret, length = 0;
//Broadcast
memset(send_req->h_dest, 0xff, MAC_LENGTH);
//Target MAC zero
memset(arp_req->target_mac, 0x00, MAC_LENGTH);
//Set source mac to our MAC address
memcpy(send_req->h_source, src_mac, MAC_LENGTH);
memcpy(arp_req->sender_mac, src_mac, MAC_LENGTH);
memcpy(socket_address.sll_addr, src_mac, MAC_LENGTH);
/* Setting protocol of the packet */
send_req->h_proto = htons(ETH_P_ARP);
/* Creating ARP request */
arp_req->hardware_type = htons(HW_TYPE);
arp_req->protocol_type = htons(ETH_P_IP);
arp_req->hardware_len = MAC_LENGTH;
arp_req->protocol_len = IPV4_LENGTH;
arp_req->opcode = htons(ARP_REQUEST);
debug("Copy IP address to arp_req");
memcpy(arp_req->sender_ip, &src_ip, sizeof(uint32_t));
memcpy(arp_req->target_ip, &dst_ip, sizeof(uint32_t));
ret = sendto(fd, buffer, 42, 0, (struct sockaddr *) &socket_address, sizeof(socket_address));
if (ret == -1) {
perror("sendto():");
goto out;
}
err = 0;
out:
return err;
}
/*
* Gets interface information by name:
* IPv4
* MAC
* ifindex
*/
int get_if_info(const char *ifname, uint32_t *ip, char *mac, int *ifindex)
{
debug("get_if_info for %s", ifname);
int err = -1;
struct ifreq ifr;
int sd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
if (sd <= 0) {
perror("socket()");
goto out;
}
if (strlen(ifname) > (IFNAMSIZ - 1)) {
printf("Too long interface name, MAX=%i\n", IFNAMSIZ - 1);
goto out;
}
strcpy(ifr.ifr_name, ifname);
//Get interface index using name
if (ioctl(sd, SIOCGIFINDEX, &ifr) == -1) {
perror("SIOCGIFINDEX");
goto out;
}
*ifindex = ifr.ifr_ifindex;
printf("interface index is %d\n", *ifindex);
//Get MAC address of the interface
if (ioctl(sd, SIOCGIFHWADDR, &ifr) == -1) {
perror("SIOCGIFINDEX");
goto out;
}
//Copy mac address to output
memcpy(mac, ifr.ifr_hwaddr.sa_data, MAC_LENGTH);
if (get_if_ip4(sd, ifname, ip)) {
goto out;
}
debug("get_if_info OK");
err = 0;
out:
if (sd > 0) {
debug("Clean up temporary socket");
close(sd);
}
return err;
}
/*
* Creates a raw socket that listens for ARP traffic on specific ifindex.
* Writes out the socket's FD.
* Return 0 on success.
*/
int bind_arp(int ifindex, int *fd)
{
debug("bind_arp: ifindex=%i", ifindex);
int ret = -1;
// Submit request for a raw socket descriptor.
*fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
if (*fd < 1) {
perror("socket()");
goto out;
}
debug("Binding to ifindex %i", ifindex);
struct sockaddr_ll sll;
memset(&sll, 0, sizeof(struct sockaddr_ll));
sll.sll_family = AF_PACKET;
sll.sll_ifindex = ifindex;
if (bind(*fd, (struct sockaddr*) &sll, sizeof(struct sockaddr_ll)) < 0) {
perror("bind");
goto out;
}
ret = 0;
out:
if (ret && *fd > 0) {
debug("Cleanup socket");
close(*fd);
}
return ret;
}
/*
* Reads a single ARP reply from fd.
* Return 0 on success.
*/
int read_arp(int fd)
{
debug("read_arp");
int ret = -1;
unsigned char buffer[BUF_SIZE];
ssize_t length = recvfrom(fd, buffer, BUF_SIZE, 0, NULL, NULL);
int index;
if (length == -1) {
perror("recvfrom()");
goto out;
}
struct ethhdr *rcv_resp = (struct ethhdr *) buffer;
struct arp_header *arp_resp = (struct arp_header *) (buffer + ETH2_HEADER_LEN);
if (ntohs(rcv_resp->h_proto) != PROTO_ARP) {
debug("Not an ARP packet");
goto out;
}
if (ntohs(arp_resp->opcode) != ARP_REPLY) {
debug("Not an ARP reply");
goto out;
}
debug("received ARP len=%ld", length);
struct in_addr sender_a;
memset(&sender_a, 0, sizeof(struct in_addr));
memcpy(&sender_a.s_addr, arp_resp->sender_ip, sizeof(uint32_t));
debug("Sender IP: %s", inet_ntoa(sender_a));
debug("Sender MAC: %02X:%02X:%02X:%02X:%02X:%02X",
arp_resp->sender_mac[0],
arp_resp->sender_mac[1],
arp_resp->sender_mac[2],
arp_resp->sender_mac[3],
arp_resp->sender_mac[4],
arp_resp->sender_mac[5]);
ret = 0;
out:
return ret;
}
/*
*
* Sample code that sends an ARP who-has request on
* interface <ifname> to IPv4 address <ip>.
* Returns 0 on success.
*/
int test_arping(const char *ifname, const char *ip) {
int ret = -1;
uint32_t dst = inet_addr(ip);
if (dst == 0 || dst == 0xffffffff) {
printf("Invalid source IP\n");
return 1;
}
int src;
int ifindex;
char mac[MAC_LENGTH];
if (get_if_info(ifname, &src, mac, &ifindex)) {
err("get_if_info failed, interface %s not found or no IP set?", ifname);
goto out;
}
int arp_fd;
if (bind_arp(ifindex, &arp_fd)) {
err("Failed to bind_arp()");
goto out;
}
if (send_arp(arp_fd, ifindex, mac, src, dst)) {
err("Failed to send_arp");
goto out;
}
while(1) {
int r = read_arp(arp_fd);
if (r == 0) {
info("Got reply, break out");
break;
}
}
ret = 0;
out:
if (arp_fd) {
close(arp_fd);
arp_fd = 0;
}
return ret;
}
int main(int argc, const char **argv) {
int ret = -1;
if (argc != 3) {
printf("Usage: %s <INTERFACE> <DEST_IP>\n", argv[0]);
return 1;
}
const char *ifname = argv[1];
const char *ip = argv[2];
return test_arping(ifname, ip);
}
A couple of things to get your packets on the wire/air.
The proper .sll_protocol for the arp reply is ETH_P_ARP, from <linux/if_ether.h>
There was an error in endianness when setting ah->arp_op. It is a network byteorder field of 2 octets, so use htons().
In general, the code is a little confused about network and host byteorder. It currently sends out the reply very mangled, but it is unclear to me whether that is the malicious intent of the code, or an accident. In the case that you want to send real, correct IP addresses, use htonl and htons, when building the reply.
To fix endianness:
Properly include <arpa/inet.h>
Use htons(), htonl() ntohs() and ntohl(), always. Their implementation makes it a NOP, if its not needed on your platform.
When setting up data to be sent out from host, always process it with hton*()
When interpreting data from the network, always ntoh*() it before comparing with local variables.
In summary, the changes I did were 1) .sll_protocol = htons(ETH_P_ARP). (when sending data) 2) ah->arp_op = htons(ARPOP_REPLY) (in the reply arp) 3) Removed the nonsensical ntohs() on ah->arp_hd and ah->arp_pr. You dont want to convert data to host byteorder when populating the send buffer (unless you really really actually do) 4) Added ntohs() conversions and proper defines in some of the comparisons 5) some other small fixes 6) disabled the bit doing system("sudo...")!
Full code at pastebin. Here is a diff:
thuovila#glx:~/src/so/arp$ diff arp2.c arp_orig.c
13d12
< #include <arpa/inet.h>
20c19
< #define DEVICE "eth1"
---
> #define DEVICE "eth0"
25c24
< int s = -1; /*Socketdescriptor*/
---
> int s = 0; /*Socketdescriptor*/
92c91
< socket_address.sll_protocol = htons(ETH_P_ARP);
---
> socket_address.sll_protocol = htons(ETH_P_IP);
95c94
< socket_address.sll_pkttype = 0; //PACKET_OTHERHOST;
---
> socket_address.sll_pkttype = PACKET_OTHERHOST;
112c111
< if(ntohs(eh->h_proto) == ETH_P_ARP)
---
> if(htons(eh->h_proto) == 0x806)
119c118
< if(ntohs(ah->arp_op) != ARPOP_REQUEST)
---
> if(htons(ah->arp_op) != 0x0001)
139d137
< #if 0
145d142
< #endif
182c179
< eh->h_proto = htons(ETH_P_ARP);
---
> eh->h_proto = ETH_ARP;
200,201c197,198
< //ah->arp_hd = ntohs(ah->arp_hd);
< //ah->arp_pr = ntohs(ah->arp_pr);
---
> ah->arp_hd = ntohs(ah->arp_hd);
> ah->arp_pr = ntohs(ah->arp_pr);
203c200
< ah->arp_op = htons(ARPOP_REPLY);
---
> ah->arp_op = 0x0002;
EDIT Some wireshark advice. Capture ether proto 0x0806 (or arp for short). Use the pseudo device that captures any packets. Your packets should become visible.
On linux, if you want to stop the network stack from interfering, use: echo "8" > /proc/sys/net/ipv4/conf/all/arp_ignore
EDIT #2 I am not completely sure about the ETH_P_ARP. It might have been a snap judgement on my part. Using ETH_P_IP is correct in the ARP header field, but Im not sure which one to use for the packet socket sll_protocol. Also notice that socket_address.sll_pkttype = PACKET_OTHERHOST;has no effect when sending (see man 7 packet). Also the mandatory SO observation, that you should always use at least -Wall (when using gcc or clang) as a compilation flag.
EDIT #3 I changed the program a little more. and updated the answer and diff accordingly. Surprisingly it does indeed seem, that .sll_protocol needs to be ETH_P_ARP. My copy of the man 7 packet doesnt even say it is used for anything, but the packet doesnt go out on the wire as ARP without it.
I know this is a very old post. This code helped me a lot. I have modified the code to send an ARP request to an IP and extract the MAC address from the reply. Please find below my code
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <asm/types.h>
#include <math.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
#define PROTO_ARP 0x0806
#define ETH2_HEADER_LEN 14
#define HW_TYPE 1
#define PROTOCOL_TYPE 0x800
#define MAC_LENGTH 6
#define IPV4_LENGTH 4
#define ARP_REQUEST 0x01
#define ARP_REPLY 0x02
#define BUF_SIZE 60
struct arp_header
{
unsigned short hardware_type;
unsigned short protocol_type;
unsigned char hardware_len;
unsigned char protocol_len;
unsigned short opcode;
unsigned char sender_mac[MAC_LENGTH];
unsigned char sender_ip[IPV4_LENGTH];
unsigned char target_mac[MAC_LENGTH];
unsigned char target_ip[IPV4_LENGTH];
};
int main()
{
int sd;
unsigned char buffer[BUF_SIZE];
unsigned char source_ip[4] = {10,222,190,160};
unsigned char target_ip[4] = {10,222,190,139};
struct ifreq ifr;
struct ethhdr *send_req = (struct ethhdr *)buffer;
struct ethhdr *rcv_resp= (struct ethhdr *)buffer;
struct arp_header *arp_req = (struct arp_header *)(buffer+ETH2_HEADER_LEN);
struct arp_header *arp_resp = (struct arp_header *)(buffer+ETH2_HEADER_LEN);
struct sockaddr_ll socket_address;
int index,ret,length=0,ifindex;
memset(buffer,0x00,60);
/*open socket*/
sd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sd == -1) {
perror("socket():");
exit(1);
}
strcpy(ifr.ifr_name,"eth1.30");
/*retrieve ethernet interface index*/
if (ioctl(sd, SIOCGIFINDEX, &ifr) == -1) {
perror("SIOCGIFINDEX");
exit(1);
}
ifindex = ifr.ifr_ifindex;
printf("interface index is %d\n",ifindex);
/*retrieve corresponding MAC*/
if (ioctl(sd, SIOCGIFHWADDR, &ifr) == -1) {
perror("SIOCGIFINDEX");
exit(1);
}
close (sd);
for (index = 0; index < 6; index++)
{
send_req->h_dest[index] = (unsigned char)0xff;
arp_req->target_mac[index] = (unsigned char)0x00;
/* Filling the source mac address in the header*/
send_req->h_source[index] = (unsigned char)ifr.ifr_hwaddr.sa_data[index];
arp_req->sender_mac[index] = (unsigned char)ifr.ifr_hwaddr.sa_data[index];
socket_address.sll_addr[index] = (unsigned char)ifr.ifr_hwaddr.sa_data[index];
}
printf("Successfully got eth1 MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
send_req->h_source[0],send_req->h_source[1],send_req->h_source[2],
send_req->h_source[3],send_req->h_source[4],send_req->h_source[5]);
printf(" arp_reqMAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
arp_req->sender_mac[0],arp_req->sender_mac[1],arp_req->sender_mac[2],
arp_req->sender_mac[3],arp_req->sender_mac[4],arp_req->sender_mac[5]);
printf("socket_address MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
socket_address.sll_addr[0],socket_address.sll_addr[1],socket_address.sll_addr[2],
socket_address.sll_addr[3],socket_address.sll_addr[4],socket_address.sll_addr[5]);
/*prepare sockaddr_ll*/
socket_address.sll_family = AF_PACKET;
socket_address.sll_protocol = htons(ETH_P_ARP);
socket_address.sll_ifindex = ifindex;
socket_address.sll_hatype = htons(ARPHRD_ETHER);
socket_address.sll_pkttype = (PACKET_BROADCAST);
socket_address.sll_halen = MAC_LENGTH;
socket_address.sll_addr[6] = 0x00;
socket_address.sll_addr[7] = 0x00;
/* Setting protocol of the packet */
send_req->h_proto = htons(ETH_P_ARP);
/* Creating ARP request */
arp_req->hardware_type = htons(HW_TYPE);
arp_req->protocol_type = htons(ETH_P_IP);
arp_req->hardware_len = MAC_LENGTH;
arp_req->protocol_len =IPV4_LENGTH;
arp_req->opcode = htons(ARP_REQUEST);
for(index=0;index<5;index++)
{
arp_req->sender_ip[index]=(unsigned char)source_ip[index];
arp_req->target_ip[index]=(unsigned char)target_ip[index];
}
// Submit request for a raw socket descriptor.
if ((sd = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 0) {
perror ("socket() failed ");
exit (EXIT_FAILURE);
}
buffer[32]=0x00;
ret = sendto(sd, buffer, 42, 0, (struct sockaddr*)&socket_address, sizeof(socket_address));
if (ret == -1)
{
perror("sendto():");
exit(1);
}
else
{
printf(" Sent the ARP REQ \n\t");
for(index=0;index<42;index++)
{
printf("%02X ",buffer[index]);
if(index % 16 ==0 && index !=0)
{printf("\n\t");}
}
}
printf("\n\t");
memset(buffer,0x00,60);
while(1)
{
length = recvfrom(sd, buffer, BUF_SIZE, 0, NULL, NULL);
if (length == -1)
{
perror("recvfrom():");
exit(1);
}
if(htons(rcv_resp->h_proto) == PROTO_ARP)
{
//if( arp_resp->opcode == ARP_REPLY )
printf(" RECEIVED ARP RESP len=%d \n",length);
printf(" Sender IP :");
for(index=0;index<4;index++)
printf("%u.",(unsigned int)arp_resp->sender_ip[index]);
printf("\n Sender MAC :");
for(index=0;index<6;index++)
printf(" %02X:",arp_resp->sender_mac[index]);
printf("\nReceiver IP :");
for(index=0;index<4;index++)
printf(" %u.",arp_resp->target_ip[index]);
printf("\n Self MAC :");
for(index=0;index<6;index++)
printf(" %02X:",arp_resp->target_mac[index]);
printf("\n :");
break;
}
}
return 0;
}
Thanks a lot once more
Arun Kumar P

Resources