reading sk_buff and getting iphdr in kernel module - c

I am trying to read sk_buff->network_header but when I try to read my computer get froze and never gets unfreez. Why is that happening. This is my code in poll of napi api
In probe function
netif_napi_add(netdev, &adapter->napi, e1000e_poll, 64);
static int e1000e_poll(struct napi_struct *napi, int budget)
{
struct sk_buff *skb=napi->skb;
// (struct iphdr *)skb_network_header(&napi->skb);
struct iphdr *ip_header = (struct iphdr *)skb->network_header;
struct udphdr *udp_header;
struct tcphdr *tcp_header;
struct list_head *p;
//unsigned int src_ip = (unsigned int)ip_header->saddr;
}

I simply dont want to assign just about anything to something similar. I had check types when assigning plus prinkk not the solution since it does not have formats that covers all types

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) {
...
}

eBPF: How can the sockaddr struct be read when hooking into security_socket_connect

As proposed in the presentation Security Monitoring with eBPF I'm trying to hook into security_socket_connect.
While my gobpf/bcc based code partly works, I seem not be able to read the IP address in the sockaddr struct.
The relevant part looks like this:
int security_socket_connect_entry(struct pt_regs *ctx, struct socket *sock, struct sockaddr *address, int addrlen)
{
u32 address_family = address->sa_family;
if (address_family == AF_INET) {
struct ipv4_data_t data4 = {.pid = pid};
struct sockaddr_in *addr2 = (struct sockaddr_in *)address;
After that I try to read the IP address in addr2. The first try was:
data4.daddr = addr2->sin_addr.s_addr;
The second try was with bpf_probe_read:
bpf_probe_read(&data4.daddr, sizeof(data4.daddr), (void *)((long)addr2->sin_addr.s_addr));
Both options present the same error:
R9 invalid mem access 'inv'
HINT: The invalid mem access 'inv' error can happen if you try to dereference memory without first using bpf_probe_read() to copy it to the BPF stack. Sometimes the bpf_probe_read is automatic by the bcc rewriter, other times you'll need to be explicit.
A repo with a buildable sample can be found here: socket-connect-bpf
I figured it out thanks to an answer to issue #1858 in the bcc repo.
We have to operate on the pointer, so the IP address can be read like this:
bpf_probe_read(&data4.daddr, sizeof(data4.daddr), &addr2->sin_addr.s_addr);

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 .

parsing ip address in raw socket programming in 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);

Resources