Detailed documentation¶
Compile/load eCLAT chains and HIKe programs¶
[to be completed]
The source code files (with extension .bpf.c) of each package package_name are copied
in the folder eclat_daemon/components/programs/package_name
The include files with extension .h are read from the same folder if present, if they are
not available in the same folder, they are read from eclat_daemon/hike_v3/src
Creating new packages¶
A package is a collection of hike programs, eclat chains, shell scripts and python programs, hosted in a github repository. By convention, the github repository name of a package foo is called hikepkg-foo.
To create a new package you should use the hikepkg-template github repository hosted in https://github.com/netgroup/hikepkg-template. You can fork this repository and call it hikepkg-foo, where foo is the name of your package.
Registering packages¶
The packages need to be registered in the mongoDB which is exposed here http://eclat.netgroup.uniroma2.it/packages
The link is this one: https://cloud.mongodb.com/v2/61d8710bbff1860d02c04427#clusters
Web APIs for updating the DB are under development, for the time being, please contact the maintaners!
eCLAT transpiler labels (resolved at load time)¶
It is possible to define labels in eCLAT scripts that are replaced at load time when the eCLAT script
is transpiled. The value of a label is set with the --define option of the eclat.py.
For example if the label DEVNAME is present in the eCLAT script script.eclat,
the value eth0 can assigne to this label as follows:
python eclat.py --load script.eclat --define DEVNAME eth0
Read and write from/to BPF maps¶
Read the full map into a python list
include os
include json
# Command Abstraction Layer
include cal
BASE_PATH = '/sys/fs/bpf/maps'
PACKAGE = 'package_name'
PROGRAM = 'program_name'
MAP = 'map_name'
map_path = f"{BASE_PATH}/{PACKAGE}/{PROGRAM}/{MAP}"
map_as_array = []
if os.path.exists(map_path):
try :
map_as_array = json.loads(cal.bpftool_map_dump(map_path))
except Exception as e:
print (e)
print (map_path)
return -1
else:
return -1
# map_as_array contains the map as a list of (key, value) pairs
print (map_as_array)
Update a map with a (key, value) pair using cal_map_update and in raw hex mode
include os
from hex_types import u32
# Command Abstraction Layer
include cal
BASE_PATH = '/sys/fs/bpf/maps'
PACKAGE = 'package_name'
PROGRAM = 'program_name'
MAP = 'map_name'
map_path = f"{BASE_PATH}/{PACKAGE}/{PROGRAM}/{MAP}"
if os.path.exists(map_path):
try :
# update using cal_map_update
key = u32(256) # objects are converted using their to_hex() method
value = 512 # int are converted with 8 bytes
cal.cal_map_update(map_path, key, value)
# raw hex mode update
hex_key = ["00","01","00", "00"]
hex_value = ["00", "02", "00", "00","00", "00","00", "00"]
cal.bpftool_map_update(map_path, hex_key, hex_value, map_reference_type="pinned", value_type="hex")
except Exception as e:
print (e, map_path)
return -1
else:
return -1
Parsing packets in HIKe eBPF programs¶
cur->mhoff : mac header offset
cur->nhoff : nework header offset
cur->thoff : transport header offset
cur->dataoff : the offset to the position that you still have to parse
(usually the packet up to cur->dataoff has already been parsed)
note that cur->thoff is not really the transport layer, but it can changed when parsing the packet
it usually starts as the first header after the basic network header, a program that parses the headers after the basic header may decide to advance cur->thoff
Supported HIKe VM instructions¶
#### ALU instructions:
64-bit:
| Mnemonic | Pseudocode
|--------------|-------------------------
| add dst imm | dst += imm
| add dst src | dst += src
| sub dst imm | dst -= imm
| sub dst src | dst -= src
| mul dst imm | dst *= imm
| mul dst src | dst *= src
| div dst imm | dst /= imm
| div dst src | dst /= src
| or dst imm | dst \|= imm
| or dst src | dst \|= src
| and dst imm | dst &= imm
| and dst src | dst &= src
| lsh dst imm | dst <<= imm
| lsh dst src | dst <<= src
| rsh dst imm | dst >>= imm (logical)
| rsh dst src | dst >>= src (logical)
| neg dst | dst = ~dst
| mod dst imm | dst %= imm
| mod dst src | dst %= src
| xor dst imm | dst ^= imm
| xor dst src | dst ^= src
| mov dst imm | dst = imm
| mov dst src | dst = src
| arsh dst imm | dst >>= imm (arithmetic)
| arsh dst src | dst >>= src (arithmetic)
-----------------------------------------
#### Endianess conversion (Byteswap) instructions:
| Mnemonic | Pseudocode
|----------|-------------------
| le16 dst | dst = htole16(dst)
| le32 dst | dst = htole32(dst)
| le64 dst | dst = htole64(dst)
| be16 dst | dst = htobe16(dst)
| be32 dst | dst = htobe32(dst)
| be64 dst | dst = htobe64(dst)
-------------------------------
#### Memory instructions:
| Mnemonic | Pseudocode
|---------------------|-------------------------------------------
| ld64 dst imm | dst = imm
| ldx8 dst src off | dst = *(uint8_t *) (src + off)
| ldx16 dst src off | dst = *(uint16_t *) (src + off)
| ldx32 dst src off | dst = *(uint32_t *) (src + off)
| ldx64 dst src off | dst = *(uint64_t *) (src + off)
| st8 dst off imm | *(uint8_t *) (dst + off) = imm
| st16 dst off imm | *(uint16_t *) (dst + off) = imm
| st32 dst off imm | *(uint32_t *) (dst + off) = imm
| st64 dst off imm | *(uint64_t *) (dst + off) = imm
| stx8 dst src off | *(uint8_t *) (dst + off) = src
| stx16 dst src off | *(uint16_t *) (dst + off) = src
| stx32 dst src off | *(uint32_t *) (dst + off) = src
| stx64 dst src off | *(uint64_t *) (dst + off) = src
--------------------------------------------------------------------
#### Branch instructions:
64-bit:
| Mnemonic | Pseudocode
|------------------|-------------------------------------------
| ja off | PC += off
| jeq dst imm off | PC += off if dst == imm
| jeq dst src off | PC += off if dst == src
| jgt dst imm off | PC += off if dst > imm
| jgt dst src off | PC += off if dst > src
| jge dst imm off | PC += off if dst >= imm
| jge dst src off | PC += off if dst >= src
| jlt dst imm off | PC += off if dst < imm
| jlt dst src off | PC += off if dst < src
| jle dst imm off | PC += off if dst <= imm
| jle dst src off | PC += off if dst <= src
| jset dst imm off | PC += off if dst & imm
| jset dst src off | PC += off if dst & src
| jne dst imm off | PC += off if dst != imm
| jne dst src off | PC += off if dst != src
| jsgt dst imm off | PC += off if dst > imm (signed)
| jsgt dst src off | PC += off if dst > src (signed)
| jsge dst imm off | PC += off if dst >= imm (signed)
| jsge dst src off | PC += off if dst >= src (signed)
| jslt dst imm off | PC += off if dst < imm (signed)
| jslt dst src off | PC += off if dst < src (signed)
| jsle dst imm off | PC += off if dst <= imm (signed)
| jsle dst src off | PC += off if dst <= src (signed)
| call imm | f(r1, r2, ..., r5); Function call
| exit | return r0
---------------------------------------------------------------