parsing ip address in raw socket programming in C - c

I am implementing raw sockets in C linux. I am new to socket programming so have some problem with the data types to be used for Internet addresses.
I want to know what should be the data type of ip_addres(in main) in my code below. I think it needs to be pointer as I need to return two addresses. Then these two addresses are to be passed in the next function as shown.
main()
{
int len,raw_socket;
struct iphdr *ip_header;
unsigned char *packet_buffer[2048];
len=recvfrom(raw_socket,packet_buffer,2048,...);
ip_addres=parseipheader(packet_buffer,len); /*I want this function to return ip address of destination and source*/
ip_header=CreateIPHeader(source_ip,destination_ip);
}
parseipheader(unsigned char *packet,int len)
{
struct iphdr *ip_header;
ip_header=(struct ip_header *)(packet+sizeof(struct ethhdr));
return ip_addresses;
}
struct iphdr* CreateIPHeader(source_ip,destination_ip)
{
struct iphdr *ip_header;
return ip_header;
}

Yes you are correct . You can return pointer to the source ip address and destination ip address is 4 bytes (32 bits) from source ip address in ip header . so you can add 4 to the address of source ip address to get the destination ip address .
char *src_ip;
src_ip = parseipheader(packet_buffer,len);
CreateIPHeader(src_ip,src_ip+4);

Related

Linux C - Convert `iphdr->daddr` to equivalent string - doesn't work anymore?

There are several threads about how to convert the ip-adresses in struct iphdr to strings (like 127.0.0.1) with the same method, for example:
Convert source IP address from struct iphdr* to string equivalent using Linux netfilter
But somehow they aren't working for me:
char daddr_str[16];
struct iphdr *iph = (struct iphdr*)(buf);
snprintf(daddr_str, sizeof(daddr_str), "%pI4", &iph->daddr);
printf("IP: %s\n", daddr_str);
And I get:
IP: 0x7f5870621020I
Any ideas what I did wrong?
One problem could be that your are not properly extracting the IP-Header from the packet. At the beginning of the buffer usually lies the Ethernet header first and the IP header follows afterwards - so in order to get the IP-Header you need to:
struct iphdr *iph = (struct iphdr*)(buf + sizeof(struct ethhdr));
Hope it helped in your case, here is also a nice guide
Edit
You are right, this was not the actual problem in your case. I tried it out by myself and also get just the address.
After some research I think that the real cause is that these special format strings like %pI4 are only known by the kernel implementation of these functions and not by the stdlib implementation.
So this attempt will only work when developing a kernel module e.g.
I now did it the other way around:
struct sockaddr_in ip;
inet_aton("127.0.0.1", &ip.sin_addr);
if(ip.sin_addr.s_addr == iph->daddr) {
...
}

static const uint8_t inside function changes value

I am writing a small analysis tool using libpcap that sniffs traffic on an ethernet device and performs some sort of analysis on the received packets. In order to do so, I have the obvious libpcap loop:
void packet_loop(u_char *args, const struct pcap_pkthdr *header,
const u_char *packetdata) {
int size = (int)header->len;
//Before we map the buffer to the ethhdr struct,
//we check if the size fits
if (ETHER_HDR_LEN > size)
return;
const struct ethhdr *ethh = (const struct ethhdr *)(packetdata);
//If this protocol is IPv4 and the packet size is bigger than
//ETH hdr size
if (ETHERTYPE_IP == ntohs(ethh->h_proto)) {
//Before we map the buffer to the iph struct,
//we check if the size fits
if (ETHER_HDR_LEN + (int)sizeof(struct iphdr) > size)
return;
const struct iphdr *iph = (const struct iphdr*)
(packetdata + sizeof(struct ethhdr));
//If this protocol isn't UDP and the header length
//isn't 5 (20bytes)
if (IPPROTO_UDP != iph->protocol && 5 != iph->ihl)
return;
//eval_udp(packetdata, size);
const struct udphdr *udph = (const struct udphdr*)
(packetdata + sizeof(struct ethhdr) +
sizeof(struct iphdr));
if (DATA_SRCPORT == ntohs(udph->uh_sport) &&
DATA_DESTPORT == ntohs(udph->uh_dport)) {
analyse_data(packetdata);
}
}
}
that calls the follwoing code snipped on receival of a specific packet type. As you can see, I am using a static variable to keep track of the previous packet, in order to compare two.
void analyse_data(const uint8_t *packet)
{
if (!packet)
return;
static const uint8_t *basepacket;
//If there was no packet to base our analysis on, we will wait for one
if (!basepacket) {
basepacket = packet;
return;
}
const struct dataheader *basedh = (const struct dataheader *)
(__OFFSETSHERE__ + basepacket);
const struct dataheader *dh = (const struct dataheader *)
(__OFFSETSHERE__ + packet);
printf("%d -> %d\n", ntohs(basedh->sequenceid),
ntohs(dh->sequenceid));
basepacket = packet;
return;
}
struct dataheader is a regular struct, just like etthdr. I would expect a constant printout like:
0 -> 1
1 -> 2
2 -> 3
Unfortunately, I get a different printout, which is mostly right. But around every 20th-40th packet, I see the following behavior (example):
12->13
13->14
0->15
15->16
...
It is maybe interesting to note that this does NOT occcur, when I receive only packets of the specific type I look after (8-10 Mbit/s). Nevertheless, as soon as I use my tool in the "regular" network environment (around 100Mbit/s), I get this behavior. I checked my if statement, that filters the packet it works flawlessly (checking UDP source and destination ports). Wireshark also shows me that there is not a single packet on those ports that is not of that specific type.
libpcap controls the packet data it passes in to your packet_loop. Once packet_loop returns, you have no guarantee what the pointers for the packet data point to - libpcap could throw the packet away, or it could reuse the same space for a new packet.
This means if you want to compare 2 packets, you must make a copy of the 1. packet - you cannot save the pointer from one call to packet_loop and expect that pointer to be valid and point to the same packet in future calls to packet_loop. So your code could be changed to e.g.
void analyse_data(const uint8_t *packet, int size )
{
if (!packet)
return;
static const uint8_t basepacket[1024*64];
static int has_basepacket;
//If there was no packet to base our analysis on, we will wait for one
if (!has_basepacket){
if (size < sizeof basepacket) {
memcpy(basepacket, packet, size);
has_basepacket = 1;
}
return;
}
...
Also, make sure your verify the sizes everywhere. Just because the ethernet type says it is an IPv4 packet, doesn't mean you can trust it to contain a full IP packet. Just because the IP header says it is 20 bytes, doesn't mean you can trust it to contain a full IP packet, and so on for all the layers you attempt to decode.

Mismatch in assigning IP address to a buffer and printing contents of buffer

I am implementing raw sockets in C. In the code below, I am parsing the IP header I received from sender.
a. I will send back the ack as well so storing IP address received in a buffer(ret_ip).
b. I don't have another computer so using lo (local loop back) as my interface.
//first two printf statements are printing the right address, 10.100.207.74
//daddr SENT = 32.112.247.9saddr SENT = 36.112.247.9
How can I get it correct?
I think this problem is due to memcpy whose first argument is pointing to unsigned char while second argument is pointing to _be32.
What I actually want to do in my program is : ret_ip's first 4 bytes should contain the destination address and next 4 the source address. Then I will create IP header and make dest addr=source addr and source-addr=dest-addr. and send ACK to sender.
char* ParseIPHeader(unsigned char *packet,int len)
{
struct ethhdr *ethernet_header;
struct iphdr *ip_header;
char *ret_ip;
ethernet_header=(struct ethhdr *)packet;
if(ntohs(ethernet_header->h_proto)==ETH_P_IP)
{
if(len>=(sizeof(struct ethhdr)+sizeof(struct iphdr)))
{
ip_header=(struct iphdr*)(packet+sizeof(struct ethhdr));
ret_ip=malloc(2*(sizeof(ip_header->daddr)));
printf("Dest IP address: %s\n",inet_ntoa(ip_header->daddr));
printf("Source IP address: %s\n",inet_ntoa(ip_header->saddr));
memcpy(ret_ip,&(ip_header->daddr),sizeof(ip_header->daddr));
memcpy(ret_ip+4,&(ip_header->saddr),4);
printf("daddr SENT = %s",inet_ntoa(ret_ip));
printf("saddr SENT = %s",inet_ntoa(ret_ip+4));
}
else
printf("IP packet does not have full header\n");
}
else
{
//not an IP packet
}
return ret_ip;
}
Thanks :)
first problem is your memory allocation
ret_ip=malloc(2*(ip_header->daddr));
it should be
ret_ip=malloc(2*(sizeof(ip_header->daddr)));
but why you are not using the ip_hdr struct again ? for example
struct iphdr *ret_ip = malloc(sizeof(iphdr));
ret_ip->daddr = ip_header->saddr;
ret_ip->saddr = ip_header->daddr;
i suggest this solution is much easier ;)

Convert source IP address from struct iphdr* to string equivalent using Linux netfilter

I want to convert the source & destination IP addresses from a packet captured using netfilter to char *.
In my netfilter hook function, I have:
sock_buff = skb; // argument 2 of hook function
// ip_header is struct iphdr*
ip_header = (struct iphdr *)skb_network_header(sock_buff);
// now how to convert ip_header->saddr & ip_header->daddr to char *
// ip_header->saddr & ip_header->daddr are of type __be32
Thanks.
The kernel's family of printf() functions has a special format specifier for IP-addresses (%pI4 for IPv4-addresses, %pI6 for IPv6).
So with IPv4, you could use something like:
char source[16];
snprintf(source, 16, "%pI4", &ip_header->saddr); // Mind the &!
Or write to dynamically allocated memory.
If you simply want to print debug-output, you can also use printk(). For the many other features of %p, see this document.
Try in4_pton() function in net/core/utils.c (definition: https://elixir.bootlin.com/linux/latest/source/net/core/utils.c#L118)
#include <linux/inet.h>
char source[16];
in4_pton(source, -1, &ip_header->saddr, '\0', NULL);

Get an IP_ADDRESS_STRING from GetAdaptersAddresses()?

GetAdaptersAddresses() will get you addresses in IP_ADAPTER_UNICAST_ADDRESS format, which is defined as:
typedef struct _IP_ADAPTER_UNICAST_ADDRESS {
union {
struct {
ULONG Length;
DWORD Flags;
} ;
} ;
struct _IP_ADAPTER_UNICAST_ADDRESS *Next;
SOCKET_ADDRESS Address;
IP_PREFIX_ORIGIN PrefixOrigin;
IP_SUFFIX_ORIGIN SuffixOrigin;
IP_DAD_STATE DadState;
ULONG ValidLifetime;
ULONG PreferredLifetime;
ULONG LeaseLifetime;
UINT8 OnLinkPrefixLength;
} IP_ADAPTER_UNICAST_ADDRESS, *PIP_ADAPTER_UNICAST_ADDRESS;
The only field that seems to suggest the human-readable IP address string is Address, which is a SOCKET_ADDRESS structure defined as:
typedef struct _SOCKET_ADDRESS {
LPSOCKADDR lpSockaddr;
INT iSockaddrLength;
} SOCKET_ADDRESS, *PSOCKET_ADDRESS;
Which, in turn, uses another structure, SOCKADDR, defined as:
Sorry, it's way to complex to post here, as it varies depending on IPv4 vs. IPv6 and the Windows edition... so here is a link to the definition:
http://msdn.microsoft.com/en-us/library/ms740496%28v=VS.85%29.aspx
If you haven't gotten dizzy yet like I did and followed through this maze of definitions, you probably noticed that it's a nightmare to retrieve the good old dotted string style of an IP address, as it used to be much easier using GetAdaptersInfo().
My question is: Is there a truly IP Helper function that can convert IP_ADAPTER_UNICAST_ADDRESS to an IPv4 dotted string (or an IPv6 string)?
You can use GetIpAddrTable - the returned data structure contains a DWORD dwAddr that is the IPv4 address. The sample code on that first link should show you what you want. Brief excerpt to show you what I mean:
if ( (dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 )) != NO_ERROR ) {
printf("GetIpAddrTable failed with error %d\n", dwRetVal);
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwRetVal, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) & lpMsgBuf, 0, NULL)) {
printf("\tError: %s", lpMsgBuf);
LocalFree(lpMsgBuf);
}
exit(1);
}
printf("\tNum Entries: %ld\n", pIPAddrTable->dwNumEntries);
for (i=0; i < (int) pIPAddrTable->dwNumEntries; i++) {
printf("\n\tInterface Index[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwIndex);
IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwAddr;
printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
The IP_ADAPTER_UNICAST_ADDRESS contains a SOCKET_ADDRESS in Address, which in turn contains a LPSOCKADDR in lpSockAddr - you can convert this to the ipv4 string form using WSAAddressToString.
Take a look at the documentation for SOCKADDR. That leads us to the documentation for SOCKADDR_STORAGE, which is a helper struct for both IPv4 and IPv6.
Quote from the sockaddr documentation:
Winsock functions using sockaddr are not strictly interpreted to be
pointers to a sockaddr structure. The structure is interpreted
differently in the context of different address families.
For ipv4, you can cast a sockaddr pointer to a sockaddr_in pointer and then access the IPv4 address information from there. Then you can use your favorite string builder to produce a dotted-quad formatted string.
sockaddr_in* address = (sockaddr_in*) temp->Address.lpSockaddr;
uint32_t ipv4 = address->sin_addr.S_un.S_addr;
// First octet: address->sin_addr.S_un.S_un_b.s_b1
// Second octet: address->sin_addr.S_un.S_un_b.s_b2
// Third octet: address->sin_addr.S_un.S_un_b.s_b3
// Fourth octet: address->sin_addr.S_un.S_un_b.s_b4
I would imagine that you can also cast the address for ipv6 in a similar way given the struct definitions (copied below).
struct sockaddr {
ushort sa_family;
char sa_data[14];
};
struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
struct sockaddr_in6 {
short sin6_family;
u_short sin6_port;
u_long sin6_flowinfo;
struct in6_addr sin6_addr;
u_long sin6_scope_id;
};
You can simply pass _SOCKET_ADDRESS.lpSockaddr and _SOCKET_ADDRESS.iSockaddrLength as lpsaAddress and dwAddressLength arguments to WSAAddressToString function. WSAAddressToString will do necessary conversion for you, no need to dig deeper.
function SOCKET_ADDRESS_ToString(const Addr: SOCKET_ADDRESS): String;
var
Len: DWORD;
begin
if (Addr.lpSockaddr = nil) or (Addr.iSockaddrLength <= 0) then
begin
Result := '';
Exit;
end;
Len := 0;
WSAAddressToString(Addr.lpSockaddr, Addr.iSockaddrLength, nil, nil, Len);
SetLength(Result, Len);
if WSAAddressToString(Addr.lpSockaddr, Addr.iSockaddrLength, nil, PChar(Result), Len) = 0 then
SetLength(Result, Len - 1)
else
Result := '';
end;

Resources