XDP program ipheader, data, nh_off confusion - bpf

I'm studying XDP codes right now and I'm having some confusion as regards to how programs approach certain parts of a packet header.
So! When I look at the code that gets the IP address of a packet, it goes like:
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;
}
Now here are some things that confuse me :
struct iphdr *iph = data + nh_off;
I thought nh_off is the offset value to the next header, so if you add data + nh_off, shouldn't that take you to the next packet?
Because to my understanding, if you add next header offset to the data, there should be a next packet waiting to be processed!
What does
(void*)&iph[1]
do exactly? I tried to guess what this line of code does for a few days but I still have no clue at all.
I am so sorry if my questions are too absorb or vague.. This things have been bothering me for a while and I would greatly appreciate it if someone could share their knowledge with me. Thank you so much in advance.

It all depends on your code, since I don't see how nh_off is defined in your case. But most of the time, it does point to the next header, so we would have:
nh_off being the offset of the next header after Ethernet header has been parsed, i.e. nh_off is the offset of the IP header in the packet (typically, it's set to 14 at this stage, the number of bytes in the Ethernet header if no VLAN/encap is used).
Setting struct iphdr *iph = data + nh_off; declares and initialises iph as a struct iphdr pointer, so we can reuse it afterwards to easily reach each field from the IPv4 header. It points to data + nh_off, i.e. the beginning of the packet plus the offset at which the IPv4 header begins in the packet.
Next packet to be processed is not accessible from within your eBPF program; you would get a new ctx with a data pointer pointing to it when that new packet is processed with a new call to the BPF program, but you only see just one packet at once.
So iph points to the beginning of your IPv4 header. We can use that pointer to easily reach the individual fields (e.g. iph->protocol to get L4 protocol). But before we do that we must ensure that the packet is long enough and actually contains those field. Otherwise we could do an out-of-bound access (therefore the verifier would reject the program in the first place). This is the check we do here: if ((void*)&iph[1] > data_end) return 0;
In that verification, (void*)&iph[1] means: i) Consider a struct iphdr * array (&iph, a pointer to a pointer to a struct iphdr). ii) Take the second cell of that array, e.g. the address of the struct pointed by the second struct iphdr *, e.g. the address of the byte that starts right after the first struct iphdr in the packet. And iii) cast it as a void * so we can compare it with data_end. In other words, this is a way to compare data_end (the address in memory right after the last byte of the packet) and the address of the byte right after IPv4 header (so possibly first byte of L4 is packet is long enough). If (void*)&iph[1] is bigger than data_end, then the IPv4 header we considered is longer than the actual packet we got, and we cannot afford to dereference iph to try to reach e.g. the protocol field.
With a diagram, maybe:
Packet data
| Ethernet | IPv4 | IPv4 data (e.g. L4, data) |
+--------------+--------------------+------ ... ----------------------+
^ ^ ^ ^
data data + nh_off | data_end
iph |
&iph[0] &iph[1]
We would have an issue to access iph->protocol if we had the following instead (this is why we return 0 if the comparison succeeds):
Packet data
| Ethernet | <something> | End of packet
+--------------+---------------- +
^ ^ ^ ^
data data + nh_off | |
iph | |
&iph[0] | &iph[1]
data_end

Related

Dynamic generation of signal spies in testbench

I have a .txt file that contains certain signals that I want to monitor in my testbench during the application of some stimulus.
I am creating an initial block in which I am reading the file and then I try to generate a init_signal_spy() for every one of the lines that I have read.
The code that I have written up until this point has the following format:
module testbench();
logic probes[];
initial begin : read_signals_to_dump_at
automatic int fd;
automatic string fname,line,line_stripped;
if ($value$plusargs("sigfile=%s",fname)) begin : read
fd = $fopen(fname,"r");
while($fgets(line,fd)) begin
//static logic net_to_be_probed;
automatic sig_and_spy entry = new();
// Trim away the '\n' from the line.
line_stripped = line.substr(0,line.len()-2);
// Resize the array
probes = new [probes.size() + 1] (probes);
// Link the extracted new line with the probe list
// - this raises an error "An invalid empty string was passed in as the Destination object."
// - expected since the last element is empty...
$init_signal_spy(line_stripped, probes[probes.size()-1] , 1);
end
end
end : read_signals_to_dump_at
endmodule
In the code above, just before I issue the generation for the spy, I get why the error
An invalid empty string was passed in as the Destination object.
is generated by the compiler. Although the array has been resized, it does not hold any element i.e., its empty. Thus, I tried creating locally a logic variable that then I assign to the signal spy within the loop in the following manner:
module testbench();
logic probes[];
initial begin : read_signals_to_dump_at
automatic int fd;
automatic string fname,line,line_stripped;
if ($value$plusargs("sigfile=%s",fname)) begin : read
fd = $fopen(fname,"r");
while($fgets(line,fd)) begin
logic new_probe;
// Trim away the '\n' from the line.
line_stripped = line.substr(0,line.len()-2);
// Resize the array and copy old values.
probes = new [probes.size() + 1] (probes);
// Add the new probe to the Testbenchs' probes array
probes[probes.size()-1] = new_probe;
// Again, An invalid empty string was passed in as the Destination object.
$init_signal_spy(line_stripped, probes[probes.size()-1] , 1);
end
end
end : read_signals_to_dump_at
endmodule
But then again, I see the same error at runtime during the simulation. So...Is there a way of achieving such a "dynamic" signal monitoring in the testbench somehow? As far as I understood the error concerns that the destination object is NOT a signal of the testbench. Thus the logic new_probe has no effect. Which is to be expected I mean, but is there a way of achieving the desired behavior in the Testbench via sysverilog?
You have at least two problems.
Both the source and destination arguments to init_signal_spy() need to be strings. Your destination argument is an integral variable with a 0 value, and that gets interpreted as a null string. init_signal_spy() was designed for mixed language simulation, and using strings was the only way to achieve that.
Your destination variable should be queue, not a dynamic array. Every time you re-size a dynamic array, the previous elements get relocated and that breaks the previous connection made by signal spy.
This example shows the proper syntax for string this up
module top;
int A[$];
int s1,s2;
initial begin
A.push_back(0);
$init_signal_spy("s1","A[0]");
A.push_back(0);
$init_signal_spy("s2","A[1]");
#1 s1 = 1;
#1 s2 = 2;
#1 $display("%p",A);
end
endmodule
A far better solution for performance is converting your .txt file into actual SystemVerilog code that can be compiled into your testbench.

STM32 HAL_FLASH_Program not working as expected

I'm trying to program FLASH using HAL_FLASH_Program() function. Precisely speaking, I've written a function which is expected to write two measurements
to flash at a set time interval (e.g. 3 seconds). However, when called, the function manages to write only the first one while ignoring the second one. Can't HAL_FLASH_Program be used twice? What am I doing wrong? I just want to mention that I'm utterly new to STM32 programming, so any helpful suggestions would be much appreciated. Here is the code:
void writeFlash(void){
mem = returnPointerToFirstEmptyAddressInSector();
Address = (uint32_t)mem;
var1.f = Temperature;
var2.f = SD;
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGSERR | FLASH_FLAG_PGPERR);
HAL_FLASH_Program(TYPEPROGRAM_WORD, Address, var1.i );
Address++;
HAL_FLASH_Program(TYPEPROGRAM_WORD, Address, var2.i);
HAL_FLASH_Lock();
}
The address is not properly aligned
The declaration of Address is not shown, but from the line
Address = (uint32_t)mem;
I'd guess it's an unsigned long. Later, you are incrementing Address after the first write with
Address++;
and use this value to program the second value. But since Address is presumably an integer, not a pointer type, it would be incremented by one instead of the word size (4), and pointing to an address that is partially overlapping the previously written value, and not aligned for word-sized writes. The second write operation would inevitably fail. As #JMA suggests in the comments, check the return value of HAL_FLASH_Program(), and the error code
uint32_t ret = HAL_FLASH_Program(TYPEPROGRAM_WORD, Address, var2.i);
switch(ret) {
case HAL_OK:
break;
case HAL_TIMEOUT:
printf("HAL_FLASH_Program() timeout!\n");
break;
case HAL_ERROR:
printf("HAL_FLASH_Program() error 0x%08x, see *hal_flash.h for bit definitions\n", HAL_FLASH_GetError());
break;
default:
printf("HAL_FLASH_Program() returned unknown status %lu\n", ret);
}
Write a double word at once
The HAL library supports writing 64 bit values at once, so you can write two 32 bit integers in one operation.
HAL_FLASH_Program(TYPEPROGRAM_DOUBLEWORD, Address, ((uint64_t)var2.i << 32) | var1.i);
Just ensure that Address is aligned to a doubleword boundary, i.e. divisible by 8.

Can pysnmp return octectstring values only

I am doing a small script to get SNMP traps with PySnmp.
I am able to get the oid = value pairs, but the value is too long with a small information in the end. How can I access the octectstring only which comes in the end of the value. Is there a way other than string manipulations? Please comment.
OID =_BindValue(componentType=NamedTypes(NamedType('value', ObjectSyntax------------------------------------------------(DELETED)-----------------(None, OctetString(b'New Alarm'))))
Is it possible to get the output like the following, as is available from another SNMP client:
.iso.org.dod.internet.private.enterprises.xxxx.1.1.2.2.14: CM_DAS Alarm Traps:
Edit - the codes are :
**for oid, val in varBinds:
print('%s = %s' % (oid.prettyPrint(), val.prettyPrint()))
target.write(str(val))**
On screen, it shows short, but on file, the val is so long.
Usage of target.write( str(val[0][1][2])) does not work for all (program stops with error), but the 1st oid(time tick) gets it fine.
How can I get the value from tail as the actual value is found there for all oids.
Thanks.
SNMP transfers information in form of a sequence of OID-value pairs called variable-bindings:
variable_bindings = [[oid1, value1], [oid2, value2], ...]
Once you get the variable-bindings sequence from SNMP PDU, to access value1, for example, you might do:
variable_binding1 = variable_bindings[0]
value1 = variable_binding1[1]
To access the tail part of value1 (assuming it's a string) you could simply subscribe it:
tail_of_value1 = value1[-10:]
I guess in your question you operate on a single variable_binding, not a sequence of them.
If you want pysnmp to translate oid-value pair into a human-friendly representation (of MIB object name, MIB object value), you'd have to pass original OID-value pair to the ObjectType class and run it through MIB resolver as explained in the documentation.
Thanks...
the following codes works like somwwhat I was looking for.
if str(oid)=="1.3.6.1.2.1.1.3.0":
target.write(" = str(val[0][1]['timeticks-value']) = " +str(val[0][1]['timeticks-value'])) # time ticks
else:
target.write("= val[0][0]['string-value']= " + str(val[0][0]['string-value']))

Connect internal signal to output port in MyHDL module

Considering the following example (a simple 8-bit counter), is there a simpler way to connect the internal s_count signal to the o_count port?
def counter(i_clk, i_reset, o_count):
""" A free-running 8-bit counter with a synchronous reset """
s_count = Signal(intbv(0)[8:])
#always(i_clk.posedge)
def count():
if i_reset == 1:
s_count.next = 0
else:
s_count.next = s_count + 1
#always_comb
def outputs():
o_count.next = s_count
return count, outputs
Of course, I could directly increment o_count in the count function but this translates to an inout port in the generated VHDL module, which I don't want.
I suspect directly incrementing o_count is an acceptable solution.
Indeed, it translates to an inout because you cannot read output ports in VHDL.
However, this will only happen when you convert this module as a top module. It is likely that this is a small submodule however. In that case, the hierarchy is flattened out and o_count will be an internal signal.

what could limit bytes read in from read()

I am reading bytes off a socket initialised like this:
fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
However when I read from this socket
char buf[ETH_FRAME_LEN]
len = read(fd, buf, sizeof(buf));
len shows only 1500 bytes were read. I checked with wireshark and the packet returned is 5854. The total length field under IP says 5840 (so + 14 bytes for ethernet header = 5854). I tried using a larger buffer (6000) but still only 1500 bytes were being read off the wire.
I tried requesting a smaller file from the server (1504 bytes), but I get the same results. As it is a raw socket, the data read in includes the ethernet headers, so it is not reading the last 4 bytes into the buffer.
What could be the cause of this? I'm not aware of any argument to socket() that could cause this.
What happens if you try calling read again? Is the next chunk of the message quickly returned?
From the read man page (my emphasis)
read() attempts to read up to count bytes
If you want to read a certain number of bytes, you should be prepared to call read in a loop until you receive your target total cumulatively over the calls.
What is happening is that you're getting exactly one Ethernet MTU's worth of payload per call to read().
read() returns:
On success, the number of bytes read is returned (zero indicates end of
file), and the file position is advanced by this number. It is not an
error if this number is smaller than the number of bytes requested;
this may happen for example because fewer bytes are actually available
right now (maybe because we were close to end-of-file, or because we
are reading from a pipe, or from a terminal), or because read() was
interrupted by a signal. On error, -1 is returned, and errno is set
appropriately. In this case it is left unspecified whether the file
position (if any) changes.
You can try to use recv() with MSG_WAITALL instead of pure read():
This flag requests that the operation block until the full
request is satisfied. However, the call may still return less
data than requested if a signal is caught, an error or disconnect occurs, or the next data to be received is of a different
type than that returned.
len = recv(fd, buf, sizeof(buf), MSG_WAITALL);
Another way is to read or recv in a loop like:
ssize_t Recv(int fd, void* buf, ssize_t n)
{
ssize_t read = 0;
ssize_t r;
while(read != n)
{
r = recv(fd, ((char*)buf)+read, n-read, 0);
if(r == -1)
return (read) ? read : -1;
if(r == 0)
return 0;
read += r;
}
return read;
}