Linux skb packet count header and metadata - linux

Could not find any info about this header at the end of Skb and about this metadata
So it seems it is user controlled and should be checked for bounds
static int ax88179_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
{
struct sk_buff *ax_skb;
int pkt_cnt;
u32 rx_hdr;
u16 hdr_off;
u32 *pkt_hdr;
/* At the end of the SKB, there's a header telling us how many packets
* are bundled into this buffer and where we can find an array of
* per-packet metadata (which contains elements encoded into u16).
*/
if (skb->len < 4)
return 0;
skb_trim(skb, skb->len - 4);
rx_hdr = get_unaligned_le32(skb_tail_pointer(skb));
pkt_cnt = (u16)rx_hdr;
hdr_off = (u16)(rx_hdr >> 16);
if (pkt_cnt == 0)
return 0;
/* Make sure that the bounds of the metadata array are inside the SKB
* (and in front of the counter at the end).
*/
if (pkt_cnt * 2 + hdr_off > skb->len)
return 0;
Can somebody point to code in Kernel or references describing it?

USB Ethernet devices don't have hardware framing support, so have their own schemes to do framing by inserting extra bytes in the packet. The tx_fixup and rx_fixup are provided to do that. Some of the schemes are described here: http://www.linux-usb.org/usbnet

Related

How to pass efficiently the hugepages-backed buffer to the BM DMA device in Linux?

I need to provide a huge circular buffer (a few GB) for the bus-mastering DMA PCIe device implemented in FPGA.
The buffers should not be reserved at the boot time. Therefore, the buffer may be not contiguous.
The device supports scatter-gather (SG) operation, but for performance reasons, the addresses and lengths of consecutive contiguous segments of the buffer are stored inside the FPGA.
Therefore, usage of standard 4KB pages is not acceptable (there would be up to 262144 segments for each 1GB of the buffer).
The right solution should allocate the buffer consisting of 2MB hugepages in the user space (reducing the maximum number of segments by factor of 512).
The virtual address of the buffer should be transferred to the kernel driver via ioctl. Then the addresses and the length of the segments should be calculated and written to the FPGA.
In theory, I could use get_user_pages to create the list of the pages, and then call sg_alloc_table_from_pages to obtain the SG list suitable to program the DMA engine in FPGA.
Unfortunately, in this approach I must prepare the intermediate list of page structures with length of 262144 pages per 1GB of the buffer. This list is stored in RAM, not in the FPGA, so it is less problematic, but anyway it would be good to avoid it.
In fact I don't need to keep the pages maped for the kernel, as the hugepages are protected against swapping out, and they are mapped for the user space application that will process the received data.
So what I'm looking for is a function sg_alloc_table_from_user_hugepages, that could take such a user-space address of the hugepages-based memory buffer, and transfer it directly into the right scatterlist, without performing unnecessary and memory-consuming mapping for the kernel.
Of course such a function should verify that the buffer indeed consists of hugepages.
I have found and read these posts: (A), (B), but couldn't find a good answer.
Is there any official method to do it in the current Linux kernel?
At the moment I have a very inefficient solution based on get_user_pages_fast:
int sgt_prepare(const char __user *buf, size_t count,
struct sg_table * sgt, struct page *** a_pages,
int * a_n_pages)
{
int res = 0;
int n_pages;
struct page ** pages = NULL;
const unsigned long offset = ((unsigned long)buf) & (PAGE_SIZE-1);
//Calculate number of pages
n_pages = (offset + count + PAGE_SIZE - 1) >> PAGE_SHIFT;
printk(KERN_ALERT "n_pages: %d",n_pages);
//Allocate the table for pages
pages = vzalloc(sizeof(* pages) * n_pages);
printk(KERN_ALERT "pages: %p",pages);
if(pages == NULL) {
res = -ENOMEM;
goto sglm_err1;
}
//Now pin the pages
res = get_user_pages_fast(((unsigned long)buf & PAGE_MASK), n_pages, 0, pages);
printk(KERN_ALERT "gupf: %d",res);
if(res < n_pages) {
int i;
for(i=0; i<res; i++)
put_page(pages[i]);
res = -ENOMEM;
goto sglm_err1;
}
//Now create the sg-list
res = sg_alloc_table_from_pages(sgt, pages, n_pages, offset, count, GFP_KERNEL);
printk(KERN_ALERT "satf: %d",res);
if(res < 0)
goto sglm_err2;
*a_pages = pages;
*a_n_pages = n_pages;
return res;
sglm_err2:
//Here we jump if we know that the pages are pinned
{
int i;
for(i=0; i<n_pages; i++)
put_page(pages[i]);
}
sglm_err1:
if(sgt) sg_free_table(sgt);
if(pages) kfree(pages);
* a_pages = NULL;
* a_n_pages = 0;
return res;
}
void sgt_destroy(struct sg_table * sgt, struct page ** pages, int n_pages)
{
int i;
//Free the sg list
if(sgt->sgl)
sg_free_table(sgt);
//Unpin pages
for(i=0; i < n_pages; i++) {
set_page_dirty(pages[i]);
put_page(pages[i]);
}
}
The sgt_prepare function builds the sg_table sgt structure that i can use to create the DMA mapping. I have verified that it contains the number of entries equal to the number of hugepages used.
Unfortunately, it requires that the list of the pages is created (allocated and returned via the a_pages pointer argument), and kept as long as the buffer is used.
Therefore, I really dislike that solution. Now I have 256 2MB hugepages used as a DMA buffer. It means that I have to create and keeep unnecessary 128*1024 page structures. I also waste 512 MB of kernel address space for unnecessary kernel mapping.
The interesting question is if the a_pages may be kept only temporarily (until the sg-list is created)? In theory it should be possible, as the pages are still locked...

ebpf packet filter on payload matching

I am new in ebpf & xdp topic and want to do learn it. My question is how to use ebpf filter to filter the packet on specific payload matching? for example, if the data(payload) of the packet is 1234 its passes to the network stack otherwise it blocks the packet. I reached payload length. For example, if I want to match the message payload length it works fine but when I start matching the payload characters I got an error. here is my code:
int ret_val;
unsigned long payload_offset;
unsigned long payload_size;
const char *payload = "test";
struct ethhdr *eth = data;
if ((void*)eth + sizeof(*eth) <= data_end) {
struct iphdr *ip = data + sizeof(*eth);
if ((void*)ip + sizeof(*ip) <= data_end) {
if (ip->protocol == IPPROTO_UDP ) {
struct udphdr *udp = (void*)ip + sizeof(*ip);
if ((void*)udp + sizeof(*udp) <= data_end) {
if (udp->dest == ntohs(5005)) {
payload_offset = sizeof(struct udphdr);
payload_size = ntohs(udp->len) - sizeof(struct udphdr);
unsigned char *s = (unsigned char *)&payload_size;
if (ret_val == __builtin_memcmp(s,payload,4) == 0) {
return XDP_DROP;
}
}
}
}
}
}
The error had removed but unable to compare the payload... I am sending the UDP message from python socket code. If I compare the payload length it works fine.
What did you try? You should probably read a bit more about eBPF to try to understand how to process packets, the basic example you give does not sound too complicated.
Basically you would have to parse the headers to see where your payload begins. Simple BPF parsing examples might help you understand the principles:
Start from beginning of header (e.g. Ethernet at first)
Check packet is long enough to hold the header (or you would risk an out-of-bound access when trying to access the upper layers otherwise)
Add header length to get the offset of your next header (e.g. IPv4, then e.g. TCP...)
Rinse and repeat.
In your case you would process all headers until you get the offset of the data payload. Note that this is trivial if the traffic you try to match always has the same headers (e.g. always IPv4 and UDP), but you get more cases to sort out if there is a mix (IPv4 + IPv6, encapsulation, IPv4 options...).
Once you have the offset for your data, just compare data at this offset to your pattern (that you may hardcode in the BPF program or get from a BPF map, depending on your use case). Note that you do not have access to strcmp(), but __builtin_memcmp() is available if you need to compare more than 64 bits.
(All the above applying of course to a C program that you would compile into an object file containing eBPF instructions with the LLVM back-end.)
If you were to search for a string at an arbitrary offset in the payload, know that eBPF now supports (bounded) loops since kernel 5.3 (if I remember correctly).
Your edit is pretty much a new question, so here an updated answer. Please consider opening a new question instead in the future.
There are a number of things that are wrong in your program. In particular:
1| payload_offset = sizeof(struct udphdr);
2| payload_size = ntohs(udp->len) - sizeof(struct udphdr);
3| unsigned char *s = (unsigned char *)&payload_size;
4|
5| if (ret_val == __builtin_memcmp(s, payload, 4) == 0) {
6| return XDP_DROP;
7| }
On line 1, your payload_offset variable is not an offset, it just contains the length of the UDP header. You would need to add that to the start of the UDP header to get the actual payload offset.
Line 2 is fine.
Line 3 does not make any sense! You make s (that you later compare to your pattern) point towards the size of the payload? (a.k.a “I told you so in the comments! :)”). Instead, it should point to... the beginning of the payload, maybe? So, basically, data + payload_offset (once offset is fixed).
Between lines 3 and 5, the check on payload length is missing. When you try to access your payload in s (__builtin_memcmp(s, payload, 4)), you try to compare four bytes of packet data; you must ensure that the packet is long enough to read those four bytes (just as you checked the length each time before you read from an Ethernet, IP or UDP header field).
While at it, we can also check that the length of the payload is equal to the length of the pattern to match, and exit if they differ without having to compare the bytes.
Line 5 has a == instead of =, as discussed in the comments. Easy to fix. However, I had no luck with __builtin_memcmp() for your program, it seems LLVM does not want to inline it and turns it into a failing function call. Never mind, we can work without it. For your example, you can cast to int and compare the four-byte long values directly. For longer patterns, and for recent kernels (or by unrolling if pattern size is fixed), we can use bounded loops.
Here is a amended version of your program, that works on my setup.
#include <arpa/inet.h>
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/udp.h>
int xdp_func(struct xdp_md *ctx)
{
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
char match_pattern[] = "test";
unsigned int payload_size, i;
struct ethhdr *eth = data;
unsigned char *payload;
struct udphdr *udp;
struct iphdr *ip;
if ((void *)eth + sizeof(*eth) > data_end)
return XDP_PASS;
ip = data + sizeof(*eth);
if ((void *)ip + sizeof(*ip) > data_end)
return XDP_PASS;
if (ip->protocol != IPPROTO_UDP)
return XDP_PASS;
udp = (void *)ip + sizeof(*ip);
if ((void *)udp + sizeof(*udp) > data_end)
return XDP_PASS;
if (udp->dest != ntohs(5005))
return XDP_PASS;
payload_size = ntohs(udp->len) - sizeof(*udp);
// Here we use "size - 1" to account for the final '\0' in "test".
// This '\0' may or may not be in your payload, adjust if necessary.
if (payload_size != sizeof(match_pattern) - 1)
return XDP_PASS;
// Point to start of payload.
payload = (unsigned char *)udp + sizeof(*udp);
if ((void *)payload + payload_size > data_end)
return XDP_PASS;
// Compare each byte, exit if a difference is found.
for (i = 0; i < payload_size; i++)
if (payload[i] != match_pattern[i])
return XDP_PASS;
// Same payload, drop.
return XDP_DROP;
}

ff_replay substructure in ff_effect empty

I am developing a force feedback driver (linux) for a yet unsupported gamepad.
Whenever a application in userspace requests a ff-effect (e.g rumbling), a function in my driver is called:
static int foo_ff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
this is set by the following code inside my init function:
input_set_capability(dev, EV_FF, FF_RUMBLE);
input_ff_create_memless(dev, NULL, foo_ff_play);
I'm accessing the ff_effect struct (which is passed to my foo_ff_play function) like this:
static int foo_ff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
{
u16 length;
length = effect->replay.length;
printk(KERN_DEBUG "length: %i", length);
return 0;
}
The problem is, that the reported length (in ff_effect->replay) is always zero.
That's confusing, since i am running fftest on my device, and fftest definitely sets the length attribute: https://github.com/flosse/linuxconsole/blob/master/utils/fftest.c (line 308)
/* a strong rumbling effect */
effects[4].type = FF_RUMBLE;
effects[4].id = -1;
effects[4].u.rumble.strong_magnitude = 0x8000;
effects[4].u.rumble.weak_magnitude = 0;
effects[4].replay.length = 5000;
effects[4].replay.delay = 1000;
Does this have something to do with the "memlessness"? Why does the data in ff_replay seem to be zero if it isn't?
Thank you in advance
Why is the replay struct empty?
Taking a look at https://elixir.free-electrons.com/linux/v4.4/source/drivers/input/ff-memless.c#L406 we find:
static void ml_play_effects(struct ml_device *ml)
{
struct ff_effect effect;
DECLARE_BITMAP(handled_bm, FF_MEMLESS_EFFECTS);
memset(handled_bm, 0, sizeof(handled_bm));
while (ml_get_combo_effect(ml, handled_bm, &effect))
ml->play_effect(ml->dev, ml->private, &effect);
ml_schedule_timer(ml);
}
ml_get_combo_effect sets the effect by calling ml_combine_effects., but ml_combine_effects simply does not copy replay.length to the ff_effect struct which is passed to our foo_play_effect (at least not if the effect-type is FF_RUMBLE): https://elixir.free-electrons.com/linux/v4.4/source/drivers/input/ff-memless.c#L286
That's why we cannot read out the ff_replay-data in our foo_play_effect function.
Okay, replay is empty - how can we determine how long we have to play the effect (e.g. FF_RUMBLE) then?
Looks like the replay structure is something we do not even need to carry about. Yes, fftest sets the length and then uploads the effect to the driver, but if we take a look at ml_ff_upload (https://elixir.free-electrons.com/linux/v4.4/source/drivers/input/ff-memless.c#L481), we can see the following:
if (test_bit(FF_EFFECT_STARTED, &state->flags)) {
__clear_bit(FF_EFFECT_PLAYING, &state->flags);
state->play_at = jiffies +
msecs_to_jiffies(state->effect->replay.delay);
state->stop_at = state->play_at +
msecs_to_jiffies(state->effect->replay.length);
state->adj_at = state->play_at;
ml_schedule_timer(ml);
}
That means that the duration is already handled by the input-subsystem. It starts the effect and also stops it as needed.
Furthermore we can see at https://elixir.free-electrons.com/linux/v4.4/source/include/uapi/linux/input.h#L279 that
/*
* All duration values are expressed in ms. Values above 32767 ms (0x7fff)
* should not be used and have unspecified results.
*/
That means that we have to make our effect play at least 32767ms. Everything else (stopping the effect before) is up to the scheduler - which is not our part :D

Is the sscanf function in the Linux kernel susceptible to buffer overflow attacks?

From what I understand, a typical buffer overflow attack occurs when an attack overflows a buffer of memory on the stack, thus allowing the attacker to inject malicious code and rewrite the return address on the stack to point to that code.
This is a common concern when using functions (such as sscanf) that blindly copy data from one area to another, checking one for a termination byte:
char str[8]; /* holds up to 8 bytes of data */
char *buf = "lots and lots of foobars"; /* way more than 8 bytes of data */
sscanf(buf, "%s", str); /* buffer overflow occurs here! */
I noticed some sysfs_ops store functions in the Linux kernel are implemented with the Linux kernel's version of the sscanf function:
static char str[8]; /* global string */
static ssize_t my_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
sscanf(buf, "%s", str); /* buf holds more than 8 bytes! */
return size;
}
Suppose this store callback function is set to a writable sysfs attribute. Would a malicious user be able to intentionally overflow the buffer via a write call?
Normally, I would expect guards against buffer overflow attacks -- such as limiting the number of bytes read -- but I see none in a good number of functions (for example in drivers/scsi/scsi_sysfs.c).
Does the implementation of the Linux kernel version of sscanf protect against buffer overflow attacks; or is there another reason -- perhaps buffer overflow attacks are impossible given how the Linux kernel works under the hood?
The Linux sscanf() is vulnerable to buffer overflows; inspection of the source shows this. You can use width specifiers to limit the amount a %s is allowed to write. At some point your str must have had copy_from_user() run on it as well. It is possible the user space to pass some garbage pointer to the kernel.
In the version of Linux you cited, the scsi_sysfs.c does have a buffer overflow. The latest version does not. The committed fix should fix the issue you see.
Short answer:
sscanf, when well called, will not cause buffer overflow, especially in sysfs xxx_store() function. (There are a lot sscanf in sysfs XXX_store() examples), because Linux kernel add a '\0' (zero-terminated) byte after the string (buf[len] = 0;) for your XXX_store() function.
Long answer:
Normally, sysfs are defined to have a strict formatted data. Since you expect 8 bytes at most, it's reasonable to limit the size you get like this:
static char str[8]; /* global string */
static ssize_t my_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
if (size > 8) {
printk("Error: Input size > 8: too large\n");
return -EINVAL;
}
sscanf(buf, "%s", str); /* buf holds more than 8 bytes! */
return size;
}
(Note: use 9 rather than 8, if you expect a 8-bytes string plus '\n')
(Note that you do reject some inputs such as those with many leading white spaces. However, who would send a string with many leading white spaces? Those who want to break your code, right? If they don't follow your spec, just reject them.)
Note that Linux kernel purposely inserts a '\0' at offset len (i.e. buf[len] = 0;) when the user write len bytes to sysfs purposely for safe sscanf, as said in a comment in kernel 2.6: fs/sysfs/file.c:
static int
fill_write_buffer(struct sysfs_buffer * buffer, const char __user * buf, size_t count)
{
int error;
if (!buffer->page)
buffer->page = (char *)get_zeroed_page(GFP_KERNEL);
if (!buffer->page)
return -ENOMEM;
if (count >= PAGE_SIZE)
count = PAGE_SIZE - 1;
error = copy_from_user(buffer->page,buf,count);
buffer->needs_read_fill = 1;
/* if buf is assumed to contain a string, terminate it by \0,
so e.g. sscanf() can scan the string easily */
buffer->page[count] = 0;
return error ? -EFAULT : count;
}
...
static ssize_t
sysfs_write_file(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
struct sysfs_buffer * buffer = file->private_data;
ssize_t len;
mutex_lock(&buffer->mutex);
len = fill_write_buffer(buffer, buf, count);
if (len > 0)
len = flush_write_buffer(file->f_path.dentry, buffer, len);
if (len > 0)
*ppos += len;
mutex_unlock(&buffer->mutex);
return len;
}
Higher kernel version keeps the same logic (though already completely rewritten).

directions about customized Layer 2 implementation in linux

I have some machines running on the same network. One node is the control node which distributes traffic coming to it to the other nodes. The thing is that I want to have a custom protocol header between MAC header and IP(or whatever) payload incoming to the control node.
Control node receives this any packet like this:
------------------------------------------------
| Layer 2 | IP(or whatever protocol) | Payload |
------------------------------------------------
This packet should be distributed like this to other nodes
----------------------------------------------------------------
| Layer 2 | Custom Header | IP(or whatever protocol) | Payload |
----------------------------------------------------------------
I want some directions to do such a thing, Is there any current solution which I can use and I have to hack kernel for it from the scratch. A similar approach is to use L2TP but that runs over IP layer so I dont want that.
I also want this communication to be appeared as a seperate interface in linux like tun0 apart from physical eth0 interface.
Any help or ideas would be highly appreciated.
I dont know in what stack-exchange website this question belongs to so directions to correct website are also appreciated.
Your case is very similar to VLAN, where VLAN header also sits between L2 header and IP header. You can take a look at VLAN code, especially net/8021q/vlan_dev.c.
The key here is you need to construct your own L2 header, so you need to register your own header_ops like what VLAN does:
static const struct header_ops vlan_header_ops = {
.create = vlan_dev_hard_header,
.rebuild = vlan_dev_rebuild_header,
.parse = eth_header_parse,
};
and register it during initialization:
dev->header_ops = &vlan_header_ops;
dev->hard_header_len = real_dev->hard_header_len + VLAN_HLEN;
The ->create() function pointer here is used to create the custom header:
static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev,
unsigned short type,
const void *daddr, const void *saddr,
unsigned int len)
{
struct vlan_hdr *vhdr;
unsigned int vhdrlen = 0;
u16 vlan_tci = 0;
int rc;
if (!(vlan_dev_priv(dev)->flags & VLAN_FLAG_REORDER_HDR)) {
vhdr = (struct vlan_hdr *) skb_push(skb, VLAN_HLEN);
vlan_tci = vlan_dev_priv(dev)->vlan_id;
vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb);
vhdr->h_vlan_TCI = htons(vlan_tci);
/*
* Set the protocol type. For a packet of type ETH_P_802_3/2 we
* put the length in here instead.
*/
if (type != ETH_P_802_3 && type != ETH_P_802_2)
vhdr->h_vlan_encapsulated_proto = htons(type);
else
vhdr->h_vlan_encapsulated_proto = htons(len);
skb->protocol = htons(ETH_P_8021Q);
type = ETH_P_8021Q;
vhdrlen = VLAN_HLEN;
}
/* Before delegating work to the lower layer, enter our MAC-address */
if (saddr == NULL)
saddr = dev->dev_addr;
/* Now make the underlying real hard header */
dev = vlan_dev_priv(dev)->real_dev;
rc = dev_hard_header(skb, dev, type, daddr, saddr, len + vhdrlen);
if (rc > 0)
rc += vhdrlen;
return rc;
}

Resources