Design Considerations for Low Power Internet Protocols

06/28/2018 ∙ by Hudson Ayers, et al. ∙ Stanford University 0

The 6lowpan Internet Standard opens sensor networks up to Internet connectivity by specifying how to format IPv6 packets over low-power wireless links such as 802.15.4. Examining 6lowpan implementations in major embedded and sensor networking operating system, however, we observe that they do not interoperate. I.e., for any pair of implementations, one implementation sends 6lowpan packets which the other fails to process and receive. We explore why these different implementations do not interoperate and find it is due to some of the basic design goals of 6lowpan. Based on these findings, we propose four principles that can be used to structure protocols for low power devices that encourage interoperability between diverse implementations. These principles are based around the importance of balancing memory usage and radio efficiency, and the importance of not relying on Postel's law when dealing with low resource devices. We evaluate and demonstrate these principles by using them to suggest changes to 6lowpan that would make it easier for implementations to interoperate.

READ FULL TEXT VIEW PDF
POST COMMENT

Comments

There are no comments yet.

Authors

page 1

page 2

page 3

page 4

This week in AI

Get the week's most popular data science and artificial intelligence research sent straight to your inbox every Saturday.

1. Introduction

“The current exponential growth of the network seems to show that connectivity is its own reward, and is more valuable than any individual application”

— RFC 1958, Architectural Principles of the Internet (Carpenter, 1996)

Interoperability is critical for the Internet of Things. Not only do edge-devices need to interoperate with the broader Internet, they should also interoperate with other devices in the same network. Historically, though, embedded systems and sensor networks have been vertical silos of proprietary technologies, each using custom network protocols and homogeneous implementations. Networks typically require specialized gateways and cannot easily include devices from different vendors for a variety of applications.

The name itself – Internet of Things – explicitly states that these devices use the Internet protocol as a basis for networking. Using IP allows devices from different manufacturers, running completely different software stacks, to interoperate, share services, and compose into larger, more complex applications. This interoperability should exist not only between IoT devices communicating with hosts across the broader Internet, but also between IoT devices in the same low power wireless network. The presence of such interoperability precludes the need for multiple gateways to support different devices, simplifies network management, and allows for efficient, logical communication between nearby devices.

To address this problem, the IETF published a series of RFCs detailing a standard format for transmitting IPv6 packets over low-power wireless link layers such as IEEE 802.15.4 (Kushalnagar et al., 2007; Montenegro et al., 2007; Hui and Thubert, 2011). IPv6 is an attractive protocol for sensor networks for a variety of reasons (e.g. interoperability with the Internet, large address space, simple address allocation), but due its to relatively large minimum transmission unit (MTU) and header overhead it is challenging to transmit over low-power links with much smaller frame sizes. The 6LoWPAN RFCs define a fragmentation format as well as numerous options for compressing 40 byte IPv6 headers. These 6LoWPAN standards have been adopted by a number of popular embedded operating systems, including Contiki (Dunkels, 2018), RiotOS (Berlin, 2018), OpenThread (Nest, 2018), mbedOS (ARM, 2018), and TinyOS (Alliance, 2018).

Unfortunately, none of these implementations are complete. Each implementation supports different subsets of 6LoWPAN. As a result, devices built using different embedded operating systems cannot interoperate. In fact, we found that for every possible pairing, one implementation is likely to transmit 6LoWPAN packets which the other cannot to process (Section 2).

This paper explores the reasons behind the lack of interoperability in practice (Section 3), and argues that this results from the protocol too heavily prioritizing radio efficiency over processor resources combined with a requirement that every device implement the entire specification to interoperate (Section 4). We proposes four principles for designing interoperable protocols for low-power wireless networks (Section 5) and show how they could be applied to future 6LoWPAN standards (Section 6). These principles are informed by an empirical analysis of existing 6LoWPAN implementations as well as our own experience implementing a full 6LoWPAN stack for the Tock (Levy et al., 2017) operating system.

2. Interoperability Study

“The Working Group will generate the necessary documents to ensure interoperable implementations of 6LoWPAN networks”

— 6LoWPAN Working Group Charter (Lemon, 2005)

The IETF’s 6LoWPAN working group has been concerned with interoperability between implementation since inception (Lemon, 2005). Indeed, members of the working group have organized “Plugtests”, where vendors verified correct implementation of the 6LowPAN specifications and tested interoperability with other vendors (ets, 2013; Huang, 2013). While detailed results of these Plugtests are not publicly available, summaries indicate that 6LoWPAN interoperability at the time was quite incomplete (Huang, 2013).

A node sending IPv6 packets using 6LoWPAN may fragment the packet or compress headers in a large number of ways permitted by the specification. These choices depend both on the properties of the packet (e.g. whether it is a UDP packet or if the origin and destinations are in the same subnet) as well as on which compression and fragmentation options the sender chooses to use. For two nodes to interoperate, the 6LoWPAN implementation on each node must be able to receive any packet the other node might send.

We investigate the interoperability of 6LoWPAN implementations from five common embedded software platforms: Riot OS, Contiki, OpenThread, TinyOS, and ARM Mbed. Our study is concerned, specifically, with each implementation’s ability to receive and decode 6LoWPAN packets sent from other implementations. We do not explore whether devices built using these implementations could form a network in the first place, since 6LoWPAN leaves much of the network formation process (e.g. discovery and joining) unspecified.

We evaluated the compatibility between five common embedded software frameworks with 6LoWPAN implementations in three ways. First, we determined how completely each implementation implements the 6LoWPAN specification by directly examining their source code (2.1). Second, we show that, under some circumstances, each implementation sends packets using compression or fragmentation options which another implementation cannot decode (2.3). Finally, we found that even in cases where two implementations use compatible compression and fragmentation options, different implementation choices, such as header decompression bounds, limit their interoperability (2.2).

As a result, we find that no pairing of these five implementations is fully interoperable.

2.1. Incomplete Implementations

6LoWPAN consists of a large number of mechanisms for compressing different kinds of IPv6 packets. For example, a sender may elide 14 bytes of the source or destination address if carries the link-local prefix. While senders are not required to use all possible compression mechanisms available to them, receivers need to be able to decode packets that use any number of compression mechanisms. By analyzing the code of five 6LoWPAN implementations, we found that no implementation is able to decode every possible 6LoWPAN packet. Moreover, implementations differ in which portions of the specification they choose to support. Table 1 summarizes our findings.

RFC 4944 describes an encapsulation header stack of variable length which will precede any IPv6 packet sent using the 6LoWPAN adaptation layer. The specification requires that any 6LoWPAN specification be able to parse the following LoWPAN header types:

  • Mesh Addressing Header

  • Broadcast Header

  • Fragmentation Header

  • Compression Headers

In addition, the specification describes the process by which 6LoWPAN devices may obtain an IPv6 interface identifier via stateless address auto configuration, and describes separate procedures for mapping unicast and multicast IPv6 addresses to IEEE 802.15.4 link layer addresses. RFC 4944 also describes how a number of specific edge cases related to the fragmentation and compression of IPv6 packets should be handled, such as the requirement that any header fields which do no fit within the first fragment of a 6LoWPAN message should be sent uncompressed in the payload of subsequent fragments.

RFC 4944 was later updated by RFC 6282 (Hui and Thubert, 2011), which defines an updated header compression format. This updated format allows for context-based and context-free compression of IPv6 addresses, and defines a number of means by which other fields within IP and UDP headers may be compressed in certain cases. RFC 6282 is split into two main sections; the compression of fields within the IPv6 header, and the compression of subsequent headers.

For the IPv6 header itself, several fields within the IPv6 header contain either common or fixed values, and RFC 6282 allows for these fields to also be compressed. The Traffic Class and Flow Label fields can all be elided, while the Hop Limit field can be compressed if it is one of several common values. Additionally, the next header field can be compressed if the subsequent header is also compressed using 6LoWPAN (see below). Finally, RFC 6282 extends the address compression options, allowing for both context-free and context-dependent compression for unicast or multicast IPv6 addresses. The compression scheme allows for more general, globally addressable addresses to be compressed using 6LoWPAN (instead of only local addresses as in RFC 4944).

The other major aspect to RFC 6282 is its extension of Next Header Compression for encapsulated IPv6 and UDP headers. First, 6282 allows for the compression of the IPv6 Hop-by-Hop Options Header, Routing Header, Fragment Header, Destination Options Header, and Mobility Header. Additionally, this RFC defines a compression mechanism for IPv6-in-IPv6 headers. Finally, the RFC extends the compression mechanism for the UDP header, allowing for the UDP checksum to be elided, in addition to the UDP next header compression, length elision, and stateless UDP port compression already described in RFC 4944.

Finally, RFC 6775 (Shelby et al., 2012) describes 6LoWPAN optimized IPv6 Neighbor Discovery. It introduces several optimizations to IPv6 ND to make it better suited for LoWPANs, in which hosts are less capable and links are less stable than IPv6 ND assumes.  111Several other 6LoWPAN RFCs also exist, including RFC 7400 (Generic Header Compression for 6LoWPAN), RFC 8025 (6LoWPAN paging dispatch), RFC 8066 (ESC Dispatch Code Points), and RFC 8138 (6LoWPAN Routing Header). We have not observed substantial use of these of the techniques described in these RFCs in the 6LoWPAN implementations we analyzed. Accordingly, we consider any further discussion of these RFCs as beyond the scope of this paper.

As this summary makes apparent, the 6LoWPAN protocol consists of a large number of complex and mostly independent features which use the link-layer frame efficiently via compression and fragmentation optimizations. We examined the code and documentation for each of the aforementioned 6LoWPAN stacks, and quickly discovered that the stacks do not implement the specification in a uniform manner. In fact, each specification implements a different subset of the requirements in the 6LoWPAN specification, and none implements the entire specification to the letter. A visualization of the mismatched feature support we discovered across different 6LoWPAN implementations can be found in Table 1.

Feature Stack
Contiki OpenThread Riot Arm Mbed TinyOS
Uncompressed IPv6
6LoWPAN Fragmentation
1280 byte packets
Dispatch_IPHC header prefix
IPv6 Stateless Address Compression
Stateless multicast address compression
802.15.4 16 bit short address support
IPv6 Address Autoconfiguration
IPv6 Stateful (Context Based) Address Compression
Stateful multicast address compression
IPv6 Traffic Class and Flow label compression
IPv6 NH Compression: Ipv6 (tunneled IPv6)
IPv6 NH Compression: UDP
UDP port compression
UDP checksum elision
Compression + headers past first first fragment
Compression of IPv6 Extension Headers ~222OpenThread does not support compression of the mobility header
Mesh Header ~333TinyOS can receive packets which use the mesh header, but will never forward such packets or send packets using this header, effectively preventing TinyOS devices from participating in route-under networks
Broadcast Header
Regular IPv6 ND ~444TinyOS supports only some of RFC 4861
RFC 6775 6LoWPAN ND
RFC 7400 Generic Header Compression Support
~ = Partial Support
Table 1. 6LoWPAN Interoperability Matrix

2.2. Unrealistic Bounds

Beyond the variation in what portions of the 6LoWPAN specification each stack implements, we also discovered significant variation in how each stack handles certain implementation-specific details. Some of these details have little impact on interoperability, such as decisions regarding how many fragments a stack holds for a given packet before dropping all of them, whether to allow for reconstruction of multiple packets simultaneously, and how long to hold onto fragments for which the rest of the packet has not yet arrived. Other details, however, differ in ways that significantly affect interoperability between stacks. A discussion of two such details follows:

2.2.1. Maximum Header Decompression

Each of the stacks analyzed imposes some limit on the maximum amount of header decompression possible for a received packet. Such a limit is necessary to ensure that packet and fragment buffers within a stack are large enough for received packets. The maximum amount of header decompression allowed by the 6LoWPAN specification is about 1200 bytes, basically, if an entire MSS IPv6 packet was sent containing only compressed headers. Some of the stacks analyzed decompress fragments directly into the MSS buffer which will eventually contain the entire IPv6 packet, and thus support this bound. Other stacks impose significantly lower limits - limits low enough that packets could easily be constructed within the 6LoWPAN specification that would exceed these limits. For example, Contiki’s limit of 38 bytes of header decompression is exceeded by any packet for which the IP header is maximally compressed (38 bytes) and the UDP header is compressed at all. Accordingly, we observed that certain stacks would send packets with a significant amount of header compression, but that other stacks would silently drop these packets due to lacking buffer space for fragments requiring that much decompression. Furthermore, these stacks do not given any indication back to the sender that a packet has been dropped for this reason, making it difficult for the sending stack to identify how to adjust its transmission to successfully deliver data,

2.2.2. Arbitrary Next Header Compression

Several of the 6LoWPAN stacks also impose limits on the arbitrary compression/decompression of IPv6 extension headers and next headers required by the specification. The headers which must be handled are as follows:

  • IPv6 Hop-By-Hop Options Header

  • IPv6 Routing Header

  • IPv6 Fragment Header

  • IPv6 Destination Options Header

  • IPv6 Mobility Header

  • IPv6 Next Header

  • UDP Next Header

Further, 6LoWPAN implementations are expected to be able to decompress at least one of each of these headers, and up to two Destination Options headers, in almost any order. Handling all of these possible cases can result in complex state machines, convoluted code, and increase in code size and RAM use. Therefore, several of the stacks examined impose a limit on this arbitrary next header decompression - namely, Contiki and Riot. Both of these stacks only check for the UDP Next Header. This greatly simplifies the code required for decompression of next headers in these stacks as compared to the others, which require recursion to handle this arbitrary compression. The offshoot of this simplified code, however, is that these stacks will drop packets with certain compressed extension header configurations when other stacks send such messages.

2.3. No Pairing Interoperates

Up to this point, all we have proven is that each of the 6LoWPAN stacks we have analyzed is incapable of receiving certain packets which should be valid 6LoWPAN packets according to the specification. In order to make our arguments regarding the lack of interoperability between stacks convincing, we now demonstrate that existing 6LoWPAN stacks can actually generate the packet formats that we show other stacks cannot receive. This proves that missing receive functionality is not simply a case of limited 6LoWPAN stacks abstaining from handling packets which no existing stacks ever generate anyway.

What follows is a listing of each of the 10 possible combinations of 6LoWPAN stacks, accompanied by a single example packet which can be generated by one of the stacks in the pairing which the other stack would not receive.

  • Contiki, OpenThread : Contiki generated message using uncompressed IPv6

  • Contiki, Riot: Riot generated message using stateful multicast address compression

  • Contiki, Mbed: Mbed generated message using compresesd, tunneled IPv6

  • Contiki, TinyOS: TinyOS generated message containing compressed IPv6 extension headers

  • OpenThread, Riot: OpenThread generated message containing any of the IPv6 extension headers, which the OpenThread stack automatically compresses

  • OpenThread, Mbed: Mbed generated IPv6 packet containing the IPv6 mobility header

  • OpenThread, TinyOS: OpenThread generated message for which the destination address is compressed using stateful multicast compression

  • Riot, Mbed: Mbed generated IPv6 message containing any compressed next header other than the UDP header

  • Riot, TinyOS: Riot generated message for which the destination address is compressed using stateful multicast compression

  • Mbed, TinyOS: Mbed generated Neighbor Discovery message using the 6LoWPAN context option as specified in RFC 6775.

This is a non-exhaustive listing, and for most of these pairings several message formats exist which could be generated by one that would be dropped by the other. We verified via code analysis that packets could be easily generated for each instance in which we make that claim.

2.4. Hardware Generation of Select Packets

In addition to this code analysis, we wanted to present further evidence that several of these packets formats could easily be generated via typical use of these 6LoWPAN stacks.

To do this, we slightly modified basic example networking apps on each stack, such that we could use the existing 6LoWPAN interface to send certain packets. We then flashed these modified examples onto embedded hardware platforms supported by each. We captured the transmitted packets using a wireless packet sniffer, and analyzed the sniffed packets using Wireshark. The software/hardware combinations used can be seen in Table 2. A summary of these tests follows:

Stack Commit Hash Device
Contiki bc2e445817aa546c 0bb93a9900093ec2 76005e2a CC2650 LaunchXL
OpenThread 4e92a737201b2001 4662a3672dbf314b f6b0a99b Nordic NRF52840 PDK
Riot 3cce9e7bd292d264 086969b4876921c4 6d541b01 SAM R21 Xplained Pro
Arm Mbed 4e92a737201b2001 4662a3672dbf314b f6b0a99b N/A
TinyOS 4d347c10e9006a92 1254eb5de072ebf2 2603d1c1 Atmel SAM4L
Table 2. Hardware/Software Combinations

2.4.1. Contiki

We used the Contiki stack to generate an uncompressed IPv6 packet, which we know OpenThread’s stack would be unable to receive. To do this, we modified the cc26xx-web-demo example application configured to run in net-uart mode. This modification involved simply making calls to uip_udp_packet_sendto() prior to associating with a border router, rather than waiting on association with a border router to begin sending packets. Additionally, Contiki was compiled with the SICSLOWPAN_CONF_COMPRESSION flag set to SICSLOWPAN_COMPRESSION_IPV6. This use of the built in 6LoWPAN networking interface was sufficient to generate an uncompressed IPv6 packet which contained a UDP datagram.

2.4.2. Riot

We used the Riot OS 6LoWPAN stack to generate packets which contained stateful compression of a multicast destination address, which we know Contiki and TinyOS could not receive. To achieve this, we simply modified the gnrc_networking example application provided by Riot. This modification involved simply adding an address to the context store using the existing gnrc_sixlowpan_ctx_update() function. We then sent an IPv6 message via the CLI interface provided with the application for which the destination IPv6 address was a Unicast-Prefix-based IPv6 Multicast Address, with the unicast prefix matching the prefix we added to the context store.

2.4.3. Contiki/OpenThread Communication

Finally, we wanted to demonstrate an actual example of sending a valid 6LoWPAN packet using one stack and watching it be dropped by another. This process required repeating the example packet generation described above for Contiki. It also required modifying code from the example 6LoWPAN CLI app on OpenThread so that it would print where in the receive stack packets were being dropped and why. Going through this process for every possible pairing of stacks would be arduous, and add little value above what has already been provided in this section. Instead, we only provide this example using Contiki and OpenThread. These stacks were selected because the Contiki 6LoWPAN stack is widely referenced in academic discussions of 6LoWPAN, and because OpenThread represents a 6LoWPAN implementation published by a leading industry player.

A UDP datagram encapsulated in an IPv6 packet was generated using the Contiki stack such that the packet was sent as uncompressed IPv6 - with a 6LoWPAN header consisting of only the byte 0x41 as required by RFC 6282. This message was received at the link layer by the OpenThread stack, but silently dropped by the 6LoWPAN layer, as a result of Riot not handling the reception of uncompressed IPv6. To confirm that this was not a fluke, we sent the same 6LoWPAN packet compressed using the IPHC header compression format, and verified that this packet passed all the way through the 6LoWPAN receive layer and was reconstructed as a full IPv6 packet.

3. Implementation Concerns

”Every bit transmitted brings a sensor node one moment closer to death”

—Greg Pottie (Pottie, 2003)

The 6LoWPAN specification was created with a clear goal—to allow for IPv6 connectivity over a link-layer with an order of magnitude smaller frame sizes than Ethernet. Unfortunately, fragmented IPv6 on its own requires header overhead much greater than typical wireless protocols designed for low power devices. As a result, the specification places an extreme focus on minimizing protocol overhead and, thus, radio utilization.

The primary problem with 6LoWPAN is that this focus was taken too far. This focus has resulted in complex implementations that require significant processor resources. In order for devices to interoperate, they must be able to parse any valid received 6LoWPAN packet that might be sent by others.

In practice, as section 2 shows, many 6LoWPAN implementation do not implement the entire specification and, therefore, are not interoperable. We argue that this is not a result of poor software design, but rather intentional choices to implement different subsets of the specification that favor limited RAM and code size, security concerns, and minimizing engineering effort.

In fact, in some cases even these incomplete 6LoWPAN implementations systems are too resource intensive for some devices. As a result, several implementations allow the developer to remove portions of the 6LoWPAN stack during compilation. Even when implementations use overlapping portions of the specification, additional interoperability conflicts arise from different choices of memory bounds for decompression.

3.1. 6LoWPAN on Tock

To obtain a better lens into the problems with 6LoWPAN, we implemented a functional 6LoWPAN stack of our own. Our 6LoWPAN stack is written in Rust, for the Tock Operating System - a new operating system for low-power devices that provides a safe multiprogramming environment for microcontrollers. Our 6LoWPAN stack can be found at https://github.com/helena-project/tock. In the process of implementing our stack, we encountered several aspects of the 6LoWPAN specification that complicated our implementation. These follow:

IPv6 Encapsulation

RFC 6282 allows for the compression of an arbitrary number of tunneled IPv6 packets. Any use of tunneled IPv6 in which the initial IPv6 packet is compressed requires that the IPv6 header of all tunelled packets be compressed as well. This requires recursive function calls to the entire decompression library, which adds substantial overhead and complexity to the code and makes it much more difficult to track buffer offsets during decompression.

UDP Compression

While UDP header compression is not overly complicated, it undermines several key assumptions about the behavior of UDP. Principally, RFC 6282 allows for UDP headers to elide the checksum, which is a violation of an important part of the end-to-end principle. Accordingly, we were wary of implementing such functionality within our OS, especially given the emphasis which Tock puts on safe interaction between concurrent, mutually distrustful applications.

In addition to this, RFC 6282 specifies ”The decompressor MUST unambiguously determine that an additional integrity check was put in place by the compressor and verify the integrity check”, but provides little detail about how strong such a check should be, and little advice as to how such a verification should be implemented. Such a verification mechanism breaks layering, and is not always intuitive to implement in a network stack — especially one designed such that different implementations can be substituted at each layer. The networking stack on Tock is designed such that the UDP layer and IP layer code can be reused regardless of what lower layers they sit on top of. Requiring that the IP layer pass information to the layer beneath it regarding whether a MIC is being used breaks this clean layering and complicates the creation of a link-layer agnostic IP layer. It is noteworthy that for concurrent embedded applications, it is crucial that the network stack be designed such that different stacks (for instance, one communicating using 802.15.4 and the other using Bluetooth Low Energy) be able to share code for any duplicated layers - not allowing this is extremely expensive from the perspective of code size.

Fragmentation

6LoWPAN fragmentation is described in RFC 4944, and is important in allowing for IPv6 packets to be sent over links with a smaller MTU. For our implementation, we did not have a dynamic memory allocator and instead used a pool of buffers for reassembling 6LoWPAN fragments. However, since fragments could arrive in any order, the IPv6 header may not arrive until after several frames have already been received. Thus, it is impossible to determine which address or port an arbitrary frame was sent to, and consequently it is difficult to enforce resource usage policies at the network level. This allows for particular applications or services to consume disproportionate amounts of the memory pool for receiving packets.

Arbitrary Next Header Support/Ordering

RFC 6282 describes compression methods for several IPv6 extension next headers, and allows for these compressed next headers to appear in an arbitrary order. This is in addition to compression of the IPv6 next header and the UDP next header. Handling this case required complicated, recursive, control flow, and overhead to track buffer offsets of multiple decompressions.

Headers Spanning Multiple Frames

6LoWPAN allows packet headers to span multiple link-layer frames. In this case, headers in the first frame are to be transmitted compressed, while headers in subsequent frames must be uncompressed. This increased the complexity of our implementation, as the size of each IEEE 802.15.4 frame also has additional headers, and so the actual payload may vary between frames. Thus, we must first compress each header, then perform a run-time check to make sure that these headers still fit within the first frame.

No Limits on Header Decompression

The logical way to design a 6LoWPAN stack would be with a layered design, in which compression and fragmentation are largely separate. This means that received fragments would be received, decompressed, and then reassembled into a full IPv6 packet. Unfortunately, since the maximum amount of header decompression is not known before decompressing, preserving this layering would require allocating fragment buffers large enough to fit the maximum possible packet. In a system without dynamic allocation, the memory costs of such a design are too high. As a result, we were forced to break this clean layering of fragmentation and compression in favor of a more complex implementation.

3.2. Processor Resources

Stack Code Size Measurements (Bytes)
Full IP stack 6LoWPAN-All Compression Fragmentation Mesh/Broadcast Headers
Contiki 37538 11262 5952 3319 N/A
OpenThread 42262 26375 4146-20000 1310 4500
Riot 30942 7500 ¿4712 1514 N/A
Arm Mbed 46030 22061 17900 3104 1331
TinyOS 37312 16174 – – – – 600
* TinyOS inlines compression code into fragmentation code, and does not completely implement the mesh header
Table 3. 6LoWPAN Stack Code Size

Evidence that developers of these 6LoWPAN stacks were concerned about 6LoWPAN’s consumption of processor resources is baked into the design of each. One of the primary indicators that each implementation was concerned with code size is the prevalence of options to compile limited subsets of the 6LoWPAN stack. For example, Contiki defines the SICSLOWPAN_CONF_COMPRESSION compilation flag, which can be set to force all Contiki packets (sent and received!) to be processed as uncompressed IPv6. Riot presents extensive compilation options for 6LoWPAN, allowing for the exclusion of all IPHC compression, the exclusion of context based compression alone, the exclusion of fragmentation, the exclusion of ND, and the exclusion of next header compression. The Mbed stack allows users to exclude elements of the IPv6 stack such as security features, routing specific features, link-layer features, and more. Further, Mbed defines macros which can be used to save RAM at the expense of flash, or vice-versa. TinyOS by default removes all code in a stack that is not being used by an application, and we observed this at work by compiling different 6LoWPAN application binaries.

Table 3 shows the code size overhead of each of the five implementations broken into independent overheads for compression, fragmentation, mesh and broadcast headers, as well as totals for 6LoWPAN and the entire networking stack including physical layer drivers, IPv6, UDP, ICMP, etc. The commit hash of each stack that was examined is contained in Table 2.

We estimate these values using two methods. We used existing options provided by the Contiki and Riot build systems to remove particular 6LoWPAN functionality at compile-time. For OpenThread, TinyOS, and Mbed, which do not have such options, we manually analyzed binary build products using the

readelf utility from the GCC toolchain to isolate functions associated with each aspect of 6LoWPAN.

These results likely overestimate the overhead of fragmentation and underestimate the overhead of certain kinds of compression since some of the complexity of compression is born on the fragmentation logic. Moreover, for OpenThread and Arm Mbed, where we had to examine binaries manually, we expect the results underestimate the overhead of all 6LoWPAN components since we only counted procedures which unambiguously implemented particular functionality, though some of the complexity is implemented in other portions of the stack. In summary:

  • 6LoWPAN stack devolopers were concerned with processor resource requirements of the protocol.

  • Fragmentation, the only portion of 6LoWPAN that’s strictly necessary for sending IPv6 packets, consumes significantly less ROM than compression.

  • Implementations with more complete adherence to compression specification consume more code for compression

  • Mesh and broadcast headers are relatively expensive given that few real-world applications use them

4. Contributing Factors

“6LoWPAN per se never has been, nor ever will be an interoperable standard…”

—Robert Cragie, 6Lo WG Mailing List (Cragie, [n. d.])

Here, we discuss fundamental factors that contributed to 6LoWPAN’s interoperability problems.

4.1. Code Size vs. Compression

Historically, the tension between RAM and code size has been a delicate dance when designing on low power embedded platforms. It is documented that designs focused on the RAM/code size split of a particular architecture can be problematic. For example, TinyOS service APIs had to change dramatically over time to accomodate devices with different ROM/RAM splits than the mica platform for which it was originally developed (Levis, 2012).

When writing low power networking specifications, another important “slider” exists—the tradeoff between code size and protocol efficiency. Techniques such as advanced MAC and physical layers, and tracking the state of a network can reduce packet sizes and, thus, radio energy consumption. However, these techniques typically require larger and more complex implementations. The irony of this situation is that if too much of an emphasis is put on saving energy through techniques that require substantial code space or RAM, it can force a requirement for more expensive, power hungry microcontrollers to run this complex code. Given that the designers of a specification can never know all of the applications for which that specification will ultimately be used, or all of the platforms for which it will be implemented, it is important that a specification not require a particular balance between protocol efficiency and code size reduction.

4.2. Complexity and Interoperability

Moving beyond the constraints of code size, added complexity harms interoperability purely because the implementers of a protocol may view aspects of the specification as not worth the time and effort it would take to implement. This is particularly true given that the current ”Vertical Silos” model of IoT deployments means that original implementations do not require interoperability to work - so long as an implementation always operates with itself, most early use cases of that implementation will perform as expected. This means that if a given packet format is not used in whatever application a given implementation is being created for, many implementers might make the conscious decision to leave out the code required to handle such packets. When interoperability is later expected of this stack, or when interoperability testing begins, problems arise.

Complex implementations are already undesirable in the IoT space, where deployments can last years without updates, and where users are unlikely to intervene when something goes wrong. The costs of complexity in IoT should not be underestimated.

4.3. Low Resource Devices and Postel’s Law

Most of the interoperability issues within the 6LoWPAN specification appear to arise from the fact that the writers of the specification assume that implementations of the specification will be capable of receiving any datagrams that can be constructed in a valid manner according to the specification. At first glance, this seems like a not-unreasonable expectation—devices that fail to follow the letter of a specification should not expect to interoperate! In fact, the original Internet RFC, RFC 760, contains a robustness principle now known as Postel’s Law: “an implementation should be conservative in its sending behavior, and liberal in its receiving behavior” (Postel, 1980). Postel’s law is a design principle which is considered central to the success of interoperability within the Internet, and most Internet-related protocols are written with the assumption that implementations will attempt to adhere to this principle.

The most common interpretation of this law is that implementations should create messages that adhere strictly to a specification, but should accept messages which may be incorrect as long as they are unambiguous. This interpretation implies an assumption that all implementations of a specification will by default accept any message created which adheres strictly to the specification - a necessary prerequisite for also accepting messages which may deviate from it.

Unfortunately, this assumption can not be so easily presumed when writing specifications for low resource devices of varying capabilites. Simply put, supporting an entire specification, much less following Postel’s law, can be prohibitively expensive for certain low resource devices. This is a fundamental problem for this domain that does not apply to more typical internet hosts. Different embedded devices and applications are often constrained in different ways—some devices are primarily limited by code size, others by RAM, others by power, cost, etc. The manifestation of this reality is that it is difficult to write protocols which will not run into certain resource constraints for at least some low resource devices, and that as a result different implementations of the same protocol may take very different shortcuts. Further, while Postel’s law has helped to prevent ambiguities in typical Internet specifications from preventing interoperability, these same ambiguities in specifications for low power devices are much less likely to be ironed out by implementations that are ”liberal in what they receive”.

Accordingly, designers of specifications for low power devices should prepare for the reality that developers will skimp some in their implementations, making design decisions that come at a potential cost to interoperability.

5. Four Design Principles

This section describes four protocol design principles which, if followed, lead to low-power protocols that are more likely to have interoperable implementations. In the next section, these are further explained by showing how each can be applied to 6LoWPAN.

5.1. Principle 1: Capability Spectrum

A low power protocol should be implementable on devices which are at the low end of code and RAM resources. Rather than require every device pay the potential energy costs of fewer optimizations, a protocol should support a spectrum of device capabilities. This spectrum defines a clear ordering via which especially resource constrained devices can reduce code size or RAM use by eliding features. Such a spectrum makes a protocol usable by extremely low resource devices without forcing more resourceful devices to communicate inefficiently.

This capability spectrum should be a linear scale. For a device to support capability level , it must also support all lower capability levels. More complex configuration approaches (e.g., a set of independent options) would allow for a particular application or implementation to be more efficient, picking the features that give the most benefit at the least complexity cost. However, this sort of optimization then makes interoperability more difficult, as two devices must negotiate which features to use.

5.2. Principle 2: Capability Negotiation

The second principle immediately follows from the first: if two implementations may have different capability levels, there should be an explicit mechanism by which two devices can efficiently negotiate what level to use when they communicate

If two devices wish to communicate, they default to the lower of their supported capability levels. For example, suppose a TinyOS device supports level 2 and a Contiki device supports level 4; Contiki must operate at level 2 when communicating with the TinyOS device. This requires keeping only a few bits of state for any device to communicate with. Also, note that this state is per-hop; for a layer 3 protocol like IP, it is stored for link-layer neighbors (not IP endpoints) and so does not require knowledge of the whole network.

5.3. Principle 3: Provide Reasonable Bounds

Specifications should specify reasonable bounds on recursive or variable features so implementations can bound RAM use. These bounds have two benefits. First, it allows implementations to safely limit their RAM use without silent interoperability failures. E.g., today, if an mbed device sends a 6lowpan packet whose compression is greater than 38 bytes to a Contiki device, Contiki will silently drop the packet. Second, it ensures that capability negotiation is sufficient to interoperate.

The original designers of a specification may not know exactly what these values should be. This is not a new problem: TCP congestion control, for example, had to specify initial congestion window values. The bounds should initially be very conservative. Over time, if increasing resources or knowledge suggests they should grow, then future devices will have the onus of using fewer resources to interoperate with earlier ones.

5.4. Principle 4: Don’t Break Layering

Designers should ensure that interoperability is a central priority for specifications throughout the design process, and that interoperability is not simply assumed from the fact that devices will be communicating via a shared protocol. In particular, specifications should be careful that considerations introduced to save energy in certain scenarios should not make assumptions about the rest of the stack. Layering is a foundational network design principle. As the difficulty NATs introduced to Internet connectivity in the early 2000s demonstrated, breaking layering can introduce unforseen and extremely difficult to fix interoperability problems.

The appeal of cross-layer optimization in embedded systems is even stronger than in traditional computers. Designed for a specific application, a developer can understand and know exactly how the entire system works, from hardware to application code. However, while this whole-system knowledge makes sense for a particular device or iteration of an application, long-lived systems will evolve and change. This is especially true if the device will need to interoperate with new gateways or application devices. Furthermore, as embedded systems have grown more complex, their software has begun to resemble more traditional systems. Rather than write software from scratch every time, systems use and draw on existing operating systems as well as libraries. By breaking layering, cross-layer optimizations require that developers own and customize the entire software stack.

6. Application to 6LoWPAN

“A good analogy for the development of the Internet is that of constantly renewing the individual streets and buildings of a city, rather than razing the city and rebuilding it.”

— RFC 1958, Architectural Principles of the Internet (Carpenter, 1996)

We apply the principles in the previous section to 6LoWPAN via specific modifications to the protocol which would improve interoperability.

6.1. Principle 1: Capability Spectrum

We propose replacing the large collection of “MUST” requirements — those “features” in Table 1—into 7 levels of functionality. These levels prioritize features that provide the best packet size savings given the resulting implementation complexity. For example, the greatest savings results from compressing 128-bit IPv6 addresses.

  1. Uncompressed IPv6

    • Uncompressed IPv6

    • 6LoWPAN Fragmentation and the Fragment Header

    • 1280 Byte Packets

  2. IPv6 Compression Basics + Stateless Address Compression

    • Support for the Dispatch_IPHC Header Prefix

    • Correctly handle elision of IPv6 length and version

    • Stateless compression of unicast addresses

    • Stateless compression of multicast addresses

    • Compression even when 16 bit addresses are used at the link layer

    • IPv6 address autoconfiguration

  3. Stateful IPv6 Address Compression

    • Stateful compression of unicast addresses

    • Stateful compression of multicast addresses

  4. IPv6 Traffic Class and Flow Label Compression

    • Traffic Class compression

    • Flow Label Compression

    • Hop Limit Compression

  5. IPv6 and UDP NH Compression + UDP Port Compression

    • Handle Tunneled IPv6 correctly

    • Handle the compression of the UDP Next Header

    • Correctly handle elision of the UDP length field

    • Correctly handle the compression of UDP ports

    • Correctly handle messages for which headers go on longer than the first fragment, and the headers in the first fragment are compressed.

  6. Entire Specification

    • Support the broadcast header and the mesh header as described in RFC 4944

    • Support compression of all IPv6 Extension headers

The classes in this scale do not precisely reflect the current feature support of the implementations described in Section 2. For example, Contiki supports UDP port compression (level 5) but does not support 802.15.4 short addresses (level 2) or tunneled IPv6 (level 5): following this formulation, Contiki only provides level 1 support. If Contiki supported 16-bit addresses, it would provide level 4 support.

The specific spectrum that we present is based off of our measurements of code size, the saved bits that each additional level of compression allows for, and our observations of existing 6LoWPAN implementations. The 6 classes we chose attempt to allow for a range of devices to optimize their use of the specification to the resources which are available. Class 0 represents the minimum functionality required for IPv6 over 802.15.4 links. Class 1 adds the functionality which allows for the largest reduction in transmitted header size - via stateless address compression - at what we suspect is a relatively small fraction of the total code size required for all the compression that RFC 6282 currently defines. The subsequent classes are chosen such that we suspect each subsequent class has a worse ratio of radio energy reduction vs. code size added. We also attempt to place the features which we observed as less implemented further down the scale.

If this scale existed as part of the initial specification, we believe that implementations would have made an effort to adhere to it. This is especially true because the scale provides guidance on the order with which to add features. This scale allows for implementations to choose their own balance between header size and code size/RAM requirements without this coming at a cost to interoperability. An additional advantage of this setup is that it allows for some future modifications to the 6LoWPAN specification without breaking interoperability between new and old implementations. For example, our scale does not include support for RFC 7400—Generic Header Compression for 6LoWPAN—because none of the open-source stacks we analyzed implement it (Bormann, 2014). Despite this, support for this RFC could easily be added as a new class on this linear scale (as Class 6), and all lower class implementations would know they were unable to interoperate with this new, higher order interoperability class.

6.2. Principle 2: Capability Negotiation

6lowpan could implement capability negotiation using two mechanisms: neighbor discovery (ND) and ICMP. Neighbor discovery allows devices to probe and determine capability levels, while ICMP allows devices to determine when incompatible features are used, or when ND is not available.

Neighbor discovery: 6LoWPAN ND should add an option that allows a device to communicate its capability class during association with a network. The inclusion of a few extra bits in ND messages would allow all devices that learn neighbor addresses via ND to also know how to send packets which that neighbor can receive. This option minimizes the energy cost of communicating capabilities. It is worth noting that RFC 7400 already employs a similar method for communicating whether devices implement General Header Compression: adding such an option is clearly viable.

ICMP: All IPv6 devices are already required to support ICMP. We propose adding a new ICMPv6 message type—6LoWPAN Class Unsupported—which could be sent in response to messages received encoded using a 6LoWPAN class higher than the class of the receiving host. This would allow for communication of capabilities even in networks not constructed using IPv6 ND. This ICMPv6 message would allow hosts to indicate exactly what class the receiving host does support, preventing any need for repeated retransmissions using different compression or fragmentation formats.

6.3. Principle 3: Provide Reasonable Bounds

Section 2 discussed two unreasonable bounds which affect 6LoWPAN interoperability. The first is the 1280 byte bound on maximum header decompression (the amount a header will grow when decompressed). A bound allows implementations to conserve RAM. As a result, some implementations impose their own lower bounds, but these bounds do not agree so some stacks cannot decompress some packets sent by other stacks. The lack of a bound on arbitrary next header compression was demonstrated as adding significant complexity to implementations to service packets which should rarely be used.

We propose that maximum header decompression in 6LoWPAN packets should be bounded to 50 bytes. This bound allows for significant RAM savings in implementations that decompress first fragments into the same buffer in which the fragment was originally held prior to any copying into a 1280 byte buffer. Currently, the lack of a bound means that there could theoretically be hundreds of bytes of header expansion if the received packet contained multiple highly compressed tunneled IPv6 headers, some with compressed IPv6 extension headers, and UDP header compression inside of all that. Given that some implementations might want to be able to receive multiple “first” fragments in close proximity without having to drop one, the extra RAM needed to guarantee handling of worst case header decompression can be significant.

We chose the limit of 50 bytes to balance the RAM savings of a lower bound against how frequently we expect such a bound would force packets to be sent uncompressed. A 50 byte limit allows for transmission of a packet containing a maximally compressed IP header, a maximally compressed UDP header, and still leaves room for some IPv6 extension headers. Packets which require more decompression than this are likely to be extremely rarely sent. This extremely rare cost, however, buys the hundreds of bytes of RAM that can be saved, and the increased ease of creating implementations that will always interoperate.

Second, the requirement for compression of interior headers for tunneled IPv6 should be removed. Currently, section 4.2 of RFC 6282 states “When the identified next header is an IPv6 Header […] The following bytes MUST be encoded using LOWPAN_IPHC”. This is problematic because it places no bound on how many tunneled IPv6 headers may need to be compressed or decompressed, creating locations in code that require unbounded amounts of recursion. We realize that tunneled IPv6 is a potentially common use case within 6LoWPAN networks, as it is the best way to ensure ICMP error messages can communicate to gateways when source routing headers make packets too long for end hosts to process. However, given the added complexity required to support this feature, and given the spotty support for this feature in existing 6LoWPAN stacks, it would be preferable if tunneled IPv6 headers were simply sent uncompressed. Implementations should adjust their path MTU constraints and responses to support inserting source routing headers, rather than tunnel IPv6.

This change would limit the complexity of arbitrary next header compression slightly. In addition, an ordering should be imposed on the order of IPv6 extension options if they are to be compressed. This would allow for implementations to avoid recursive functions to decompress these headers, and instead use simple if/else statements. If for some reason IPv6 extension headers must be placed in a different order for a particular packet, those options must be sent uncompressed.

6.4. Principle 4: Don’t Break Layering

UDP checksum compression, as defined in section 4.3.2 of RFC 6282, should be removed from the 6LoWPAN specification. The RFC says that a higher layer may request the checksum be elided if it has an integrity mechanism that covers the UDP header. At first glance, this seems sufficient: if the UDP header is covered by a message integrity code (MIC) or other checksum, then corrupted packets will be correctly dropped.

However, it misses an important error case: if the UDP ports are corrupted, then a packet missing a checksum may be delivered to the wrong application, and this incorrect application may not impose a replacement integrity measure or know one exists. It therefore cannot verify the MIC. Furthermore, protecting the header with a link-layer MIC is insufficient, as it only protects packets against sub-link corruption.

The end-to-end principle, foundational to all modern network design, says that only endpoints can verify correct communication (Saltzer et al., 1984). The only place that can safely verify the UDP header is the UDP stack. It is worth noting that the seminal example that led to definition of the end-to-end principe was a memory corruption: packets held in memory to be sent were corrupted before being sent. The recommended workarounds in RFC 6282 are vulnerable to such an event. A packet sent by an application that elides the UDP checksum could be corrupted in memory before the link-layer MIC is computed. Such a packet would be successfully received by the destination and dispatched to the wrong application, which would not check the application-level MIC.

The payoff of UDP checksum compression is not even significant—2 bytes of checksum is a small portion of a 127 byte frame. The problematic nature of UDP checksum compression is further demonstrated by the fact that only one of the five stacks we tested implements the feature.

7. Discussion and Conclusion

The Internet of Things is composed of a wide array of devices with different processor resources, cost and energy constraints. This unique property requires a fundamentally different approach to network protocol design than is used for other Internet protocols where end-nodes have virtually infinite resources relative to protocol overhead. In particular, it is unreasonable to design one-size-fits-all protocols. Instead, Internet protocols for low power devices should be designed to allow devices to use the protocol while optimizing for their particular resource tradeoffs.

Networked embedded systems have operated primarily as vertical silos for much of the early life of the ”Internet of Things” — most networking standards were full stack solutions developed for particular devices or applications, and could not interoperate directly with other networked systems. 6LoWPAN was the first standard that provided hope of a true interconnected Internet of Things — the IETF has tremendous experience creating interoperable protocols for the Internet.

Unfortunately, 6LoWPAN thus far has failed to truly break the vertical silos model of IoT networking, in large part because the specification did not address the trade-offs associated with the domain of low power devices. This reality is closely related to the fact that the research community which has investigated the tradeoffs in embedded systems at length is largely separate from the community that has focused on protocol creation and interoperability. Internet systems, and the idea of the Internet of Things, are entirely heterogeneous. However, the need to support a capability spectrum never comes up in low power research projects, where systems use homogeneous hardware and resources. Fortunately, this does not mean that the 6LoWPAN protocol, as it exists, is not valuable — the number of existing implementations of 6LoWPAN speaks to this. Instead, the tweaks to the protocol proposed above are sufficient to allow for the specification to remain interoperable across diverse implementations.

Interoperability is critical to bringing Internet connectivity to the next 100 million devices. To enable this, low power protocols must be intentionally designed to support interoperability between varied devices. The principles in this paper provide a framework for designing protocols with interoperability baked into the design.

8. Appendix

8.1. Code Size Measurements - Methodology

This section describes how the measurements in Table 3 were obtained.

8.1.1. Contiki

We compiled Contiki for the Atmel AVR Raven USB platform using several different compile-time configurations of the 6LoWPAN/IPv6 stack. For this platform, Contiki provides explicit suggestions regarding how to reduce the firmware size of the OS. To obtain a measurement of the full IP stack size, the binary was compiled with and without the CONTIKI_NO_NET flag set. To estimate the amount of code attributable to compression, the binary was compiled with the SICSLOWPAN_CONF_COMPRESSION flag set to SICSLOWPAN_COMPRESSION_HC06 (all 6LoWPAN compression support), then with it set to SICSLOWPAN_COMPRESSION_IPV6 (uncompressed 6LoWPAN support only). For fragmentation, the same process was repeated with SICSLOWPAN_CONF_FRAG set to 1, then 0.

8.1.2. OpenThread

The OpenThread stack does not provide explicit support for excluding portions of the 6LoWPAN stack. Therefore, we used the readelf script. A listing of the keywords used follows:

Full IP stack: ”lowpan—meshf—message—frag—ip6—radio802154— platradio—mac—energyscan—crypto—udp—tcp—icmp”.

6LoWPAN code alone”: ”lowpan—meshf—message—frag”.

Compression code: ”lowpan—message—meshf—frag” - ”meshf—frag”.

Fragmentation code: ”frag”.

Mesh Code: ”meshf”.

This method likely overestimates the size of compression code and underestimates fragmentation, as the ”message” keyword picks up a number of functions required for both fragmentation and compression, and there was no easy way to dissociate these given the volume and variety of these functions.

8.1.3. Riot

Riot allowed for extensive conditional compilation of the 6LoWPAN stack. We built the Riot ”minimal networking” example application, and measured the full code size. We also built the stack with fragmentation disabled, and again with compression disabled, allowing us to obtain estimates for the amount of code required for these functions. We believe that both numbers provided are short of the actual amount of code required, however, as the conditional compilation did not exclude all of the control logic associated with compression and fragmentation outside of the libraries themselves. To calculate the size of the entire 6LoWPAN stack we looked at the size of symbols, examined the source code, compiled a list of symbols associated with 6LoWPAN, and summed the size of those functions. We suspect that we did not identify 100% of the symbols associated with 6LoWPAN, so this is likely an underestimate.

8.1.4. Arm Mbed

For the Mbed-os 6LoWPAN stack, we used the readelf script with the following keywords:

Full IP stack: ”lowpan—iphc—compress—frag—mesh—reassembly— ipv6—udp—nd_—tcp—icmp”.

6LoWPAN code alone: ”lowpan—iphc—compress—frag—mesh— reassembly”.

Compression code: ”lowpan—iphc—compress”.

Fragmentation code: ”frag—reassembly”.

Mesh Code: ”mesh”.

8.1.5. TinyOS

For this stack, we compiled two different binaries - a UDPEcho example, which includes almost exclusively networking code for sending UDP and ICMP packets, and another demo app designed to include the full 6LoWPAN implementation. We looked at total binary sizes, and analyzed symbols within the binaries which corresponded to sixlowpan functions. TinyOS automatically enforces extensive code inlining, which made it impossible to disambiguate fragmentation code and compression code in the binaries.

8.1.6. Notes on Accuracy

These measurements are estimates, and estimates that we believe to be conservative. It is unlikely that removing modules such as fragmentation or compression from these stacks actually removes 100% of the code associated with the associated functionality. Further, it is clearly the case that a stack constructed from scratch to never use compression and fragmentation would be more memory efficient than a stack designed with those features, but those features removed. Further, when using the readelf script, we expect that our keyword search likely skipped over some functions associated 6LoWPAN which had atypical names, and that this keyword search may have excluded control code required to use these functions which is found in higher level networking functions. Despite this, we beleive these measurements effectively convey the relative size of these different stacks, and shed light on how 6LoWPAN code complexity is distributed between compression, fragmentation, and the mesh/broadcast headers.

8.2. Transmitted Packets

This section contains the hexadecial makeup of the packets generated on hardware, as recorded using a CC2650 running TIs Packet Sniffer 2 utility.

8.2.1. Contiki

Packet using uncompressed IPv6: ’71 dc c7 cd ab 2a 2a 2a 2a 2a 2a 2a 2a 5e d9 04 12 00 4b 12 00 41 60 00 00 00 00 3f 11 40 fe 80 00 00 00 00 00 00 02 12 4b 00 12 04 d9 5e fe 80 00 00 00 00 00 00 28 2a 2a 2a 2a 2a 2a 2a 1e 60 1e 60 00 3f 33 16 68 65 79 20 69 74 73 20 68 75 64 73 6f 6e 20 73 65 6e 64 69 6e 67 20 61 20 6c 6f 6e 67 69 73 68 20 6d 65 73 73 61 67 65 20 6a 75 73 74 20 74 6f 20 63 68 65 63 6b 0a 10 f1’.

8.2.2. Riot

Packet demonstrating stateless multicast compression: ’41 d8 03 23 00 ff ff 02 23 08 75 3e 1f 62 79 7e 3c 31 00 00 00 00 00 f0 00 16 00 16 d6 ce 74 65 73 74 69 6e 67 1e eb’.

Acknowledgements.
This work was supported in part by Intel/NSF CPS Security grant #1505728, with additional support from a Stanford Graduate Fellowship, VMware, Google, and the Secure Internet of Things Project. The authors would also like to acknowledge the help of the Tock core team for providing suggestions and criticisms throughout the process of writing this paper.

References