So I've been trying to access the iphdr using eBPF.
static inline int parse_ipv4(void *data, u64 nh_off, void *data_end) {
struct iphdr *iph = data + nh_off;
if ((void*)&iph[1] > data_end)
return 0;
return iph->protocol;
}
When I use the code above in the eBPF function, it works fine like :
if (h_proto == htons(ETH_P_IP)){
index = parse_ipv4(data, nh_off, data_end);
Like this, calling parse_ipv4 function works.
However, if I try to access the ipheader directly without using the function, it doesn't work.
if (h_proto == htons(ETH_P_IP)){
index = parse_ipv4(data, nh_off, data_end);
struct iphdr *iph2 = sizeof(*eth) + nh_off;
}
This gives me an error : 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.
and fails to activate.
Thank you so much in advance!
Unless I misunderstand your program, the following:
struct iphdr *iph2 = sizeof(*eth) + nh_off;
looks erroneous. Instead, iph2 should be something like data + nh_off, just as in your function, no? If you set it to the sum of two sizes, without any base address, then you try to access data at an arbitrary memory location (something like 0x28 I guess), which of course is not permitted.
Related
I would like to load some text from a file and print it out using eBPF. Is such a thing even possible? I did something similar using bpf_probe_read but I'm wondering if there is a simpler way of doing something like this by just giving it a location? I want to try expanding this by using CSVs for instance as a means of practice.
#!/usr/bin/python3
# BPF PROGRAM
bpfprogram = """
static void helloworld() {
bpf_trace_printk("Hello World!\\n");
}
int helloworld2(void *ctx)
{
helloWorld();
return 0;
}
"""
b = BPF(text=bpfprogram)
b.attach_kprobe(event=b.get_syscall_fnname("clone"), fn_name="helloworld")
b.trace_print()
I started coding in ebpf and XDP.
I am using python bcc to load the XDP program to the NICs.
I am trying to work with __sk_buff structure, but when I am trying to access any filed of skb the verifier failed to load the program.
int xdp_test(struct sk_buff *skb)
{
void *data = (void*)(long)skb->data;
void *data_end = (void*)(long)skb->data_end;
if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) < data_end)
{
struct iphdr * ip = ip_hdr(skb);
// according to my checks, it failed because of this line. I cant access to ip->protocol (or any other fileds)
if (ip->protocol == IPPROTO_TCP)
{
return XDP_PASS;
}
}
...
return XDP_PASS
}
I just want to calculates layer 4 checksum on my program using bpf_l4_csum_replace Which takes skb as the first argument.
Why is that happening?
Can I even use __sk_buff structure in XDP? Or I have to use the xdp_md struct?
UPDATE:
Thanks to Qeole, I understood that I cannot use sk_buff using XDP.
There is a way to calculate TCP checksum using xdp_md?
Indeed you cannot use the struct __sk_buff in XDP programs. You have to use the struct xdp_md instead. XDP performance is due for a great part to the kernel calling the eBPF program before the allocation and initialisation of the socket buffer (struct sk_buff in the kernel), which saves time and resources, but also means you don't have access to that structure in your XDP program.
I was looking through a slide by IOvisor project, https://events.static.linuxfound.org/sites/events/files/slides/iovisor-lc-bof-2016.pdf
#include <bcc/proto.h>
struct IPKey { u32 dip; u32 sip; };
BPF_TABLE("hash", struct IPKey, int, mytable, 1024);
int recv_packet(struct __sk_buff *skb) {
struct IPKey key;
u8 *cursor = 0;
struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet));
struct ip_t *ip = cursor_advance(cursor, sizeof(*ip));
key.dip = ip->dst;
key.sip = ip->src;
int *leaf = mytable.lookup(&key);
if (leaf)
*(leaf)++;
return 0;
}
This code is amongst the examples.
I've been using cursor_advance() quite often and now I'm trying to figure out what exactly it does.
I suspect that cursor is a pointer where we save the address of the packet we are parsing.
Then, with cursor_advance() we move the cursor by the size of the ethernet header, since ethernet_t contains all the ethernet header information.
Then, the cursor now at the address at the end of the ethernet header of the packet and if we use variables declared in the ethernet_t header, such as type, like : ethernet->type, we can access the information saved at type since the struct ethernet would read the values saved in that address?
I'm sorry my explanation is not really good.
I'm just looking for a general explanation or if my theory is correct.
Thanks!
Your understanding sounds correct to me. Just think of it as a “cursor” used to successively parse the different headers of your packet. The cursor_advance() macro is defined as:
#define cursor_advance(_cursor, _len) \
({ void *_tmp = _cursor; _cursor += _len; _tmp; })
It adds _len to the _cursor, and returns the value _cursor had before we added _len.
So the first call to cursor_advance() returns the initial value: ethernet points to the beginning of the packet, and we can use its attributes to access the different fields of the Ethernet header. But this same call also moves the cursor forwards by the length of the Ethernet header, so now it points to the beginning of the next header (L3, e.g. IP). The second call to cursor_advance() returns the pointer to the L3 layer, which we store in ip. The cursor is also moved forward and, assuming the packet is IPv4, would now point at the L4 header.
Note: I do not believe this mechanism is widely used in BPF programs aside from the few networking examples available in BCC. Instead, programs often navigate through packet headers with skb->data and skb->data_end.
I'm using CLR profiling API and trying to get arguments info (COR_PRF_FUNCTION_ARGUMENT_INFO) from COR_PRF_ELT_INFO using GetFunctionEnter3Info function.
Below is my code. It seems GetFunctionEnter3Info function is not setting the value for pArgumentInfo. It always has null value. However, the function returns S_OK, which is a success.
I may be missing something. How should I get COR_PRF_FUNCTION_ARGUMENT_INFO from COR_PRF_ELT_INFO ?
PROFILER_STUB EnterStub(FunctionIDOrClientID functionId, COR_PRF_ELT_INFO eltInfo)
{
COR_PRF_FRAME_INFO *pFrameInfo = 0;
ULONG *pcbArgumentInfo = 0;
COR_PRF_FUNCTION_ARGUMENT_INFO *pArgumentInfo = NULL;
corProfilerInfo->GetFunctionEnter3Info(functionId.functionID, eltInfo, pFrameInfo, pcbArgumentInfo, pArgumentInfo);
if(pArgumentInfo) {
//
}
}
It is a little bit tricky,
By msdn doc:
pcbArgumentInfo
[in, out] A pointer to the total size, in bytes, of the COR_PRF_FUNCTION_ARGUMENT_INFO structure (plus any additional COR_PRF_FUNCTION_ARGUMENT_RANGE structures for the argument ranges pointed to by pArgumentInfo). If the specified size is not enough, ERROR_INSUFFICIENT_BUFFER is returned and the expected size is stored in pcbArgumentInfo. To call GetFunctionEnter3Info just to retrieve the expected value for *pcbArgumentInfo, set *pcbArgumentInfo=0 and pArgumentInfo=NULL
In other words, you have a single COR_PRF_FUNCTION_ARGUMENT_INFO structure, which references multiple COR_PRF_FUNCTION_ARGUMENT_RANGE.
First of all, get a number of bytes of pcbArgumentInfo, after that allocate bytes and pass the pointer to GetFunctionEnter3Info as COR_PRF_FUNCTION_ARGUMENT_INFO.
Here is an example
PROFILER_STUB EnterStub(FunctionIDOrClientID functionId, COR_PRF_ELT_INFO eltInfo)
{
ULONG pcbArgumentInfo = 0;
COR_PRF_FRAME_INFO frameInfo;
corProfilerInfo3->GetFunctionEnter3Info(functionIDOrClientID.functionID, eltInfo, &frameInfo, &pcbArgumentInfo, NULL);
char* pArgumentInfo = new char[pcbArgumentInfo];
corProfilerInfo3->GetFunctionEnter3Info(functionIDOrClientID.functionID, eltInfo, &frameInfo, &pcbArgumentInfo, (COR_PRF_FUNCTION_ARGUMENT_INFO*)pArgumentInfo);
COR_PRF_FUNCTION_ARGUMENT_INFO* ptr = (COR_PRF_FUNCTION_ARGUMENT_INFO*)pArgumentInfo;
}
To access the second argument info block of COR_PRF_FUNCTION_ARGUMENT_RANGE use
prt->ranges[1]
The number of blocks is written in ptr->numRanges
I need to parse the pcap files and count the packets separately (TCP,UDP,IP). I found a lot of libraries for this like pcap, jnetpcap but I want to do this without using any external libraries.I do not need a code but a just a conceptual explanation.
Question
While parsing pcap files how should I distinguish between the frames(be it TCP,UDP,IP). I tried reading about the format but what I do not understand is how would I come to know about how many bytes should I read for a particular frame and how would i know what type of a frame is it.Because only once I am able to extract the packets separately I will be able to filter out other information.
You'd have to parse each frame separately and have a counter for each value you are trying to count. Assuming the capture you are examining is in pcap/pcapng format you might find libpcap helpful.
To give a quick run of what you might have to do (assuming the lower level is Ethernet without VLAN tags)
uint64_t ip_count, tcp_count, udp_count;
void parse_pkt(uint8_t *data, uint32_t data_len) {
uint8_t *ether_hdr = data;
uint16_t ether_type = ntohs(*(uint16_t *) (data + 12))
if (ether_type != 0x800) {
return;
}
ip_count += 1;
uint8_t *ip_hdr = data + 14;
protocol = ntohs(*(uint16_t *) (ip_hdr + 9))
//protocol is either udp/tcp/sctp...etc
if (protocol == 0x11) {
udp_count++;
} else if (protocol == 0x06) {
tcp_count++;
}
}
// foreach pkt from libpcap_open call parse_pkt with the data and data_len
This code is fragile. Jumping to direct offsets without the proper length and type checks is not a good idea.