Get an IP_ADDRESS_STRING from GetAdaptersAddresses()? - c

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;

Related

How to send and receive a struct through netlink?

I'm trying to send a struct from user-space to my module in kernel space using netlink, my struct in the user-space is:
struct test{
unsigned int length;
char name[MAX_NAME_LENGTH];
};
and in the kernel space is:
struct test{
__u32 length;
char name[MAX_NAME_LENGTH];
};
where MAX_NAME_LENGTH is a macro defined to be equal 50.
In the user-space, I've the function main which send my struct to the kernel with the following code:
int main(){
struct iovec iov[2];
int sock_fd;
struct sockaddr_nl src_add;
struct sockaddr_nl dest_add;
struct nlmsghdr * nl_hdr = NULL;
struct msghdr msg;
struct test message;
memset(&message, 0, sizeof(struct test));
message.length = 18;
strcpy(message.name, "Just a test\0");
sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);
if (sock_fd < 0){
printf("Netlink socket creation failed\n");
return -1;
}
memset(&src_add, 0, sizeof(src_add));
src_add.nl_family = AF_NETLINK;
src_add.nl_pid = getpid();
memset(&dest_add, 0, sizeof(dest_add));
dest_add.nl_family = AF_NETLINK;
dest_add.nl_pid = 0; // Send to linux kernel
dest_add.nl_groups = 0; // Unicast
bind(sock_fd,(struct sockaddr *)&src_add,sizeof(src_add));
nl_hdr = (struct nlmsghdr *) malloc(NLMSG_SPACE(sizeof(struct test)));
memset(nl_hdr, 0, NLMSG_SPACE(sizeof (struct test)));
nl_hdr->nlmsg_len = NLMSG_SPACE(sizeof(struct test));
nl_hdr->nlmsg_pid = getpid();
nl_hdr->nlmsg_flags = 0;
iov[0].iov_base = (void *)nl_hdr;
iov[0].iov_len = nl_hdr->nlmsg_len;
iov[1].iov_base = &message;
iov[1].iov_len = sizeof(struct test);
memset(&msg,0, sizeof(msg));
msg.msg_name = (void *)&dest_add;
msg.msg_namelen = sizeof(dest_add);
msg.msg_iov = &iov[0];
msg.msg_iovlen = 2;
sendmsg(sock_fd,&msg,0);
close(sock_fd);
return 0;
}
And in the kernel side I've registered a function called callback to be called every time that a message is received, this is the callback function:
static void callback(struct sk_buff *skb){
struct nlmsghdr *nl_hdr;
struct test * msg_rcv;
nl_hdr = (struct nlmsghdr*)skb->data;
msg_rcv = (struct test*) nlmsg_data(nl_hdr);
printk(KERN_INFO "Priting the length and name in the struct:%u, %s\n",msg_rcv->length, msg_rcv->name);
}
When I run these codes and see the dmesg output I receive the following message: Priting the length and name in the struct:0,, so why the fields of the struct filled in the user-space side aren't being sent to the kernel?
Btw, NETLINK_USER is defined as 31.
DON'T DO THAT. YOUR CODE HAS BUGS BY DESIGN.
I'm going to first explain the one superfluous issue that prevents your code from doing what you want, then explain why what you want is a bad idea, then explain the right solution.
1. Doing what you want
You "want" to send a packet consisting of a netlink header followed by a struct. In other words, this:
+-----------------+-------------+
| struct nlmsghdr | struct test |
| (16 bytes) | (54 bytes) |
+-----------------+-------------+
The problem is that's not what you're telling your iovec. According to your iovec code, the packet looks like this:
+-----------------+--------------+-------------+
| struct nlmsghdr | struct test | struct test |
| (16 bytes) | (54 bytes) | (54 bytes) |
| (data) | (all zeroes) | (data) |
+-----------------+--------------+-------------+
This line:
iov[0].iov_len = nl_hdr->nlmsg_len;
Should be this:
iov[0].iov_len = NLMSG_HDRLEN;
Because your first iovec slot is just the Netlink header; not the whole packet.
2. Why what you want is bad
C has a gotcha called "data structure padding." Don't skip this lecture; I'd argue that anyone who deals with the C language MUST read it ASAP: http://www.catb.org/esr/structure-packing/
The gist of it is that C compilers are allowed to introduce garbage between the members of any structure. Thus, when you declare this:
struct test {
unsigned int length;
char name[MAX_NAME_LENGTH];
};
The compiler is technically allowed to mutate that during implementation into something like
struct test {
unsigned int length;
unsigned char garbage[4];
char name[MAX_NAME_LENGTH];
};
See the problem? If your kernel module and your userspace client were generated by different compilers, or by the same compiler but with slightly different flags, or even by slightly different versions of the same compiler, the structures might differ and the kernel will receive garbage, no matter how correct your code looks.
Update: Someone asked me to elaborate on that, so here it goes:
Suppose you have the following structure:
struct example {
__u8 value8;
__u16 value16;
};
In userspace, the compiler decides to leave it as is. However, in kernelspace the compiler "randomly" decides to convert it to:
struct example {
__u8 value8;
__u8 garbage;
__u16 value16;
};
In your userspace client, you then write this code:
struct example x;
x.value8 = 0x01;
x.value16 = 0x0203;
In memory, the structure will look like this:
01 <- value8
02 <- First byte of value16
03 <- Second byte of value16
When you send that to the kernel, the kernel will, of course, receive the same thing:
01
02
03
But it will interpret it differently:
01 <- value8
02 <- garbage
03 <- First byte of value16
junk <- Second byte of value16
(End of Update)
In your case the problem is aggravated by the fact that you define test.length as unsigned int in userspace, yet for some reason you change it into __u32 in kernelspace. Your code is problematic even before structure padding; if your userspace defines basic integers as 64-bit, the bug will also inevitably trigger.
And there's another problem: "Btw, NETLINK_USER is defined as 31" tells me you're following tutorials or code samples long obsolete or written by people who don't know what they are doing. Do you know where that 31 comes from? It's the identifier of your "Netlink family." They define it as 31 because that's the highest possible value it can have (0-31), and therefore, it's the most unlikely one to collide with other Netlink families defined by the kernel. (Because they are numbered monotonically.) But most careless Netlink users are following the tutorials, and therefore most of their Netlink families identify as 31. Therefore, your kernel module will be unable to coexist with any of them. netlink_kernel_create() will kick you out because 31 is already claimed.
And you might be wondering, "well shit. There are only 32 available slots, 23 of them are already taken by the kernel and there's an unknown but likely large number of additional people wanting to register different Netlink families. What do I do?!"
3. The proper way
It's 2020. We don't use Netlink anymore. We use better-Netlink: Generic Netlink.
Generic Netlink uses strings and dynamic integers as family identifiers, and drives you to use Netlink's "attribute" framework by default. (The latter encourages you to serialize and deserialize structures in a portable way, which is the real solution to your original problem.)
This code needs to be visible to both your userspace client and kernel module:
#define SAMPLE_FAMILY "Sample Family"
enum sample_operations {
SO_TEST, /* from your "struct test" */
/* List more here for different request types. */
};
enum sample_attribute_ids {
/* Numbering must start from 1 */
SAI_LENGTH = 1, /* From your test.length */
SAI_NAME, /* From your test.name */
/* This is a special one; don't list any more after this. */
SAI_COUNT,
#define SAI_MAX (SAI_COUNT - 1)
};
This is the kernel module:
#include <linux/module.h>
#include <linux/version.h>
#include <net/genetlink.h>
#include "../include/protocol.h"
/*
* A "policy" is a bunch of rules. The kernel will validate the request's fields
* match these data types (and other defined constraints) for us.
*/
struct nla_policy const sample_policy[SAI_COUNT] = {
[SAI_LENGTH] = { .type = NLA_U32 },
[SAI_NAME] = { .type = NLA_STRING },
};
/*
* This is the function the kernel calls whenever the client sends SO_TEST
* requests.
*/
static int handle_test_operation(struct sk_buff *skb, struct genl_info *info)
{
if (!info->attrs[SAI_LENGTH]) {
pr_err("Invalid request: Missing length attribute.\n");
return -EINVAL;
}
if (!info->attrs[SAI_NAME]) {
pr_err("Invalid request: Missing name attribute.\n");
return -EINVAL;
}
pr_info("Printing the length and name: %u, '%s'\n",
nla_get_u32(info->attrs[SAI_LENGTH]),
(unsigned char *)nla_data(info->attrs[SAI_NAME]));
return 0;
}
static const struct genl_ops ops[] = {
/*
* This is what tells the kernel to use the function above whenever
* userspace sends SO_TEST requests.
* Add more array entries if you define more sample_operations.
*/
{
.cmd = SO_TEST,
.doit = handle_test_operation,
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0)
/* Before kernel 5.2, each op had its own policy. */
.policy = sample_policy,
#endif
},
};
/* Descriptor of our Generic Netlink family */
static struct genl_family sample_family = {
.name = SAMPLE_FAMILY,
.version = 1,
.maxattr = SAI_MAX,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)
/* Since kernel 5.2, the policy is family-wide. */
.policy = sample_policy,
#endif
.module = THIS_MODULE,
.ops = ops,
.n_ops = ARRAY_SIZE(ops),
};
/* Called by the kernel when the kernel module is inserted */
static int test_init(void)
{
return genl_register_family(&sample_family);
}
/* Called by the kernel when the kernel module is removed */
static void test_exit(void)
{
genl_unregister_family(&sample_family);
}
module_init(test_init);
module_exit(test_exit);
And here's the userspace client (You need to install libnl-genl-3 --sudo apt install libnl-genl-3-dev on Debian/Ubuntu):
#include <errno.h>
#include <netlink/genl/ctrl.h>
#include <netlink/genl/genl.h>
#include "../include/protocol.h"
static struct nl_sock *sk;
static int genl_family;
static void prepare_socket(void)
{
sk = nl_socket_alloc();
genl_connect(sk);
genl_family = genl_ctrl_resolve(sk, SAMPLE_FAMILY);
}
static struct nl_msg *prepare_message(void)
{
struct nl_msg *msg;
msg = nlmsg_alloc();
genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, genl_family, 0, 0, SO_TEST, 1);
/*
* The nla_put* functions ensure that your data will be stored in a
* portable way.
*/
nla_put_u32(msg, SAI_LENGTH, 18);
nla_put_string(msg, SAI_NAME, "Just a test");
return msg;
}
int main(int argc, char **argv)
{
struct nl_msg *msg;
prepare_socket();
msg = prepare_message();
nl_send_auto(sk, msg); /* Send message */
nlmsg_free(msg);
nl_socket_free(sk);
return 0;
}
This code should work starting from kernel 4.10. (I tested it in 4.15.) The kernel API was somewhat different before that.
I left a pocket version of my test environment (with makefiles and proper error handling and everything) in my Dropbox, so you can run it easily.

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.

How to convert struct to char array in C?

I am trying to send an ethernet packet using RAW socket in C Linux. I have following struct definition in my code:
typedef struct vlink_header_s
{
uint8_t verCmd;
uint8_t reverseVerCmd;
}vlink_header_t;
typedef struct vlink_reg_rd_s
{
vlink_header_t header;
uint32_t address;
uint16_t length;
}vlink_reg_rd_t;
In main i created a struct:
vlink_reg_rd_t g_pkt;
g_pkt.header.verCmd = 0x10|VLINK_CMD_REG_RD;
g_pkt.header.reverseVerCmd = ~(g_pkt.header.verCmd);
g_pkt.address = 0x0007 .....
and message:
char sendbuf[1024];
struct ether_header *eh = (struct ether_header *) sendbuf;
how do I add all the info from the struct g_pkt to this sendbuf after ether_header so I can send a complete packet using:
sendto(sockfd, sendbuf, txLen, 0, (struct sockaddr*)&socket_address, sizeof(struct sockaddr_ll));
Everything else in my code is working, I tried other ways by adding info to sendbuf one by one and it works fine and my machine receive the packets on the other side too. I just want to make it more versatile because there are a bunch of commands and structs for each commands will work best. Thanks.
Try to use the memcpy function:
#include<iostream>
typedef struct vlink_header_s
{
uint8_t verCmd;
uint8_t reverseVerCmd;
}vlink_header_t;
typedef struct vlink_reg_rd_s
{
vlink_header_s header;
uint32_t address;
uint16_t length;
}vlink_reg_rd_t;
using namespace std;
int main()
{
vlink_reg_rd_t data;
//TODO: Set values into typedef data.
int size = sizeof(vlink_reg_rd_t); //get
char* buffer = new char[size];
memset(buffer, 0x00, size);
memcpy(buffer, &data, size); //Copy data from vlink_reg_rd_t to char*
//TODO: Send the buffer.
delete[] buffer; //free memory
return 0;
}
IMPORTANT: be aware of the order in which data types number are written into the buffer. Also it is necessary check the align the data into the structure to avoid extra bytes at the moment of use memcpy. Here you can check this topic:
for Microsoft:
https://msdn.microsoft.com/en-us/library/xh3e3fd0.aspx
https://msdn.microsoft.com/en-us/library/83ythb65.aspx
For Gcc:
https://gcc.gnu.org/onlinedocs/gcc-3.3/gcc/Type-Attributes.html
I have done this way with protocol buffer , you can take a look at : https://www.google.com.vn/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=protocol+buffer&* .
Edit : this way called serialize data , as you serialize your data into a proto file then compile it to the packet file that you use on both server and client .

Smartest way to compare IP addresses quickly?

I have a list of IP addresses, stored like this:
char IP_addresses_list[] = {
"157.55.130", /* 157.55.130.0/24 */
"157.56.52", /* 157.56.52.0/24 */
"157.12.53", /* 157.12.53.0/24 */
...
};
I get the IP address from the sniffed packet (casting it to struct iphdr *iph = (struct iphdr *)(packet + sizeof(struct ether_header)); I convert it in a character string using inet_ntop; finally, I compare the IP address from the packet with the ones in the list with the following code:
/*
* input: IP address to search in the list
* output: 1 if IP address is found in the list, 0 otherwise
*/
int find_IP_addr(char *server) {
int ret = 0;
int i, string_size1, string_size2;
char *copied_server, *copied_const_char;
char *save_ptr1, *save_ptr2;
char dot[2] = ".";
/* Here I store the IP address from the packet */
char first_IPaddr_pkt[4], second_IPaddr_pkt[4], third_IPaddr_pkt[4];
/* Here I store the IP address from the list */
char first_IPaddr_list[4], second_IPaddr_list[4], third_IPaddr_list[4];
string_size1 = strlen(server)+1;
copied_server = (char *)malloc(string_size1 * sizeof(char));
strcpy(copied_server, server);
/* I store and compare the first three bits of the IP address */
strcpy(first_IPaddr_pkt, strtok_r(copied_server, dot, &save_ptr1));
strcpy(second_IPaddr_pkt, strtok_r(NULL, dot, &save_ptr1));
strcpy(third_IPaddr_pkt, strtok_r(NULL, dot, &save_ptr1));
printf("tokenized %s, %s and %s\n", first_IPaddr_pkt, second_IPaddr_pkt, third_IPaddr_pkt);
/* Now I scan the list */
for (i=0; i<LIST_LENGTH; i++) {
/* I copy an address from the list */
string_size2 = strlen(IP_addresses_list[i])+1; // +1 for null character
copied_const_char = (char *)malloc(string_size2 * sizeof(char));
strcpy(copied_const_char, IP_addresses_list[i]);
/* Let's split the address from the list */
strcpy(first_IPaddr_list, strtok_r(copied_const_char, dot, &save_ptr2));
strcpy(second_IPaddr_list, strtok_r(NULL, dot, &save_ptr2));
strcpy(third_IPaddr_list, strtok_r(NULL, dot, &save_ptr2));
printf("tokenized %s, %s and %s\n", first_IPaddr_list, second_IPaddr_list, third_IPaddr_list);
/* I compare the first byte of the address from the packet I got and
the first byte of the address from the list:
if they are different, there's no reason to continue comparing
the other bytes of the addresses */
if (strcmp(first_IPaddr_pkt, first_IPaddr_list) != 0) {
continue;
}
else {
if (strcmp(second_IPaddr_pkt, second_IPaddr_list) != 0) {
continue;
}
else {
if (strcmp(third_IPaddr_pkt, third_IPaddr_list) != 0) {
continue;
}
else
/* All the bytes are the same! */
ret = 1;
}
}
free(copied_const_char);
}
free(copied_server);
return ret;
}
I'd like to make this more fast, without using strtok, strcmp, malloc or free.
In /usr/include/netinet/ip.h I see that addresses are
u_int32_t saddr;
u_int32_t daddr;
is it possible to compare without even using inet_ntop first, maybe just comparing the two addresses while they still are u_int32_t?
EDIT: here's a solution example for whoever will read this question.
#include <stdio.h>
#include <sys/types.h>
int main() {
// In the list I have: 104.40.0.0./13
int cidr = 13;
u_int32_t ipaddr_from_pkt = 1747488105; // pkt coming from 104.40.141.105
u_int32_t ipaddr_from_list = 1747451904; // 104.40.0.0
int mask = (-1) << (32 - cidr);
if ((ipaddr_from_pkt & mask) == ipaddr_from_list)
printf("IP address belongs to the given range!!!\n");
else printf ("failure\n");
return 0;
}
Thanks to iharob too for the bsearch hint.
I would avoid converting the binary data to strings. If you keep them binary then it's quite easy to compare:
match = (ip & listed_mask) == listed_ip;
"/24" is a mask. Means inly 24 highest bits are relevant. You convert it to binary mask as follows:
listed_mask = (-1) << (32 - 24);
The performance issues have nothing to do with strcmp(), malloc() is unnecessary though.
If you are only using IPv4 addresses you only need 16 characters to store it so you can remove malloc() and declare the temporary storage as an array.
But there is an important improvement if there are going to be many ip addresses in the list.
First you need to sort the list of IP addresses, and then use bsearch() to search for the right IP. This way the code will run in O(log(2n)) time which is a lot faster than O(N), specially for large N
My approach here would be:
simply use a strncat with ".0" to build valid IPv4 addresses.
use getaddrinfo with constant values for socket type etc to build a addrinfo struct
compare the relevant fields of the addrinfo.
Basically, the example from man getaddrinfo does all this.
The fastest way would be to store addresses in a dictionary, see this link

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);

Resources