|
libschc
|
libschc is a C implementation of the Static Context Header Compression, drafted by the IETF. It is a header compression technique, used in Low Power Wide Area Networks in order to enable tiny low-power microcontrollers to have an end-to-end IPv6 connection. This repository contains both the compression aswell as the fragmentation mechanism. For further information related to SCHC, see https://datatracker.ietf.org/doc/draft-ietf-lpwan-ipv6-static-context-hc/.
As this implementation is work in progress, there are some limitations you should keep in mind. The library has been designed in such a way, that it can be used on top of a constrained device, as well as on a more powerful server side device. As a consequence, memory allocation and memory intensive calculations are avoided. I tended to use fixed point arithmetic for 8-bit mircoprocessors, however some optimizations are possible.
The schc-config.h file contains a definition for dynamic memory allocation, used by fragmenter.
The current implementation is based on draft-ietf-lpwan-ipv6-static-context-hc-18 (https://datatracker.ietf.org/doc/draft-ietf-lpwan-ipv6-static-context-hc/18/), some naming conventions are therefore not in line with the current specification.
First copy the configuration file
and edit the definitions according to your platform.
As the rules tend to consume a large part of memory, and
Currently, I only have been working with rules for a single device. However, as the server application will have to keep track of multiple devices, this should be implemented in a decoupled way. The rules are implemented in a layered fashion and should be combined with a rule map to use different layers in a single ID. This map could then be reused for different devices.
In rules.h, several rules can be defined
Where the number of fields with Field Direction UP and DOWN are set and the total number of fields in the rule The rule is therefore constructed of different schc_field:
field holds a string of the field name (i.e. the human-readability of the rules).msb_length is used in combination with the Matching Operator MSB, but should be removed in coming releases.field_length indicates the length of bytes, but should be bits in coming releases. Field Position is only used for headers where multiple fields can exist for the same entry (e.g. CoAP uri-path).dir indicates the direction (UP, DOWN or BI) and will have an impact on how the rules behave while compressing/decompressing. Depending on the #define SERVER in schc_config.h, the source and destination in the decompress_ipv6_rule and generate_ip_header_fields will be swapped, to ensure a single rule for server and end device.target_value holds a char array in order to support larger values. The downside of this approach is the MAX_COAP_FIELD_LENGTH definition, which should be set to the largest defined Target Value in order to save as much memory as possible.MO is a pointer to the Matching Operator functions (defined in config.h)CDA contains the Compression/Decompression action (enum in config.h)Once all the rules are set up for a device, these can be saved and added to the device definition
This is all done very statically and needs further enhancements.
The rules.h file should contain enough information to try out different settings.
The compressor performs all actions to compress the given protocol headers. First, the compressesor should be initialized with the node it's source IP address (8 bit array):
In order to compress a CoAP/UDP/IP packet, the following function can be called. This requires a buffer (uint8_t *buf) to which the compressed packet may be returned.
The reverse can be done by calling:
This requires a buffer to which the decompressed headers can be returned (unsigned char *header), a pointer to the complete original data packet (unsigned char *data), the device id and total length and finally a pointer to an integer (uint8_t *header_offset), which will return the compressed header size (i.e. the position in the buffer where the actual data starts). The function will return the decompressed header length.
Once the decompressed packet has been constructed, the UDP length and checksum may be calculated (this is still an open issue, as these functions should be called from the construct_header function itself).
The result should be a complete decompressed packet.
The fragmenter and compressor are decoupled and require seperate initialization.
The initilization function takes the following arguments:
tx_conn, which can be an empty schc_fragmentation_t struct, to hold the information of the sending device.send requires a pointer to a callback function, which will transmit the fragment over any interface and requires a platform specific implementationend_rx is called once the complete packet has been received (more information bellow)remove_timer_entry had to be added for some platforms to remove timers once the complete transmission has been completedThe fragmenter is built around the mbuf principle, derived from the BSD OS, where every fragment is part of a linked list. The fragmenter holds a preallocated number of slots, defined in schc_config.h with #define SCHC_CONF_RX_CONNS. Every received packet is added to the MBUF_POOL, containing a linked list of fragments for a particular connection. Once a transmission has been ended, the fragmenter will then glue together the different fragments.
Upon reception of a fragment, the following function should be called:
tx_conn structure is used to check if the received frame was an acknowledgment and will return the tx_conn if sodevice_id is used to find out if the current device is involved in an ongoing transmission and will return the rx_conn if soThese return values can be used in the application to perform corresponding actions, e.g:
The above example is application code of the server, receiving a compressed, fragmented packet. By calling schc_reassemble, the fragmenter will take care of adding fragments to the MBUF_POOL.
Once the reception is finished, packet_to_ip6 is called, where the mbuf can be reassembled to a regular packet. First we want to get the length of the packet:
Next, a buffer can be allocated with the appropriate length (the return value of get_mbuf_len). The reassmbled packet can then be copied to the pointer passed to the following function:
The result will be a compressed packet, which can be decompressed by using the decompressor.
Don't forget to reset the connection.
After compressing a packet, the return value of schc_compress can be used to check whether a packet should be fragmented or not. In order to fragment a packet, the parameters of the connection should be set according to your preferences.
As you can see in the above examples, the library has no native support for timers and requires callback functions from the main application to schedule transmissions and to time out. Therefore, 2 function callbacks are required. The following is based on the OSS-7 platform.
As the server has to keep track of multiple devices and connections, a vector is used to keep track of multiple devices. The following is part of a C++ implementation, which makes use of the C library.
Licensed under the GNU General Public License, Version 3 (the "License"): You may not use these files except in compliance with the License. You may obtain a copy of the License at https://www.gnu.org/licenses/gpl-3.0.nl.html
See the License for the specific language governing permissions and limitations under the License.
© Copyright 2018-2019, Bart Moons bamoons.moons@ugent.be and Ghent University
1.8.17