Contiki-NG
tsch-packet.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014, SICS Swedish ICT.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the Institute nor the names of its contributors
14  * may be used to endorse or promote products derived from this software
15  * without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * This file is part of the Contiki operating system.
30  *
31  */
32 
33 /**
34  * \file
35  * TSCH packet format management
36  * \author
37  * Simon Duquennoy <simonduq@sics.se>
38  * Beshr Al Nahas <beshr@sics.se>
39  */
40 
41 /**
42  * \addtogroup tsch
43  * @{
44 */
45 
46 #include "contiki.h"
47 #include "net/packetbuf.h"
48 #include "net/mac/tsch/tsch.h"
51 #include "net/netstack.h"
52 #include "lib/ccm-star.h"
53 #include "lib/aes-128.h"
54 
55 /* Log configuration */
56 #include "sys/log.h"
57 #define LOG_MODULE "TSCH Pkt"
58 #define LOG_LEVEL LOG_LEVEL_MAC
59 
60 /*
61  * We use a local packetbuf_attr array to collect necessary frame settings to
62  * create an EACK because EACK is generated in the interrupt context where
63  * packetbuf and packetbuf_attrs[] may be in use for another purpose.
64  *
65  * We have accessors of eackbuf_attrs: tsch_packet_eackbuf_set_attr() and
66  * tsch_packet_eackbuf_attr(). For some platform, they might need to be
67  * implemented as inline functions. However, for now, we don't provide the
68  * inline option. Such an optimization is left to the compiler for a target
69  * platform.
70  */
71 static struct packetbuf_attr eackbuf_attrs[PACKETBUF_NUM_ATTRS];
72 
73 /* The offset of the frame pending bit flag within the first byte of FCF */
74 #define IEEE802154_FRAME_PENDING_BIT_OFFSET 4
75 
76 /*---------------------------------------------------------------------------*/
77 static int
78 tsch_packet_eackbuf_set_attr(uint8_t type, const packetbuf_attr_t val)
79 {
80  eackbuf_attrs[type].val = val;
81  return 1;
82 }
83 /*---------------------------------------------------------------------------*/
84 /* Return the value of a specified attribute */
85 packetbuf_attr_t
86 tsch_packet_eackbuf_attr(uint8_t type)
87 {
88  return eackbuf_attrs[type].val;
89 }
90 /*---------------------------------------------------------------------------*/
91 /* Construct enhanced ACK packet and return ACK length */
92 int
93 tsch_packet_create_eack(uint8_t *buf, uint16_t buf_len,
94  const linkaddr_t *dest_addr, uint8_t seqno,
95  int16_t drift, int nack)
96 {
97  frame802154_t params;
98  struct ieee802154_ies ies;
99  int hdr_len;
100  int ack_len;
101 
102  if(buf == NULL) {
103  return -1;
104  }
105 
106  memset(eackbuf_attrs, 0, sizeof(eackbuf_attrs));
107 
108  tsch_packet_eackbuf_set_attr(PACKETBUF_ATTR_FRAME_TYPE, FRAME802154_ACKFRAME);
109  tsch_packet_eackbuf_set_attr(PACKETBUF_ATTR_MAC_METADATA, 1);
110  tsch_packet_eackbuf_set_attr(PACKETBUF_ATTR_MAC_SEQNO, seqno);
111 
112  tsch_packet_eackbuf_set_attr(PACKETBUF_ATTR_MAC_NO_DEST_ADDR, 1);
113 #if TSCH_PACKET_EACK_WITH_DEST_ADDR
114  if(dest_addr != NULL) {
115  tsch_packet_eackbuf_set_attr(PACKETBUF_ATTR_MAC_NO_DEST_ADDR, 0);
116  linkaddr_copy((linkaddr_t *)&params.dest_addr, dest_addr);
117  }
118 #endif
119 
120  tsch_packet_eackbuf_set_attr(PACKETBUF_ATTR_MAC_NO_SRC_ADDR, 1);
121 #if TSCH_PACKET_EACK_WITH_SRC_ADDR
122  tsch_packet_eackbuf_set_attr(PACKETBUF_ATTR_MAC_NO_SRC_ADDR, 0);
123  linkaddr_copy((linkaddr_t *)&params.src_addr, &linkaddr_node_addr);
124 #endif
125 
126 #if LLSEC802154_ENABLED
127  if(tsch_is_pan_secured) {
128  tsch_packet_eackbuf_set_attr(PACKETBUF_ATTR_SECURITY_LEVEL,
129  TSCH_SECURITY_KEY_SEC_LEVEL_ACK);
130  tsch_packet_eackbuf_set_attr(PACKETBUF_ATTR_KEY_ID_MODE,
131  FRAME802154_1_BYTE_KEY_ID_MODE);
132  tsch_packet_eackbuf_set_attr(PACKETBUF_ATTR_KEY_INDEX,
133  TSCH_SECURITY_KEY_INDEX_ACK);
134  }
135 #endif /* LLSEC802154_ENABLED */
136 
137  framer_802154_setup_params(tsch_packet_eackbuf_attr, 0, &params);
138  hdr_len = frame802154_hdrlen(&params);
139 
140  memset(buf, 0, buf_len);
141 
142  /* Setup IE timesync */
143  memset(&ies, 0, sizeof(ies));
144  ies.ie_time_correction = drift;
145  ies.ie_is_nack = nack;
146 
147  ack_len =
149  buf_len - hdr_len, &ies);
150  if(ack_len < 0) {
151  return -1;
152  }
153  ack_len += hdr_len;
154 
155  frame802154_create(&params, buf);
156 
157  return ack_len;
158 }
159 /*---------------------------------------------------------------------------*/
160 /* Parse enhanced ACK packet, extract drift and nack */
161 int
162 tsch_packet_parse_eack(const uint8_t *buf, int buf_size,
163  uint8_t seqno, frame802154_t *frame, struct ieee802154_ies *ies, uint8_t *hdr_len)
164 {
165  uint8_t curr_len = 0;
166  int ret;
167  linkaddr_t dest;
168 
169  if(frame == NULL || buf_size < 0) {
170  return 0;
171  }
172  /* Parse 802.15.4-2006 frame, i.e. all fields before Information Elements */
173  if((ret = frame802154_parse((uint8_t *)buf, buf_size, frame)) < 3) {
174  return 0;
175  }
176  if(hdr_len != NULL) {
177  *hdr_len = ret;
178  }
179  curr_len += ret;
180 
181  /* Check seqno */
182  if(seqno != frame->seq) {
183  return 0;
184  }
185 
186  /* Check destination PAN ID */
187  if(frame802154_check_dest_panid(frame) == 0) {
188  return 0;
189  }
190 
191  /* Check destination address (if any) */
192  if(frame802154_extract_linkaddr(frame, NULL, &dest) == 0 ||
194  && !linkaddr_cmp(&dest, &linkaddr_null))) {
195  return 0;
196  }
197 
198  if(ies != NULL) {
199  memset(ies, 0, sizeof(struct ieee802154_ies));
200  }
201 
202  if(frame->fcf.ie_list_present) {
203  int mic_len = 0;
204 #if LLSEC802154_ENABLED
205  /* Check if there is space for the security MIC (if any) */
206  mic_len = tsch_security_mic_len(frame);
207  if(buf_size < curr_len + mic_len) {
208  return 0;
209  }
210 #endif /* LLSEC802154_ENABLED */
211  /* Parse information elements. We need to substract the MIC length, as the exact payload len is needed while parsing */
212  if((ret = frame802154e_parse_information_elements(buf + curr_len, buf_size - curr_len - mic_len, ies)) == -1) {
213  return 0;
214  }
215  curr_len += ret;
216  }
217 
218  if(hdr_len != NULL) {
219  *hdr_len += ies->ie_payload_ie_offset;
220  }
221 
222  return curr_len;
223 }
224 /*---------------------------------------------------------------------------*/
225 /* Create an EB packet */
226 int
227 tsch_packet_create_eb(uint8_t *hdr_len, uint8_t *tsch_sync_ie_offset)
228 {
229  struct ieee802154_ies ies;
230  uint8_t *p;
231  int ie_len;
232  const uint16_t payload_ie_hdr_len = 2;
233 
234  packetbuf_clear();
235 
236  /* Prepare Information Elements for inclusion in the EB */
237  memset(&ies, 0, sizeof(ies));
238 
239  /* Add TSCH timeslot timing IE. */
240 #if TSCH_PACKET_EB_WITH_TIMESLOT_TIMING
241  {
242  int i;
243  ies.ie_tsch_timeslot_id = 1;
244  for(i = 0; i < tsch_ts_elements_count; i++) {
245  ies.ie_tsch_timeslot[i] = RTIMERTICKS_TO_US(tsch_timing[i]);
246  }
247  }
248 #endif /* TSCH_PACKET_EB_WITH_TIMESLOT_TIMING */
249 
250  /* Add TSCH hopping sequence IE */
251 #if TSCH_PACKET_EB_WITH_HOPPING_SEQUENCE
252  if(tsch_hopping_sequence_length.val <= sizeof(ies.ie_hopping_sequence_list)) {
253  ies.ie_channel_hopping_sequence_id = 1;
254  ies.ie_hopping_sequence_len = tsch_hopping_sequence_length.val;
255  memcpy(ies.ie_hopping_sequence_list, tsch_hopping_sequence,
256  ies.ie_hopping_sequence_len);
257  }
258 #endif /* TSCH_PACKET_EB_WITH_HOPPING_SEQUENCE */
259 
260  /* Add Slotframe and Link IE */
261 #if TSCH_PACKET_EB_WITH_SLOTFRAME_AND_LINK
262  {
263  /* Send slotframe 0 with link at timeslot 0 */
265  struct tsch_link *link0 = tsch_schedule_get_link_by_timeslot(sf0, 0);
266  if(sf0 && link0) {
267  ies.ie_tsch_slotframe_and_link.num_slotframes = 1;
268  ies.ie_tsch_slotframe_and_link.slotframe_handle = sf0->handle;
269  ies.ie_tsch_slotframe_and_link.slotframe_size = sf0->size.val;
270  ies.ie_tsch_slotframe_and_link.num_links = 1;
271  ies.ie_tsch_slotframe_and_link.links[0].timeslot = link0->timeslot;
272  ies.ie_tsch_slotframe_and_link.links[0].channel_offset =
273  link0->channel_offset;
274  ies.ie_tsch_slotframe_and_link.links[0].link_options =
275  link0->link_options;
276  }
277  }
278 #endif /* TSCH_PACKET_EB_WITH_SLOTFRAME_AND_LINK */
279 
280  p = packetbuf_dataptr();
281 
282  ie_len = frame80215e_create_ie_tsch_synchronization(p,
284  &ies);
285  if(ie_len < 0) {
286  return -1;
287  }
288  p += ie_len;
290 
291  ie_len = frame80215e_create_ie_tsch_timeslot(p,
293  &ies);
294  if(ie_len < 0) {
295  return -1;
296  }
297  p += ie_len;
299 
300  ie_len = frame80215e_create_ie_tsch_channel_hopping_sequence(p,
302  &ies);
303  if(ie_len < 0) {
304  return -1;
305  }
306  p += ie_len;
308 
309  ie_len = frame80215e_create_ie_tsch_slotframe_and_link(p,
311  &ies);
312  if(ie_len < 0) {
313  return -1;
314  }
315  p += ie_len;
317 
318 #if 0
319  /* Payload IE list termination: optional */
320  ie_len = frame80215e_create_ie_payload_list_termination(p,
322  &ies);
323  if(ie_len < 0) {
324  return -1;
325  }
326  p += ie_len;
328 #endif
329 
330  ies.ie_mlme_len = packetbuf_datalen();
331 
332  /* make room for Payload IE header */
333  memmove((uint8_t *)packetbuf_dataptr() + payload_ie_hdr_len,
335  packetbuf_set_datalen(packetbuf_datalen() + payload_ie_hdr_len);
336  ie_len = frame80215e_create_ie_mlme(packetbuf_dataptr(),
338  &ies);
339  if(ie_len < 0) {
340  return -1;
341  }
342 
343  /* allocate space for Header Termination IE, the size of which is 2 octets */
345  ie_len = frame80215e_create_ie_header_list_termination_1(packetbuf_hdrptr(),
347  &ies);
348  if(ie_len < 0) {
349  return -1;
350  }
351 
352  packetbuf_set_attr(PACKETBUF_ATTR_FRAME_TYPE, FRAME802154_BEACONFRAME);
353  packetbuf_set_attr(PACKETBUF_ATTR_MAC_METADATA, 1);
354 
355  packetbuf_set_addr(PACKETBUF_ADDR_SENDER, &linkaddr_node_addr);
356  packetbuf_set_addr(PACKETBUF_ADDR_RECEIVER, &tsch_eb_address);
357 
358 #if LLSEC802154_ENABLED
359  if(tsch_is_pan_secured) {
360  packetbuf_set_attr(PACKETBUF_ATTR_SECURITY_LEVEL,
361  TSCH_SECURITY_KEY_SEC_LEVEL_EB);
362  packetbuf_set_attr(PACKETBUF_ATTR_KEY_ID_MODE,
363  FRAME802154_1_BYTE_KEY_ID_MODE);
364  packetbuf_set_attr(PACKETBUF_ATTR_KEY_INDEX,
365  TSCH_SECURITY_KEY_INDEX_EB);
366  }
367 #endif /* LLSEC802154_ENABLED */
368 
369  if(NETSTACK_FRAMER.create() < 0) {
370  return -1;
371  }
372 
373  if(hdr_len != NULL) {
374  *hdr_len = packetbuf_hdrlen();
375  }
376 
377  /*
378  * Save the offset of the TSCH Synchronization IE, which is expected to be
379  * located just after the Payload IE header, needed to update ASN and join
380  * priority before sending.
381  */
382  if(tsch_sync_ie_offset != NULL) {
383  *tsch_sync_ie_offset = packetbuf_hdrlen() + payload_ie_hdr_len;
384  }
385 
386  return packetbuf_totlen();
387 }
388 /*---------------------------------------------------------------------------*/
389 /* Update ASN in EB packet */
390 int
391 tsch_packet_update_eb(uint8_t *buf, int buf_size, uint8_t tsch_sync_ie_offset)
392 {
393  struct ieee802154_ies ies;
394  ies.ie_asn = tsch_current_asn;
395  ies.ie_join_priority = tsch_join_priority;
396  return frame80215e_create_ie_tsch_synchronization(buf+tsch_sync_ie_offset, buf_size-tsch_sync_ie_offset, &ies) != -1;
397 }
398 /*---------------------------------------------------------------------------*/
399 /* Parse a IEEE 802.15.4e TSCH Enhanced Beacon (EB) */
400 int
401 tsch_packet_parse_eb(const uint8_t *buf, int buf_size,
402  frame802154_t *frame, struct ieee802154_ies *ies, uint8_t *hdr_len, int frame_without_mic)
403 {
404  uint8_t curr_len = 0;
405  int ret;
406 
407  if(frame == NULL || buf_size < 0) {
408  return 0;
409  }
410 
411  /* Parse 802.15.4-2006 frame, i.e. all fields before Information Elements */
412  if((ret = frame802154_parse((uint8_t *)buf, buf_size, frame)) == 0) {
413  LOG_ERR("! parse_eb: failed to parse frame\n");
414  return 0;
415  }
416 
417  if(frame->fcf.frame_version < FRAME802154_IEEE802154_2015
418  || frame->fcf.frame_type != FRAME802154_BEACONFRAME) {
419  LOG_INFO("! parse_eb: frame is not a valid TSCH beacon. Frame version %u, type %u, FCF %02x %02x\n",
420  frame->fcf.frame_version, frame->fcf.frame_type, buf[0], buf[1]);
421  LOG_INFO("! parse_eb: frame was from 0x%x/", frame->src_pid);
422  LOG_INFO_LLADDR((const linkaddr_t *)&frame->src_addr);
423  LOG_INFO_(" to 0x%x/", frame->dest_pid);
424  LOG_INFO_LLADDR((const linkaddr_t *)&frame->dest_addr);
425  LOG_INFO_("\n");
426  return 0;
427  }
428 
429  if(hdr_len != NULL) {
430  *hdr_len = ret;
431  }
432  curr_len += ret;
433 
434  if(ies != NULL) {
435  memset(ies, 0, sizeof(struct ieee802154_ies));
436  ies->ie_join_priority = 0xff; /* Use max value in case the Beacon does not include a join priority */
437  }
438  if(frame->fcf.ie_list_present) {
439  /* Calculate space needed for the security MIC, if any, before attempting to parse IEs */
440  int mic_len = 0;
441 #if LLSEC802154_ENABLED
442  if(!frame_without_mic) {
443  mic_len = tsch_security_mic_len(frame);
444  if(buf_size < curr_len + mic_len) {
445  return 0;
446  }
447  }
448 #endif /* LLSEC802154_ENABLED */
449 
450  /* Parse information elements. We need to substract the MIC length, as the exact payload len is needed while parsing */
451  if((ret = frame802154e_parse_information_elements(buf + curr_len, buf_size - curr_len - mic_len, ies)) == -1) {
452  LOG_ERR("! parse_eb: failed to parse IEs\n");
453  return 0;
454  }
455  curr_len += ret;
456  }
457 
458  if(hdr_len != NULL) {
459  *hdr_len += ies->ie_payload_ie_offset;
460  }
461 
462  return curr_len;
463 }
464 /*---------------------------------------------------------------------------*/
465 /* Set frame pending bit in a packet (whose header was already build) */
466 void
467 tsch_packet_set_frame_pending(uint8_t *buf, int buf_size)
468 {
469  buf[0] |= (1 << IEEE802154_FRAME_PENDING_BIT_OFFSET);
470 }
471 /*---------------------------------------------------------------------------*/
472 /* Get frame pending bit from a packet */
473 int
474 tsch_packet_get_frame_pending(uint8_t *buf, int buf_size)
475 {
476  return (buf[0] >> IEEE802154_FRAME_PENDING_BIT_OFFSET) & 1;
477 }
478 /*---------------------------------------------------------------------------*/
479 /** @} */
uint16_t src_pid
Source PAN ID.
Definition: frame802154.h:207
void * packetbuf_dataptr(void)
Get a pointer to the data in the packetbuf.
Definition: packetbuf.c:143
int tsch_packet_create_eb(uint8_t *hdr_len, uint8_t *tsch_sync_ie_offset)
Create an EB packet directly in packetbuf.
Definition: tsch-packet.c:227
frame802154_fcf_t fcf
Frame control field.
Definition: frame802154.h:204
void packetbuf_clear(void)
Clear and reset the packetbuf.
Definition: packetbuf.c:75
int packetbuf_hdralloc(int size)
Extend the header of the packetbuf, for outbound packets.
Definition: packetbuf.c:107
uint16_t packetbuf_remaininglen(void)
Get the total length of the remaining space in the packetbuf.
Definition: packetbuf.c:173
int frame802154_parse(uint8_t *data, int len, frame802154_t *pf)
Parses an input frame.
Definition: frame802154.c:500
802.15.4e slotframe (contains links)
Definition: tsch-types.h:84
uint8_t packetbuf_hdrlen(void)
Get the length of the header in the packetbuf.
Definition: packetbuf.c:161
struct tsch_link * tsch_schedule_get_link_by_timeslot(struct tsch_slotframe *slotframe, uint16_t timeslot)
Looks within a slotframe for a link with a given timeslot.
uint8_t src_addr[8]
Source address.
Definition: frame802154.h:203
int frame802154_hdrlen(frame802154_t *p)
Calculates the length of the frame header.
Definition: frame802154.c:358
A MAC framer for IEEE 802.15.4
const linkaddr_t linkaddr_null
The null link-layer address.
int tsch_packet_update_eb(uint8_t *buf, int buf_size, uint8_t tsch_sync_ie_offset)
Update ASN in EB packet.
Definition: tsch-packet.c:391
uint16_t packetbuf_datalen(void)
Get the length of the data in the packetbuf.
Definition: packetbuf.c:155
linkaddr_t linkaddr_node_addr
The link-layer address of the node.
Definition: linkaddr.c:48
struct tsch_slotframe * tsch_schedule_get_slotframe_by_handle(uint16_t handle)
Looks up a slotframe by handle.
unsigned int tsch_security_mic_len(const frame802154_t *frame)
Return MIC length.
uint8_t frame_version
2 bit.
Definition: frame802154.h:162
uint16_t packetbuf_totlen(void)
Get the total length of the header and data in the packetbuf.
Definition: packetbuf.c:167
CCM* header file.
int tsch_packet_parse_eack(const uint8_t *buf, int buf_size, uint8_t seqno, frame802154_t *frame, struct ieee802154_ies *ies, uint8_t *hdr_len)
Parse enhanced ACK packet.
Definition: tsch-packet.c:162
Main API declarations for TSCH.
802.15.4 frame creation and parsing functions
uint8_t frame_type
3 bit.
Definition: frame802154.h:153
uint16_t dest_pid
Destination PAN ID.
Definition: frame802154.h:206
Parameters used by the frame802154_create() function.
Definition: frame802154.h:198
void linkaddr_copy(linkaddr_t *dest, const linkaddr_t *src)
Copy a link-layer address.
Definition: linkaddr.c:63
void tsch_packet_set_frame_pending(uint8_t *buf, int buf_size)
Set frame pending bit in a packet (whose header was already build)
Definition: tsch-packet.c:467
int linkaddr_cmp(const linkaddr_t *addr1, const linkaddr_t *addr2)
Compare two link-layer addresses.
Definition: linkaddr.c:69
void * packetbuf_hdrptr(void)
Get a pointer to the header in the packetbuf, for outbound packets.
Definition: packetbuf.c:149
uint8_t ie_list_present
1 bit.
Definition: frame802154.h:160
uint8_t seq
Sequence number.
Definition: frame802154.h:205
int tsch_packet_get_frame_pending(uint8_t *buf, int buf_size)
Get frame pending bit from a packet.
Definition: tsch-packet.c:474
Header file for the Packet buffer (packetbuf) management
Include file for the Contiki low-layer network stack (NETSTACK)
uint8_t dest_addr[8]
Destination address.
Definition: frame802154.h:202
Header file for the logging system
AES-128.
int tsch_packet_parse_eb(const uint8_t *buf, int buf_size, frame802154_t *frame, struct ieee802154_ies *ies, uint8_t *hdr_len, int frame_without_mic)
Parse EB.
Definition: tsch-packet.c:401
void packetbuf_set_datalen(uint16_t len)
Set the length of the data in the packetbuf.
Definition: packetbuf.c:136
int frame80215e_create_ie_header_ack_nack_time_correction(uint8_t *buf, int len, struct ieee802154_ies *ies)
Insert various Information Elements.
int frame802154_create(frame802154_t *p, uint8_t *buf)
Creates a frame for transmission over the air.
Definition: frame802154.c:392
int tsch_packet_create_eack(uint8_t *buf, uint16_t buf_len, const linkaddr_t *dest_addr, uint8_t seqno, int16_t drift, int nack)
Construct Enhanced ACK packet.
Definition: tsch-packet.c:93