1 /* packet-knxip_decrypt.c
2 * Decryption keys and decryption functions for KNX/IP Dissector
3 * Copyright 2018, ise GmbH <Ralf.Nasilowski@ise.de>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
14 #define WS_LOG_DOMAIN "packet-knxip"
16 #include <wsutil/file_util.h>
17 #include <epan/proto.h>
18 #include "packet-knxip_decrypt.h"
19 #include <epan/wmem_scopes.h>
20 #include <wsutil/wsgcrypt.h>
21 #include <wsutil/strtoi.h>
22 #include <wsutil/wslog.h>
23 #include <wsutil/inet_addr.h>
25 #define TEXT_BUFFER_SIZE 128
27 #define IPA_SIZE 4 // = size of IPv4 address
29 #define BASE64_KNX_KEY_LENGTH 24 // = length of base64 encoded KNX key
31 struct knx_keyring_mca_keys
* knx_keyring_mca_keys
;
32 struct knx_keyring_ga_keys
* knx_keyring_ga_keys
;
33 struct knx_keyring_ga_senders
* knx_keyring_ga_senders
;
34 struct knx_keyring_ia_keys
* knx_keyring_ia_keys
;
35 struct knx_keyring_ia_seqs
* knx_keyring_ia_seqs
;
37 // Encrypt 16-byte block via AES
38 static void encrypt_block( const uint8_t key
[ KNX_KEY_LENGTH
], const uint8_t plain
[ KNX_KEY_LENGTH
], uint8_t p_crypt
[ KNX_KEY_LENGTH
] )
40 gcry_cipher_hd_t cryptor
= NULL
;
41 gcry_cipher_open( &cryptor
, GCRY_CIPHER_AES128
, GCRY_CIPHER_MODE_CBC
, 0 );
42 gcry_cipher_setkey( cryptor
, key
, KNX_KEY_LENGTH
);
43 gcry_cipher_encrypt( cryptor
, p_crypt
, KNX_KEY_LENGTH
, plain
, KNX_KEY_LENGTH
);
44 gcry_cipher_close( cryptor
);
47 // Create B_0 for CBC-MAC
48 static void build_b0( uint8_t p_result
[ KNX_KEY_LENGTH
], const uint8_t* nonce
, uint8_t nonce_length
)
50 DISSECTOR_ASSERT( nonce_length
<= KNX_KEY_LENGTH
);
51 if( nonce_length
) memcpy( p_result
, nonce
, nonce_length
);
52 memset( p_result
+ nonce_length
, 0, KNX_KEY_LENGTH
- nonce_length
);
55 // Create Ctr_0 for CCM encryption/decryption
56 static void build_ctr0( uint8_t p_result
[ KNX_KEY_LENGTH
], const uint8_t* nonce
, uint8_t nonce_length
)
58 build_b0( p_result
, nonce
, nonce_length
);
59 p_result
[ KNX_KEY_LENGTH
- 2 ] = 0xFF;
62 // Calculate MAC for KNX IP Security or KNX Data Security
63 void knx_ccm_calc_cbc_mac(uint8_t p_mac
[ KNX_KEY_LENGTH
], const uint8_t key
[ KNX_KEY_LENGTH
],
64 const uint8_t* a_bytes
, int a_length
, const uint8_t* p_bytes
, int p_length
,
65 const uint8_t b_0
[ KNX_KEY_LENGTH
] )
67 uint8_t plain
[ KNX_KEY_LENGTH
];
71 memcpy( plain
, b_0
, KNX_KEY_LENGTH
);
72 encrypt_block( key
, plain
, p_mac
);
75 plain
[ 0 ] = (uint8_t) ((a_length
>> 8) ^ p_mac
[ 0 ]);
76 plain
[ 1 ] = (uint8_t) ((a_length
& 0xFF) ^ p_mac
[ 1 ]);
79 // Add a_bytes directly followed by p_bytes
80 while( a_length
|| p_length
)
82 while( a_length
&& b_pos
< KNX_KEY_LENGTH
)
84 plain
[ b_pos
] = *a_bytes
++ ^ p_mac
[ b_pos
];
89 while( p_length
&& b_pos
< KNX_KEY_LENGTH
)
91 plain
[ b_pos
] = *p_bytes
++ ^ p_mac
[ b_pos
];
96 while( b_pos
< KNX_KEY_LENGTH
)
98 plain
[ b_pos
] = p_mac
[ b_pos
];
102 encrypt_block( key
, plain
, p_mac
);
108 // Calculate MAC for KNX IP Security, using 6-byte Sequence ID
109 void knxip_ccm_calc_cbc_mac( uint8_t p_mac
[ KNX_KEY_LENGTH
], const uint8_t key
[ KNX_KEY_LENGTH
],
110 const uint8_t* a_bytes
, int a_length
, const uint8_t* p_bytes
, int p_length
,
111 const uint8_t* nonce
, uint8_t nonce_length
)
113 uint8_t b_0
[ KNX_KEY_LENGTH
];
114 build_b0( b_0
, nonce
, nonce_length
);
115 b_0
[ KNX_KEY_LENGTH
- 2 ] = (uint8_t) (p_length
>> 8);
116 b_0
[ KNX_KEY_LENGTH
- 1 ] = (uint8_t) (p_length
& 0xFF);
117 knx_ccm_calc_cbc_mac( p_mac
, key
, a_bytes
, a_length
, p_bytes
, p_length
, b_0
);
120 // Encrypt for KNX IP Security or KNX Data Security
121 uint8_t* knx_ccm_encrypt( uint8_t* p_result
, const uint8_t key
[ KNX_KEY_LENGTH
], const uint8_t* p_bytes
, int p_length
,
122 const uint8_t* mac
, uint8_t mac_length
, const uint8_t ctr_0
[ KNX_KEY_LENGTH
], uint8_t s0_bytes_used_for_mac
)
124 if( p_length
>= 0 && !(p_length
&& !p_bytes
) )
126 // NB: mac_length = 16 (for IP Security), or 4 (for Data Security)
128 uint8_t* result
= p_result
? p_result
: (uint8_t*) wmem_alloc( wmem_packet_scope(), p_length
+ mac_length
);
130 uint8_t* dest
= result
;
132 uint8_t ctr
[ KNX_KEY_LENGTH
];
133 uint8_t mask
[ KNX_KEY_LENGTH
];
134 uint8_t mask_0
[ KNX_KEY_LENGTH
];
137 // Encrypt ctr_0 for mac
138 memcpy( ctr
, ctr_0
, KNX_KEY_LENGTH
);
139 encrypt_block( key
, ctr
, mask_0
);
141 // Encrypt p_bytes with rest of S_0, only if mac_length < 16.
142 b_pos
= s0_bytes_used_for_mac
;
143 while (p_length
&& b_pos
< KNX_KEY_LENGTH
)
145 *dest
++ = mask_0
[b_pos
++] ^ *p_bytes
++;
152 // Increment and encrypt ctr
153 ++ctr
[ KNX_KEY_LENGTH
- 1 ];
154 encrypt_block( key
, ctr
, mask
);
156 // Encrypt input block via encrypted ctr
158 while( p_length
&& b_pos
< KNX_KEY_LENGTH
)
160 *dest
++ = mask
[ b_pos
++] ^ *p_bytes
++;
167 if( mac_length
> KNX_KEY_LENGTH
)
169 mac_length
= KNX_KEY_LENGTH
;
172 // Encrypt and append mac
176 *dest
++ = mask_0
[ b_pos
++] ^ *mac
++;
187 // Encrypt for KNX IP Security (with 16-byte MAC and Nonce based on 6-byte Sequence ID)
188 uint8_t* knxip_ccm_encrypt( uint8_t* p_result
, const uint8_t key
[ KNX_KEY_LENGTH
], const uint8_t* p_bytes
, int p_length
,
189 const uint8_t mac
[KNX_KEY_LENGTH
], const uint8_t* nonce
, uint8_t nonce_length
)
191 uint8_t ctr_0
[ KNX_KEY_LENGTH
];
192 build_ctr0( ctr_0
, nonce
, nonce_length
);
193 return knx_ccm_encrypt( p_result
, key
, p_bytes
, p_length
, mac
, KNX_KEY_LENGTH
, ctr_0
, KNX_KEY_LENGTH
);
196 // Decrypt for KNX-IP Security (with 16-byte MAC and Nonce based on 6-byte Sequence ID)
197 uint8_t* knxip_ccm_decrypt( uint8_t* p_result
, const uint8_t key
[ KNX_KEY_LENGTH
], const uint8_t* crypt
, int crypt_length
,
198 const uint8_t* nonce
, uint8_t nonce_length
)
200 int p_length
= crypt_length
- KNX_KEY_LENGTH
;
201 uint8_t ctr_0
[ KNX_KEY_LENGTH
];
202 build_ctr0( ctr_0
, nonce
, nonce_length
);
203 return knx_ccm_encrypt( p_result
, key
, crypt
, p_length
, crypt
+ p_length
, KNX_KEY_LENGTH
, ctr_0
, KNX_KEY_LENGTH
);
206 static void fprintf_hex( FILE* f
, const uint8_t* data
, uint8_t length
)
208 for( ; length
; --length
) fprintf( f
, " %02X", *data
++ );
212 static void clear_keyring_data( void )
214 while( knx_keyring_mca_keys
)
216 struct knx_keyring_mca_keys
* mca_key
= knx_keyring_mca_keys
;
217 knx_keyring_mca_keys
= mca_key
->next
;
218 wmem_free( wmem_epan_scope(), mca_key
);
221 while( knx_keyring_ga_keys
)
223 struct knx_keyring_ga_keys
* ga_key
= knx_keyring_ga_keys
;
224 knx_keyring_ga_keys
= ga_key
->next
;
225 wmem_free( wmem_epan_scope(), ga_key
);
228 while( knx_keyring_ga_senders
)
230 struct knx_keyring_ga_senders
* ga_sender
= knx_keyring_ga_senders
;
231 knx_keyring_ga_senders
= ga_sender
->next
;
232 wmem_free( wmem_epan_scope(), ga_sender
);
235 while( knx_keyring_ia_keys
)
237 struct knx_keyring_ia_keys
* ia_key
= knx_keyring_ia_keys
;
238 knx_keyring_ia_keys
= ia_key
->next
;
239 wmem_free( wmem_epan_scope(), ia_key
);
242 while( knx_keyring_ia_seqs
)
244 struct knx_keyring_ia_seqs
* ia_seq
= knx_keyring_ia_seqs
;
245 knx_keyring_ia_seqs
= ia_seq
->next
;
246 wmem_free( wmem_epan_scope(), ia_seq
);
251 static void read_ip_addr( uint8_t result
[ 4 ], const char* text
)
253 ws_in4_addr value
= 0;
254 if( ws_inet_pton4( text
, &value
) )
255 memcpy( result
, &value
, 4 );
257 memset( result
, 0, 4 );
260 // Read KNX group address
261 static uint16_t read_ga( const char* text
)
264 int n
= sscanf( text
, "%u/%u/%u", a
, a
+ 1, a
+ 2 );
266 (n
== 1) ? (uint16_t) a
[ 0 ] :
267 (n
== 2) ? (uint16_t) ((a
[ 0 ] << 11) | a
[ 1 ]) :
268 (n
== 3) ? (uint16_t) ((a
[ 0 ] << 11) | (a
[ 1 ] << 8) | a
[ 2 ]) :
272 // Read KNX individual address
273 static uint16_t read_ia( const char* text
)
276 int n
= sscanf( text
, "%u.%u.%u", a
, a
+ 1, a
+ 2 );
278 (n
== 1) ? (uint16_t) a
[ 0 ] :
279 (n
== 2) ? (uint16_t) ((a
[ 0 ] << 8) | a
[ 1 ]) :
280 (n
== 3) ? (uint16_t) ((a
[ 0 ] << 12) | (a
[ 1 ] << 8) | a
[ 2 ]) :
284 // Read 6-byte sequence number from decimal representation
285 static uint64_t read_seq( const char* text
)
288 return ws_strtou64( text
, NULL
, &result
) ? result
: 0;
292 static void decrypt_key( uint8_t key
[] _U_
, uint8_t password_hash
[] _U_
, uint8_t created_hash
[] _U_
)
294 // TODO: decrypt as AES128-CBC(key, password_hash, created_hash)
297 // Decode and decrypt key
298 static void decode_and_decrypt_key( uint8_t key
[ BASE64_KNX_KEY_LENGTH
+ 1 ], const char* text
, uint8_t password_hash
[], uint8_t created_hash
[] )
301 snprintf( (char*) key
, BASE64_KNX_KEY_LENGTH
+ 1, "%s", text
);
302 g_base64_decode_inplace( (char*) key
, &out_len
);
303 decrypt_key( key
, password_hash
, created_hash
);
306 // Add MCA <-> key association
307 static void add_mca_key( const uint8_t mca
[ IPA_SIZE
], const char* text
, uint8_t password_hash
[], uint8_t created_hash
[], FILE* f2
)
309 int text_length
= (int) strlen( text
);
311 if( text_length
== BASE64_KNX_KEY_LENGTH
)
313 uint8_t key
[ BASE64_KNX_KEY_LENGTH
+ 1 ];
314 struct knx_keyring_mca_keys
** mca_keys_next
;
315 struct knx_keyring_mca_keys
* mca_key
;
317 decode_and_decrypt_key( key
, text
, password_hash
, created_hash
);
319 mca_keys_next
= &knx_keyring_mca_keys
;
321 while( (mca_key
= *mca_keys_next
) != NULL
)
323 if( memcmp( mca_key
->mca
, mca
, IPA_SIZE
) == 0 )
325 if( memcmp( mca_key
->key
, key
, KNX_KEY_LENGTH
) == 0 )
331 mca_keys_next
= &mca_key
->next
;
336 fprintf( f2
, "MCA %u.%u.%u.%u key", mca
[ 0 ], mca
[ 1 ], mca
[ 2 ], mca
[ 3 ] );
337 fprintf_hex( f2
, key
, KNX_KEY_LENGTH
);
340 mca_key
= wmem_new(wmem_epan_scope(), struct knx_keyring_mca_keys
);
344 mca_key
->next
= NULL
;
345 memcpy( mca_key
->mca
, mca
, IPA_SIZE
);
346 memcpy( mca_key
->key
, key
, KNX_KEY_LENGTH
);
348 *mca_keys_next
= mca_key
;
353 // Add GA <-> key association
354 static void add_ga_key( uint16_t ga
, const char* text
, uint8_t password_hash
[], uint8_t created_hash
[], FILE* f2
)
356 int text_length
= (int) strlen( text
);
358 if( text_length
== BASE64_KNX_KEY_LENGTH
)
360 uint8_t key
[ BASE64_KNX_KEY_LENGTH
+ 1 ];
361 struct knx_keyring_ga_keys
** ga_keys_next
;
362 struct knx_keyring_ga_keys
* ga_key
;
364 decode_and_decrypt_key( key
, text
, password_hash
, created_hash
);
366 ga_keys_next
= &knx_keyring_ga_keys
;
368 while( (ga_key
= *ga_keys_next
) != NULL
)
370 if( ga_key
->ga
== ga
)
372 if( memcmp( ga_key
->key
, key
, KNX_KEY_LENGTH
) == 0 )
378 ga_keys_next
= &ga_key
->next
;
383 fprintf( f2
, "GA %u/%u/%u key", (ga
>> 11) & 0x1F, (ga
>> 8) & 0x7, ga
& 0xFF );
384 fprintf_hex( f2
, key
, KNX_KEY_LENGTH
);
387 ga_key
= wmem_new(wmem_epan_scope(), struct knx_keyring_ga_keys
);
393 memcpy( ga_key
->key
, key
, KNX_KEY_LENGTH
);
395 *ga_keys_next
= ga_key
;
400 // Add GA <-> sender association
401 static void add_ga_sender( uint16_t ga
, const char* text
, FILE* f2
)
403 uint16_t ia
= read_ia( text
);
404 struct knx_keyring_ga_senders
** ga_senders_next
= &knx_keyring_ga_senders
;
405 struct knx_keyring_ga_senders
* ga_sender
;
407 while( (ga_sender
= *ga_senders_next
) != NULL
)
409 if( ga_sender
->ga
== ga
)
411 if( ga_sender
->ia
== ia
)
417 ga_senders_next
= &ga_sender
->next
;
422 fprintf( f2
, "GA %u/%u/%u sender %u.%u.%u\n", (ga
>> 11) & 0x1F, (ga
>> 8) & 0x7, ga
& 0xFF, (ia
>> 12) & 0xF, (ia
>> 8) & 0xF, ia
& 0xFF );
425 ga_sender
= wmem_new(wmem_epan_scope(), struct knx_keyring_ga_senders
);
429 ga_sender
->next
= NULL
;
433 *ga_senders_next
= ga_sender
;
437 // Add IA <-> key association
438 static void add_ia_key( uint16_t ia
, const char* text
, uint8_t password_hash
[], uint8_t created_hash
[], FILE* f2
)
440 int text_length
= (int) strlen( text
);
442 if( text_length
== BASE64_KNX_KEY_LENGTH
)
444 uint8_t key
[ BASE64_KNX_KEY_LENGTH
+ 1 ];
445 struct knx_keyring_ia_keys
** ia_keys_next
;
446 struct knx_keyring_ia_keys
* ia_key
;
448 decode_and_decrypt_key( key
, text
, password_hash
, created_hash
);
450 ia_keys_next
= &knx_keyring_ia_keys
;
452 while( (ia_key
= *ia_keys_next
) != NULL
)
454 if( ia_key
->ia
== ia
)
456 if( memcmp( ia_key
->key
, key
, KNX_KEY_LENGTH
) == 0 )
462 ia_keys_next
= &ia_key
->next
;
467 fprintf( f2
, "IA %u.%u.%u key", (ia
>> 12) & 0xF, (ia
>> 8) & 0xF, ia
& 0xFF );
468 fprintf_hex( f2
, key
, KNX_KEY_LENGTH
);
471 ia_key
= wmem_new(wmem_epan_scope(), struct knx_keyring_ia_keys
);
477 memcpy( ia_key
->key
, key
, KNX_KEY_LENGTH
);
479 *ia_keys_next
= ia_key
;
484 // Add IA <-> sequence number association
485 static void add_ia_seq( uint16_t ia
, const char* text
, FILE* f2
)
487 uint64_t seq
= read_seq( text
);
489 struct knx_keyring_ia_seqs
** ia_seqs_next
= &knx_keyring_ia_seqs
;
490 struct knx_keyring_ia_seqs
* ia_seq
;
492 while( (ia_seq
= *ia_seqs_next
) != NULL
)
494 if( ia_seq
->ia
== ia
)
496 if( ia_seq
->seq
== seq
)
502 ia_seqs_next
= &ia_seq
->next
;
507 fprintf( f2
, "IA %u.%u.%u SeqNr %" PRIu64
"\n", (ia
>> 12) & 0xF, (ia
>> 8) & 0xF, ia
& 0xFF, seq
);
510 ia_seq
= wmem_new(wmem_epan_scope(), struct knx_keyring_ia_seqs
);
518 *ia_seqs_next
= ia_seq
;
522 // Calculate PBKDF2(HMAC-SHA256, password, "1.keyring.ets.knx.org", 65536, 128)
523 static void make_password_hash( uint8_t password_hash
[] _U_
, const char* password _U_
)
525 // TODO: password_hash = PBKDF2(HMAC-SHA256, password, "1.keyring.ets.knx.org", 65536, 128)
528 // Calculate MSB128(SHA256(created))
529 static void make_created_hash( uint8_t created_hash
[] _U_
, const char* created _U_
)
531 // TODO: created_hash = MSB128(SHA256(created))
534 // Read KNX security key info from keyring XML file.
536 // An example keyring XML file is
537 // "test/keys/knx_keyring.xml".
539 // Corresponding test is
540 // suite_decryption.case_decrypt_knxip.test_knxip_keyring_xml_import
542 // We do not use LibXml2 here, because
543 // (1) we want to be platform independent,
544 // (2) we just want to extract some data from the keyring XML file,
545 // (3) we want to avoid the complicated recursive DOM processing implied by LibXml2.
547 // Resulting decoded and decrypted 16-byte keys with context info are optionally written to a "key info" text file.
548 // This may be useful, as these keys are not directly available from the keyring XML file .
549 void read_knx_keyring_xml_file( const char* key_file
, const char* password
, const char* key_info_file
)
551 // Clear old keyring data
552 clear_keyring_data();
554 // Read new data from keyring XML file
555 FILE* f
= ws_fopen( key_file
, "r" );
557 // Optionally write extracted data to key info file
558 FILE* f2
= (!key_info_file
|| !*key_info_file
) ? NULL
:
559 (strcmp( key_info_file
, "-" ) == 0) ? stdout
:
560 ws_fopen( key_info_file
, "w" );
564 uint8_t backbone_mca
[ IPA_SIZE
];
565 uint8_t backbone_mca_valid
= 0;
566 uint16_t group_ga
= 0;
567 uint8_t group_ga_valid
= 0;
568 uint16_t device_ia
= 0;
569 uint8_t device_ia_valid
= 0;
570 char name
[ TEXT_BUFFER_SIZE
];
571 char value
[ TEXT_BUFFER_SIZE
];
572 uint8_t password_hash
[ KNX_KEY_LENGTH
];
573 uint8_t created_hash
[ KNX_KEY_LENGTH
];
574 char tag_name
[ TEXT_BUFFER_SIZE
];
575 uint8_t tag_name_done
= 0;
579 memset( backbone_mca
, 0, IPA_SIZE
);
582 memset( password_hash
, 0, KNX_KEY_LENGTH
);
583 memset( created_hash
, 0, KNX_KEY_LENGTH
);
586 make_password_hash( password_hash
, password
);
588 ws_debug( "%s:", key_file
);
594 if( c
== '<' ) // tag start
603 else if( c
== '>' ) // tag end
609 if( in_tag
) // "</" or "/>"
618 else if( g_ascii_isalpha( c
) || c
== '_' ) // possibly tag name, or attribute name
621 name
[ length
++ ] = (char) c
;
622 while( (c
= fgetc( f
)) >= 0 )
624 if( g_ascii_isalnum( c
) || c
== '_' )
626 if( length
< sizeof name
- 1 )
628 name
[ length
++ ] = (char) c
;
636 name
[ length
] = '\0';
639 if( !tag_name_done
) // tag name
641 snprintf( tag_name
, sizeof tag_name
, "%s", name
);
645 else // Check for name="value" construct
647 while( c
>= 0 && g_ascii_isspace( c
) ) c
= fgetc( f
);
651 while( (c
= fgetc( f
)) >= 0 && g_ascii_isspace( c
) );
657 while( (c
= fgetc( f
)) >= 0 )
667 if( length
< sizeof value
- 1 )
669 value
[ length
++ ] = (char) c
;
677 // Found name="value" construct between < and >
678 ws_debug( "%s %s=%s", tag_name
, name
, value
);
680 // Process name/value pair
681 if( strcmp( tag_name
, "Keyring" ) == 0 )
683 if( strcmp( name
, "Created" ) == 0 )
685 make_created_hash( created_hash
, value
);
688 else if( strcmp( tag_name
, "Backbone" ) == 0 )
693 if( strcmp( name
, "MulticastAddress" ) == 0 )
695 read_ip_addr( backbone_mca
, value
);
696 backbone_mca_valid
= 1;
698 else if( strcmp( name
, "Key" ) == 0 )
700 if( backbone_mca_valid
)
702 add_mca_key( backbone_mca
, value
, password_hash
, created_hash
, f2
);
706 else if( strcmp( tag_name
, "Group" ) == 0 )
708 backbone_mca_valid
= 0;
711 if( strcmp( name
, "Address" ) == 0 )
713 group_ga
= read_ga( value
);
716 else if( strcmp( name
, "Key" ) == 0 )
720 add_ga_key( group_ga
, value
, password_hash
, created_hash
, f2
);
723 else if( strcmp( name
, "Senders" ) == 0 )
727 // Add senders given by space separated list of KNX IAs
728 static const char delim
[] = " ,";
729 const char* token
= strtok( value
, delim
);
732 add_ga_sender( group_ga
, token
, f2
);
733 token
= strtok( NULL
, delim
);
738 else if( strcmp( tag_name
, "Device" ) == 0 )
740 backbone_mca_valid
= 0;
743 if( strcmp( name
, "IndividualAddress" ) == 0 )
745 device_ia
= read_ia( value
);
748 else if( strcmp( name
, "ToolKey" ) == 0 )
750 if( device_ia_valid
)
752 add_ia_key( device_ia
, value
, password_hash
, created_hash
, f2
);
755 else if( strcmp( name
, "SequenceNumber" ) == 0 )
757 if( device_ia_valid
)
759 add_ia_seq( device_ia
, value
, f2
);
765 backbone_mca_valid
= 0;
783 if( !g_ascii_isspace( c
) )
797 if( f2
&& f2
!= stdout
)
804 * Editor modelines - https://www.wireshark.org/tools/modelines.html
809 * indent-tabs-mode: nil
812 * vi: set shiftwidth=2 tabstop=8 expandtab:
813 * :indentSize=2:tabSize=8:noTabs=true: