Why does this code produce an incomplete IP address? - c

I am reading a buffer from a socket (AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP)) with the following code. I am using an arp_frame struct to access the component parts of the contained ARP reply. The inet_ntoa() returns the correct first octet of the IP but the other octets are 0 producing 172.0.0.0.
Question 1 is why might this happen?
Question 2 is how can I print r bytes of the msg buffer as hex in host byte order to debug the packet?
unsigned char msg[65535];
struct ether_arp *arp_frame = (struct ether_arp *)msg;
while ((r = recv(sock, msg, sizeof(msg), 0))) {
// skip it's not an ARP REPLY
if (ntohs(arp_frame->arp_op) != ARPOP_REPLY)
continue;
for (i = 0; i < SONOS_PREFIX_NUM; i++) {
if (!memcmp(sonos_prefixes[i], arp_frame->arp_sha, 3)) {
struct in_addr addr;
addr.s_addr = *arp_frame->arp_spa;
printf("Blah: %lu\n", ntohl(*arp_frame->arp_spa));
printf("Sonos found at %s\n", inet_ntoa(addr));
}
}
}

struct ether_arp looks like this:
struct ether_arp {
struct arphdr ea_hdr; /* fixed-size header */
u_int8_t arp_sha[ETH_ALEN]; /* sender hardware address */
u_int8_t arp_spa[4]; /* sender protocol address */
u_int8_t arp_tha[ETH_ALEN]; /* target hardware address */
u_int8_t arp_tpa[4]; /* target protocol address */
};
With that in mind, I think that your addr.s_addr = *arp_frame->arp_spa; looks a little fishy. arp_frame->arp_spa yields a u_int8_t[4], which you then dereference as a pointer. I think memcpy() might be more appropriate there.

If you think the API is broken, print out the bytes.
"Methinks, Brutus, the fault is not in the stars."

Related

How to display or do eDNS in dpdk packets?

I am using l2fwd-dpdk application from which I can extract 5-tuples, and can see if DNS Packet is present or not.
Now I want to classify the DNS Packet using dpdk, for which I am failing.
Here is my code.
struct rte_udp_hdr *udp_hdr;
struct dnshdr *dns_hdr;
if (rte_be_to_cpu_16(udp_hdr->dst_port) == 53)
{
printf("DNS Packet");
char *dns_hdr = (char *)udp_hdr + sizeof(rte_udp_hdr);
}
I want to separate
Flags
Rdata
Class
TTL
and save them separately. Is there any way around, I can comfortable to use cpp wrapper as well.
DPDK as of 21.08 does not house any header or structure to typecast to DNS packet. Hence easiest way to solve the issue as mentioned by #wildplasser is to declare your custom DNS header and use it. In your code snippet, you already have struct dnshdr *dns_hdr; So the easier way is to modify your existing code to reflect
struct rte_udp_hdr *udp_hdr;
struct dnshdr *dns_hdr;
/* use DPDK mtod API to get the start of ethernet frame */
/* check for packet size, ether type, IP protocol */
/* update udp_hdr to position in the packet */
if (rte_be_to_cpu_16(udp_hdr->dst_port) == 53)
{
printf("DNS Packet");
struct dnshdr *dns_hdr = (struct dnshdr *)((char *)udp_hdr + sizeof(rte_udp_hdr));
}
Note: Possible structure definition code snippet would be
typedef struct {
uint16_t id;
uint16_t rd:1;
uint16_t tc:1;
uint16_t aa:1;
uint16_t opcode:4;
uint16_t qr:1;
uint16_t rcode:4;
uint16_t zero:3;
uint16_t ra:1;
uint16_t qcount; /* question count */
uint16_t ancount; /* Answer record count */
uint16_t nscount; /* Name Server (Autority Record) Count */
uint16_t adcount; /* Additional Record Count */
} custom_dnshdr;
custom_dnshdr *dns_hdr = (custom_dnshdr *) ((char *)udp_hdr + sizeof(rte_udp_hdr));

Get IP version from packet data

The pcap callback function returns the IP header and data as follows:
void packet_handler(u_char* param, const struct pcap_pkthdr* header, const u_char* pkt_data);
My understanding is the first 4 bits of the pkt_data is the IP version from which I can determine it is is IPv4 or IPv6. However, I've tried a few different ways to read the first 4 bits and I'm getting data that does not make sense.
For example, I defined the following structure:
struct ipdata {
u_char version : 4;
u_char dontcare : 4;
};
And then I tried to get the ip version using this code:
ipdata* pipdata;
pipdata = (ipdata*) pkt_data;
ip_ver = pipdata->version;
printf(" %d ", ip_ver);
The above method prints values of 3, 6, 9, 8 and 12. If I watch the traffic at the same time in Wireshark I see that most of the packets are IPv6.
Could someone who has done this clarify how would I go about reading the IP version?
Figure out the answer. Npcap returns the entire ethernet packet, so the first 14 bytes are the Ethernet header:
/* Length of the Ethernet Header (Data Link Layer) */
#define ETHERNET_HEADER_LEN 14
/* Ethernet addresses are 6 bytes */
#define ETHER_ADDR_LEN 6
/* Ethernet header */
struct sniff_ethernet {
u_char ether_dhost[ETHER_ADDR_LEN]; /* Destination host address (i.e. Destination MAC Address) */
u_char ether_shost[ETHER_ADDR_LEN]; /* Source host address (i.e. Source MAC Address) */
u_short ether_type; /* IP? ARP? RARP? etc */
};
You can figure out whether it is an IPv4 or IPv6 packet by looking at the ether_type in the above structure rather than the version in the IP header, such as:
/* Common ethernet types in Hex*/
#define ETHERNET_TYPE_IPv4 0x0800
#define ETHERNET_TYPE_IPv6 0x86DD
u_short eth_type;
ethernet = (struct sniff_ethernet*)(pkt_data);
eth_type = ntohs(ethernet->ether_type);
if (eth_type == ETHERNET_TYPE_IPv4) {
ipv4_handler(pkt_data);
}
else if (eth_type == ETHERNET_TYPE_IPv6)
{
ipv6_handler(pkt_data);
}
The IP header starts right after the ethernet header, so you can get it with code such as the following example for an IPv6 packet:
/* IPv6 header */
typedef struct ipv6_header
{
unsigned int
version : 4,
traffic_class : 8,
flow_label : 20;
uint16_t length;
uint8_t next_header;
uint8_t hop_limit;
struct in6_addr saddr;
struct in6_addr daddr;
} ipv6_header;
const ipv6_header* iph;
iph = (ipv6_header*)(pkt_data + ETHERNET_HEADER_LEN);
From there you can access the version and other information about the IP header. See this post for more information: Getting Npcap IPv6 source and destination addresses

Printing the Source and Destination IP address from an ARP Packet

I am able to print the destination and source address from an ARP packet. But I want to print the source ip and destination ip address from the ARP request. I have searched in the and found this structure.
`struct ether_arp {
struct arphdr ea_hdr;
u_char arp_sha[6];
u_char arp_spa[4];
u_char arp_tha[6];
u_char arp_tpa[4];
};`
But when I am trying to print arp_spa I am getting something like 0:1:8:0.
Obviously this is not the source IP address.
arp_ptr = (struct ether_arp *) packet;
I feel this is wrong as some bytes need to be skipped. But I am not sure about this
Can anybody please comment.
EDIT :
arp_ptr = (struct ether_arp *) packet;
ptr = arp_ptr->arp_sha;
i = ETHER_ADDR_LEN;
printf(" source is: ");
do{
printf("%s%x",(i == ETHER_ADDR_LEN) ? " " : ":",*ptr++);
}while(--i>0);
The above is the code I am using to print either source IP or source MAC from ARP request

Struct copied into byte array .. wrong alignement?

I'm trying to send some manually crafted ARP packets over the network,more specifically an ARP request to get the MAC address of a host.
I can't get the final packet right, on wireshark it stills shows some inconsistency.
Let me walk you through :
Here are the struct & typedef I use all over the program ,
I've defined
a IP struct ( => in_addr )
a MAC struct ( => ether_addr )
a Host struct composed of a MAC & IP
Custom struct to represent a Ethernet frame & an ARP frame.
The code:
#define ETH_ADDR_SIZE 6
#define IP_ADDR_SIZE 4
typedef u_char Packet;
typedef struct in_addr IP;
typedef struct ether_addr MAC;
struct Host {
IP ip;
MAC mac;
};
typedef struct pkt_eth {
MAC dest;
MAC src;
u_short type;
} pkt_eth;
typedef struct pkt_arp {
u_short htype;/* hardware type => ethernet , etc */
u_short ptype; /*protocol type => ipv4 or ipv6 */
u_char hard_addr_len; /* usually 6 bytes for ethernet */
u_char proto_addr_len; /*usually 8 bytes for ipv4 */
u_short opcode; /* type of arp */
MAC hard_addr_send;
IP proto_addr_send;
MAC hard_addr_dest;
IP proto_addr_dest;
} pkt_arp;
/* Designate our own MAC / IP addresses of the interface */
extern MAC mac;
extern IP ip;
extern char * interface;
/* Just some vars used to compare with the struct we use */
const MAC broadcast_mac = { 0xff,0xff,0xff,0xff,0xff,0xff };
const MAC null_mac = { 0x00,0x00,0x00,0x00,0x00,0x00 };
const IP broadcast_ip = { 0xffffffff };
const IP null_ip = { 0x00000000 };
const struct Host null_host = {{ 0x00000000 },
{ 0x00,0x00,0x00,0x00,0x00,0x00 }};
/* Empty mac address which can be used as a temp variable */
MAC tmp_mac = { 0x00,0x00,0x00,0x00,0x00,0x00 };
IP tmp_ip = { 0x00000000 };
Here is the relevant function :
int
arp_resolve_mac ( struct Host * host )
{
struct pkt_arp * arp;
struct pkt_eth * eth;
/*Create the request packet */
Packet * request = arp_packet(REQUEST);
eth = (struct pkt_eth *) (request);
arp = (struct pkt_arp *) (request + ETH_SIZE);
/* ethernet frame */
copy_mac(&eth->dest,&broadcast_mac);
copy_mac(&eth->src,&mac);
/* arp request => mac dest address set to null */
copy_mac(&arp->hard_addr_send,&mac);
copy_mac(&arp->hard_addr_dest,&null_mac);
/* arp request => target ip ! */
copy_ip(&arp->proto_addr_send,&ip);
copy_ip(&arp->proto_addr_dest,&host->ip);
/* Set up sniffing. Better to do it before so less
* prepare time and if any error occurs, no need to send
* the packet. less intrusive */
pcap_init(interface,"arp");
pcap_set_arp_analyzer(arp_analyzer_resolv);
/* Sets the tmp ip variable so we will know if it the right
* response we get or a response coming from another source */
tmp_ip = host->ip;
/* sends the packet */
if(pcap_send_packet(request,ARP_PACKET_SIZE) == -1) {
fprintf(stderr,"Error while sending ARP request packet.\n");
return -1;
}
....
}
Packet *
arp_packet ( int opcode )
{
struct pkt_arp * arp;
struct pkt_eth * eth;
Packet * bytes = (Packet *) malloc(ARP_PACKET_SIZE);
if(bytes == NULL) {
fprintf(stderr,"Could not alloc ARP packet.\n");
return NULL;
}
eth = (struct pkt_eth *) (bytes);
eth->type = htons(ETHERTYPE_ARP);
/* length about hard / proto ... */
arp = (struct pkt_arp *) (bytes + ETH_SIZE);
arp->htype = htons(1);
arp->ptype = htons(0x0800);
arp->hard_addr_len = ETH_ADDR_SIZE;
arp->proto_addr_len = IP_ADDR_SIZE;
/* reply or request */
arp->opcode = opcode == REQUEST ? htons(ARPOP_REQUEST) : htons(ARPOP_REPLY);
return bytes;
} /* ----- end of function arp_empty ----- */
void copy_mac(MAC * m1,const MAC * m2) {
memcpy(m1,m2,ETH_ADDR_SIZE);
}
void copy_ip(IP * i1,const IP * i2) {
memcpy(i1,i2,IP_ADDR_SIZE);
}
void copy_host(struct Host * h1,const struct Host * h2) {
copy_mac(&h1->mac,&h2->mac);
copy_ip(&h1->ip,&h2->ip);
}
Problem:
The created packet is not quite right. Everything is fine up to the hard_addr_send. After this field, there is 2 bytes 0x00,0x00, (seen in GDB) and then the IP address. But due to this offset, it's impossible to correctly parse this packet. For example,in wireshark, instead of getting "10.0.0.1", I've got "0.0.10.0" for IP.
Here is the transcript of GDB :
/** 14 to pass ethernet frame & 4 + 2 + 2 to go to the addresses section*/
(gdb) x/6xb request+14+4+2+2
/** My MAC address , field hard_addr_send. it's GOOD. */
0x606b16: 0x34 0x67 0x20 0x01 0x9a 0x67
(gdb) x/6xb request+14+4+2+2+6
/** 6bytes later, supposedly my IP address.
* It should be 10.0.0.7 but you can see the 0x0a shifted by 2 bytes */
0x606b1c: 0x00 0x00 0x0a 0x00 0x00 0x07
In the method "arp_resolv_mac", everything info is right, i.e. struct Host contains the good information etc; I've checked everything.
I just don't get this offset by 2 bytes ... In a older versions, not using all theses new structs (only char *), I've already succeed at creating a right ARP packet, so I'm kind of wondering if this is not due to the struct, but my knowledge of C does not extend to the memory alignement subject ...!
Thank you.
The problem is that your structs are not packed. One solution would be to use packed structs, i.e.
typedef struct __attribute__ ((__packed__)) pkt_arp {
u_short htype;/* hardware type => ethernet , etc */
u_short ptype; /*protocol type => ipv4 or ipv6 */
u_char hard_addr_len; /* usually 6 bytes for ethernet */
u_char proto_addr_len; /*usually 8 bytes for ipv4 */
u_short opcode; /* type of arp */
MAC hard_addr_send;
IP proto_addr_send;
MAC hard_addr_dest;
IP proto_addr_dest;
} pkt_arp;
However, that is a gcc-specific extension other compilers may not support.
In my opinion, the best solution is accessing the elements of the byte array directly instead of using structs. Yes, it adds a few lines of code, but it's guaranteed to work for compilers that don't implement packed structs too.

in_addr with random results

I'm working with the pcap library and I'm trying to capture the source and destination IP addresses but it seems to be giving me totally random results:
Here is my struct:
struct sniff_ip {
u_char ip_vhl;
u_char ip_tos;
u_short ip_len;
u_short ip_id;
u_short ip_off;
u_char ip_ttl;
u_char ip_p;
u_short ip_sum;
struct in_addr ip_src, ip_dst;
};
Here is the relevant code that uses the struct:
void print_payload(u_char *arg, const struct pcap_pkthdr *pkthdr, const u_char *packet)
{
const struct sniff_ip *ip;
int i=0;
static int count=0;
ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
printf("Source [%s] - Destination [%s]\n", inet_ntoa(ip->ip_src), inet_ntoa(ip->ip_dst));
printf("Payload:\n");
for(i=0; i<pkthdr->len; i++)
{
if(isprint(packet[i]))
printf("%c", packet[i]);
else
printf(".", packet[i]);
if((i%16 == 0 && i != 0) || ( i== pkthdr->len-1))
printf("\n");
}
}
The output is:
Source [207.117.127.0] - Destination [207.117.127.0]
Payload:
................E
..<m4#.#..u.....
.......Q#1......
....0....#......
1\.........
Source [60.190.127.0] - Destination [60.190.127.0]
Payload:
................E
..(..#.#.<......
...........Q#1.P
...wN..
The IP addresses seem to be totally random and aren't mine. The expected output would be to show my own IP for both source and destination since I'm testing it by connecting to myself. I'm running it on port 23 to avoid anybody other data from interfering.
Edit: I got it working, for some reason I had to telnet to "eth0" instead of "localhost" for it to work. However once I changed the port to something more useful, like port 80 it worked fine. I'm not sure why port 23 was different, but oh well.
inet_ntoa returns pointer to its internal buffer, so it's not likely that you can use two inet_ntoas in one call to printf and hope for different results.

Resources