From the idea to implementation
I would like to introduce you to my idea and how it came about giving “ROHC” a binding in NodeJS.
I wanted to implement a VPN that runs over Web-Socket. The advantages would be that the service would be hidden over HTTPS. With HTTP3 this would be even more optimized. So I started playing around with the TunTap2 Module for NodeJS, which I had to patch first.
Always fascinated by wireless technology, at some point I came across “LoRa” and with it a project “IP2Lora”.
In this project “IP2Lora”, the IP packets were shortened to save 40 bytes, which is very important for the transfer; with a radio band of 434 MHz or 868 MHz, not that much can be transferred.
In the graphic you can clearly see how the IP packet size decreases.
Unfortunately there was only one lib binding for Python.
So why not write a node lib binding yourself!?
The result can now be seen.
https://www.npmjs.com/package/node-rohc
You can find out more about how ROHC works in the links to the project or simply search for it. I won't explain it here so as not to make the post too long.
Installation Lib
I installed under Linux Debian/Mint. I think this should be similar to other Linux versions.
(By the way, I also had to patch the ROHC-lib to the new kernel.)
sudo apt-get install autotools-dev
sudo apt-get install automake
sudo apt-get install libtool
sudo apt-get install libpcap-dev
sudo apt-get install -y libcmocka-dev
git clone https://github.com/stefanwerfling/rohc.git
cd rohc
./autogen.sh --prefix=/usr
make all
sudo make install
Installation NPM
Now we can go into our project and install the module.
cd yourProject
npm i node-rohc
Now we have to create the NodeJS binding (this has to be compiled for each CPU architecture itself).
cd yourProject/node_modules/node-rohc
npm run build --loglevel verbose
The installation is now complete.
Coding/API usage
Now let's assume we get an IP packet that we want to compress into the following packets to save bytes.
const ipU8Packet = new Uint8Array(ipPacketBufferWithContent);
console.log(ipU8Packet);
Uint8Array(52) [
69, 0, 0, 52, 0, 0, 0, 0, 64, 6, 249,
112, 192, 168, 0, 1, 192, 168, 0, 2, 72, 101,
108, 108, 111, 44, 32, 116, 104, 105, 115, 32, 105,
115, 32, 116, 104, 101, 32, 100, 97, 116, 97, 32,
112, 97, 121, 108, 111, 97, 100, 33
]
The module is now imported and the Unit8Array in which the IP packet is given to the Rhoc object for compression.
import {Rohc} from 'node-rohc';
const r = new Rohc([
RohcProfiles.ROHC_PROFILE_UNCOMPRESSED,
RohcProfiles.ROHC_PROFILE_IP,
RohcProfiles.ROHC_PROFILE_TCP,
RohcProfiles.ROHC_PROFILE_UDP,
RohcProfiles.ROHC_PROFILE_ESP,
RohcProfiles.ROHC_PROFILE_RTP
]);
try {
const compress = r.compress(ipU8Packet);
console.log(compress);
} catch (e) {
console.error(e);
}
Uint8Array(53) [
253, 4, 69, 64, 6, 192, 168, 0, 1, 192, 168,
0, 2, 0, 64, 0, 0, 32, 0, 251, 103, 72,
101, 108, 108, 111, 44, 32, 116, 104, 105, 115, 32,
105, 115, 32, 116, 104, 101, 32, 100, 97, 116, 97,
32, 112, 97, 121, 108, 111, 97, 100, 33
]
In the constructor of the Rohc object we specify the profiles that should be used for compression in an array.
Then comes compression. In the output we see the new package. But why isn't it smaller?
The first packet still contains the information about port/IP-Address etc. Only the following packets become significantly smaller.
To convert the Rohc packet back into a normal IP packet we use decompress.
try {
const decompress = r.decompress(compress);
console.log(decompress);
} catch (e) {
console.error(e);
}
Uint8Array(52) [
69, 0, 0, 52, 0, 0, 0, 0, 64, 6, 249,
112, 192, 168, 0, 1, 192, 168, 0, 2, 72, 101,
108, 108, 111, 44, 32, 116, 104, 105, 115, 32, 105,
115, 32, 116, 104, 101, 32, 100, 97, 116, 97, 32,
112, 97, 121, 108, 111, 97, 100, 33
]
What is important is the beginning, the first packet is compressed and transmitted to the destination and the destination has decompressed the packet, the instance must be maintained. So that the connection ID remains known. This means that the program has to keep the object instance running. If one of the two pages (source with compression or destination with decompression) is stopped, both pages must be restarted.
Additional function with useful information:
Last compress/decompress status
import {Rohc, RohcStatus} from 'node-rohc';
if (r.getLastStatus() === RohcStatus.ROHC_OK) {
console.log('All OK');
}
During compression or decompression, the status is remembered; this can be queried again immediately afterwards to get more detailed information about what happened.
Last compress/decompress packet info
console.log(r.compressLastPacketInfo());
console.log(r.decompressLastPacketInfo());
{
version_major: 0,
version_minor: 0,
context_id: 0,
is_context_init: true,
context_mode: 1,
context_state: 1,
context_used: true,
profile_id: 4,
packet_type: 0,
total_last_uncomp_size: 52,
header_last_uncomp_size: 20,
total_last_comp_size: 53,
header_last_comp_size: 21
}
{
version_major: 0,
version_minor: 0,
context_mode: 2,
context_state: 3,
profile_id: 4,
nr_lost_packets: 0,
nr_misordered_packets: 0,
is_duplicated: false,
corrected_crc_failures: 11745388377929038000,
corrected_sn_wraparounds: 14987979559889062000,
corrected_wrong_sn_updates: 12105675798372346000,
packet_type: 449595,
total_last_comp_size: 18407961667527770000,
header_last_comp_size: 1940628627783807,
total_last_uncomp_size: 18407961667125117000,
header_last_uncomp_size: 217316637802623
}
Information about the last compression or decompression.
General compress/decompress info
console.log(r.compressGeneralInfo());
console.log(r.decompressGeneralInfo());
{
version_major: 0,
version_minor: 0,
contexts_nr: 1,
packets_nr: 1,
uncomp_bytes_nr: 52,
comp_bytes_nr: 53
}
{
version_major: 0,
version_minor: 0,
contexts_nr: 1,
packets_nr: 1,
comp_bytes_nr: 53,
uncomp_bytes_nr: 52,
corrected_crc_failures: 0,
corrected_sn_wraparounds: 8518447232180027000,
corrected_wrong_sn_updates: 4295000063
}
General information about compression and decompression.
Final word
I hope you enjoyed my little post. I am always open to improvements.
Top comments (0)