ebpf-tc: how to keep unique information inside a ebpf instance when same program is attached to multiple interface - ebpf

When we pin a MAP, we can able to share information from userspace to ebpf but it is system wide.
But if i want to share different value to different instance from tc ingress/egress (array map with size of 1)
Is there any way to pass argument ?
Map (unpinned unique per instance) - update from userspace
Any other way to communicate from userspace to kernel (while attaching or after)
Really appreciate your help.

Pinning a map doesn't make it system wide. Every map is always accessible system wide, pinning just adds a reference to the file system to make it easier to find and to make sure a map isn't removed even when not in use by any program.
Is there any way to pass argument ?
No, once a program is loaded, only the kernel can pass arguments(contexts) to a program, userspace can only use maps to communicate with eBPF programs.
Map (unpinned unique per instance) - update from userspace
Any userspace program with the right permissions can update any map as long as you can obtain a file descriptor to the map. Map FDs can be obtained:
By creating a new map
By opening a map pin
By using its unique ID (directly or by looping over all loaded maps)
By IPC from another process that already has the FD
Any other way to communicate from userspace to kernel (while attaching or after)
Maps are it. You can rewrite the program before loading it into the kernel by setting constants at specific locations, but not at attach time. One way which might be interesting is that on newer kernels, global data is supported. Which allow you to change the value of variables defined in the global scope from userspace. In this case the global data is packed in a array map with a single key/value.

Related

How to share a ebpf map between interfaces

Is it possible to share an ebpf Map between two network interfaces.
I want to write an XDP program and hook it on two devices namely eth0 and eth1. The implementation requires that they both use the same map.
Is it possible to load the same program, hooking them at eth0 and eth1 and use the same Map.
Thank you all!
Yes, this is entirely possible. An eBPF map is not attached to an interface, it is created in the kernel and then referenced by one or several of:
A file descriptor, from the application that created the map,
An eBPF program that uses the map,
A reference in another map of a dedicated type (array-of-maps, hash-of-maps),
A pinned path in the eBPF virtual file system.
eBPF maps can be shared between consecutive program runs, between kernel and user space, or - as for your use case - between different eBPF programs, no matter what interfaces they are attached to. Note that a given program could even be detached and later re-attached to another interface, anyway.
For reusing a map for several programs, this is done by pointing to the same map when you load your programs: What happens when you load one is that you get a handle to the map (its id or its pinned path, for example), get a file descriptor to the map from that handle, and place this file descriptor in your eBPF bytecode before loading it. Then the kernel translates the file descriptor into the relevant memory address.
What happens most of the time in practice is that this “relocation” step (placing the file descriptor in the bytecode) is handled for you by the framework you are using to load your programs, such as libbpf or bcc tools. For example, libbpf has a bpf_map__reuse_fd(struct bpf_map *map, int fd) function to explicitly reuse a given file descriptor for a specific map used by a program, after parsing an object file to extract the bytecode but before loading it.

Passing kernel params from U-Boot to ARM Linux when device tree is used

I've been investigating a change to some embedded software inasmuch as we want U-Boot to be able to pass specific command-line parameters to the kernel, ones that aren't necessarily known in advance.
This is so that the kernel can tell which U-Boot partition it was started by (we have two copies, one in /dev/mmc3boot0 and /dev/mmc3boot1, and they both share a single (redundant) environment space, so we can't use that alone to uniquely identify the instance).
One thought was to simply have each U-Boot write its ID to the shared environment when it boots but that has the downside that there are variants out there that do not currently do this. So, if we boot from one that does, it will write its ID and, if we then boot from one that doesn't, it will not change the ID back to blank, leading to incorrect information if we rely on that.
This is why we thought to use a kernel parameter - since older style U-Boot instances never supply an ID, we know it's running in boot0. The newer style would provide their actual ID so we could then search the two boot partitions to see which one it was in.
To that end, I've modified U-Boot so that it sets up ATAGs to pass through the extra parameter needed. Specifically:
I've define CONFIG_SYS_BOOT_GET_CMDLINE in arch\arm\include\asm\config.h so that boot_get_cmdline() is called.
I've modified that boot_get_cmdline() function so that it sets up a specific parameter before appending the normal parameters. In other words, rather than just plugh=xyzzy, we now get uboot_instance=42 plugh=xyzzy.
This all compiles fine and U-Boot successfully starts the kernel but the extra information is not being reflected in the Linux kernel, which has its kernel parameters set to the regular plugh=xyzzy.
On further research, it appears that we are falling afoul of the two possible ways to invoke the kernel. One of these is with ATAGs and one is with a flattened device tree (FDT), and they appear to be mutually exclusive (the kernel startup code selects one or the other based on signatures passed in with the pointer referencing either the ATAGs or FDT structure).
So my question is this. Given that the device tree is meant to be a fixed structure for the device you're describing, how do you pass arbitrary kernel command line parameters (calculated at runtime) when the bootloader is invoking the kernel?
You can use a dummy environment variable for your platform in include/configs/<board>.h.
For example, assume you have the following (simplified) UBoot environment variables for booting:
bootcmd=run mmcargs
run loadimage loadfdt
bootz ${loadaddr} - ${fdt_addr}
mmcargs=setenv bootargs blah=blah
This uses mmcargs to set up the kernel command line to use. What we need to do is to insert that dummy environment variable in a way that current UBoot instances supply nothing and new ones supply the actual ID. This is done simply with the following change:
mmcargs=setenv bootargs ${uboot_id_stanza} blah=blah
Then, during the initialization of the board, you can set this variable using env_set API, specifically by writing your own custom board_late_init of the board init code in board/<vendor>/<init_code>.c.
The following line should be placed at the end of the board_late_init function:
setenv("uboot_id_stanza", "uboot_id=<uniqueId>");
That way, the uboot_id variable setting is added to the kernel command line but, since you didn't do a saveenv, it doesn't become persistent. Every UBoot instance will set the correct ID (including old ones that don't set an ID).

Confusions about address binding

Compile time. If you know at compile time where the process will reside
in memory, then absolute code can be generated. For example, if you know
that a user process will reside starting at location R, then the generated
compiler code will start at that location and extend up from there. If, at
some later time, the starting location changes, then it will be necessary
to recompile this code. The MS-DOS .COM-format programs are bound at
compile time.
What can be the reason of the starting location to change? Can it be
because of context switching/swapping ?
Does absolute code means binary code?
Load time. If it is not known at compile time where the process will reside
in memory, then the compiler must generate relocatable code. In this case,
final binding is delayed until load time. If the starting address changes, we
need only reload the user code to incorporate this changed value.
How is relocatable code different from absolute code? Does it contain info about base,limit and relocation register?
How is reloading more efficient then recompiling as they mentioned only reload means no recompiling only reload?
Execution time. If the process can be moved during its execution from
one memory segment to another, then binding must be delayed until run
time. .
Why it may be needed to move a process during it's execution?
The compile-time and load-time address-binding methods generate
identical logical and physical addresses. However, the execution-time address-binding scheme results in differing logical and physical addresses.
How compile and load-time methods generate identical logical and physical addresses?
To begin with, I would find a better source for your information. What you have is very poor.
What can be the reason of the starting location to change? Can it be because of context switching/swapping ?
You change the code or need the code to be loaded at a different location in memory.
Does absolute code means binary code?
No. They are independent concepts.
How is relocatable code different from absolute code? Does it contain info about base,limit and relocation register?
Relocatable code uses relative addresses, generally relative to the program counter.
(Base limit and relocation registers would be a system specific ocncept).
How is reloading more efficient then recompiling as they mentioned only reload means no recompiling only reload?
Let's say two different programs use the same dynamic library. They made need to have loaded at different locations in memory. It's not an efficiency issue.
Why it may be needed to move a process during it's execution?
This is what was done in ye olde days before virtual memory. To my knowledge no one does this any more.
How compile and load-time methods generate identical logical and physical addresses?
I don't know what the &^54 they are talking about. That statement makes no sense.
Dynamic libraries (.dll .so) are relocatable, because they might appear at different adresses in different applications, but in order to save memory, the operating system only has one copy in physical memory (virtual memory is great), and each application has read only access.
Same happens for applications that are relocatable. For security, it is also wize that the addresses are random - some remote attacks are slighty harder

Force writing only-read register #Modbus

I was wondering,
Is there anyway to force writing on a 'Read-Only' Modbus Register?
Does defining a Register as a 'Read-Only' is secure enough or can be bypassed??
Thanks for the answers!
The correct way to define a "read-only" analog variable in Modbus is to map it as an input register. There is no function code defined in Modbus to write to an input register.
For historical reasons, several vendors maps all their variables as holding registers, which are theoretically read/write, i.e, there's a Write Multiple Registers function. Whenever they map a read only variable as a holding register, they must assert that the write functions fail. However, there's no standard exception code for this, as a holding register should be read/write. This is only one of Modbus' idiosyncrasies.
Getting back to your question, if you map your variable as an input register, you can be sure that the protocol will not allow a master to write to it. If, for interoperability issues you map it as a holding register, the protocol will allow the master to use a write funcion to change its value, and it is up to you to block in your device implementation.

Why are there ioctl calls in socket.c?

Trying to understand why there are ioctl calls in socket.c ? I can see a modified kernel that I am using, it has some ioctl calls which load in the required modules when the calls are made.
I was wondering why these calls ended up in socket.c ? Isn't socket kind of not-a-device and ioctls are primarily used for device.
Talking about 2.6.32.0 heavily modified kernel here.
ioctl suffers from its historic name. While originally developed to perform i/o controls on devices, it has a generic enough construct that it may be used for arbitrary service requests to the kernel in context of a file descriptor. A file descriptor is an opaque value (just an int) provided by the kernel that can be associated with anything.
Now if you treat a file descriptor and think of things as files, which most *nix constructs do, open/read/write/close isn't enough. What if you want to label a file (rename)? what if you want to wait for a file to become available (ioctl)? what if you want to terminate everything if a file closes (termios)? all the "meta" operations that don't make sense in the core read/write context are lumped under ioctls; fctls; etc. unless they are so frequently used that they deserve their own system call (e.g. flock(2) functionality in BSD4.2)