Coverage Report

Created: 2026-11-12 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-nflog.c
Line
Count
Source
1
/* packet-nflog.c
2
 * Copyright 2011,2012 Jakub Zawadzki <darkjames-ws@darkjames.pl>
3
 *
4
 * Wireshark - Network traffic analyzer
5
 * By Gerald Combs <gerald@wireshark.org>
6
 * Copyright 1998
7
 *
8
 * SPDX-License-Identifier: GPL-2.0-or-later
9
 */
10
#include "config.h"
11
12
#include <epan/etypes.h>
13
#include <epan/packet.h>
14
#include <wiretap/wtap.h>
15
#include <wsutil/ws_roundup.h>
16
17
#include "packet-arp.h"
18
#include "packet-netlink.h"
19
20
void proto_register_nflog(void);
21
void proto_reg_handoff_nflog(void);
22
23
/* nfulnl_attr_type enum from <linux/netfilter/nfnetlink_log.h> */
24
enum ws_nfulnl_attr_type {
25
    WS_NFULA_UNSPEC,
26
    WS_NFULA_PACKET_HDR,
27
    WS_NFULA_MARK,               /* __u32 nfmark */
28
    WS_NFULA_TIMESTAMP,          /* nfulnl_msg_packet_timestamp */
29
    WS_NFULA_IFINDEX_INDEV,      /* __u32 ifindex */
30
    WS_NFULA_IFINDEX_OUTDEV,     /* __u32 ifindex */
31
    WS_NFULA_IFINDEX_PHYSINDEV,  /* __u32 ifindex */
32
    WS_NFULA_IFINDEX_PHYSOUTDEV, /* __u32 ifindex */
33
    WS_NFULA_HWADDR,             /* nfulnl_msg_packet_hw */
34
    WS_NFULA_PAYLOAD,            /* opaque data payload */
35
    WS_NFULA_PREFIX,             /* string prefix */
36
    WS_NFULA_UID,                /* user id of socket */
37
    WS_NFULA_SEQ,                /* instance-local sequence number */
38
    WS_NFULA_SEQ_GLOBAL,         /* global sequence number */
39
    WS_NFULA_GID,                /* group id of socket */
40
    WS_NFULA_HWTYPE,             /* hardware type */
41
    WS_NFULA_HWHEADER,           /* hardware header */
42
    WS_NFULA_HWLEN,              /* hardware header length */
43
    WS_NFULA_CT,                 /* nfnetlink_conntrack.h */
44
    WS_NFULA_CT_INFO,            /* enum ip_conntrack_info */
45
};
46
47
static const value_string nflog_tlv_vals[] = {
48
    { WS_NFULA_UNSPEC,             "NFULA_UNSPEC" },
49
    { WS_NFULA_PACKET_HDR,         "NFULA_PACKET_HDR" },
50
    { WS_NFULA_MARK,               "NFULA_MARK" },
51
    { WS_NFULA_TIMESTAMP,          "NFULA_TIMESTAMP" },
52
    { WS_NFULA_IFINDEX_INDEV,      "NFULA_IFINDEX_INDEV" },
53
    { WS_NFULA_IFINDEX_OUTDEV,     "NFULA_IFINDEX_OUTDEV" },
54
    { WS_NFULA_IFINDEX_PHYSINDEV,  "NFULA_IFINDEX_PHYSINDEV" },
55
    { WS_NFULA_IFINDEX_PHYSOUTDEV, "NFULA_IFINDEX_PHYSOUTDEV" },
56
    { WS_NFULA_HWADDR,             "NFULA_HWADDR" },
57
    { WS_NFULA_PAYLOAD,            "NFULA_PAYLOAD" },
58
    { WS_NFULA_PREFIX,             "NFULA_PREFIX" },
59
    { WS_NFULA_UID,                "NFULA_UID" },
60
    { WS_NFULA_SEQ,                "NFULA_SEQ" },
61
    { WS_NFULA_SEQ_GLOBAL,         "NFULA_SEQ_GLOBAL" },
62
    { WS_NFULA_GID,                "NFULA_GID" },
63
    { WS_NFULA_HWTYPE,             "NFULA_HWTYPE" },
64
    { WS_NFULA_HWHEADER,           "NFULA_HWHEADER" },
65
    { WS_NFULA_HWLEN,              "NFULA_HWLEN" },
66
    { WS_NFULA_CT,                 "NFULA_CT" },
67
    { WS_NFULA_CT_INFO,            "NFULA_CT_INFO" },
68
    { 0, NULL }
69
};
70
71
static int proto_nflog;
72
73
static int hf_nflog_tlv_ct;
74
static int hf_nflog_tlv_ct_info;
75
static int hf_nflog_family;
76
static int hf_nflog_resid;
77
static int hf_nflog_tlv;
78
static int hf_nflog_tlv_gid;
79
static int hf_nflog_tlv_hook;
80
static int hf_nflog_tlv_hw_type;
81
static int hf_nflog_tlv_hwheader_len;
82
static int hf_nflog_tlv_hwprotocol;
83
static int hf_nflog_tlv_ifindex_indev;
84
static int hf_nflog_tlv_ifindex_outdev;
85
static int hf_nflog_tlv_ifindex_physindev;
86
static int hf_nflog_tlv_ifindex_physoutdev;
87
static int hf_nflog_tlv_length;
88
static int hf_nflog_tlv_prefix;
89
static int hf_nflog_tlv_timestamp;
90
static int hf_nflog_tlv_type;
91
static int hf_nflog_tlv_uid;
92
static int hf_nflog_tlv_unknown;
93
static int hf_nflog_version;
94
95
static int ett_nflog;
96
static int ett_nflog_tlv;
97
98
static dissector_handle_t ip_handle;
99
static dissector_handle_t ip6_handle;
100
static dissector_handle_t eth_handle;
101
static dissector_table_t ethertype_table;
102
static dissector_handle_t nflog_handle;
103
104
static bool eth_ip_layout_inside = true;
105
106
/* The TLV length and type are in the byte order of the host that
107
   captured the packet, given by tlv_encoding. */
108
static int
109
dissect_nflog_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned tlv_encoding)
110
0
{
111
0
    const int start_tlv_offset = 4;
112
113
0
    proto_tree *nflog_tree = NULL;
114
0
    proto_item *ti;
115
116
0
    unsigned offset = 0;
117
118
0
    struct {
119
0
        proto_tree *parent;
120
0
        tvbuff_t   *buffer;
121
0
    } payload = {NULL, NULL}, hwheader = {NULL, NULL};
122
123
0
    int pf;
124
0
    uint16_t hw_protocol = 0;
125
126
0
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "NFLOG");
127
0
    col_clear(pinfo->cinfo, COL_INFO);
128
129
0
    pf = tvb_get_uint8(tvb, 0);
130
131
    /* Header */
132
0
    if (proto_field_is_referenced(tree, proto_nflog)) {
133
0
        ti = proto_tree_add_item(tree, proto_nflog, tvb, 0, -1, ENC_NA);
134
0
        nflog_tree = proto_item_add_subtree(ti, ett_nflog);
135
136
0
        proto_tree_add_item(nflog_tree, hf_nflog_family, tvb, offset, 1, ENC_NA);
137
0
        offset += 1;
138
139
0
        proto_tree_add_item(nflog_tree, hf_nflog_version, tvb, offset, 1, ENC_NA);
140
0
        offset += 1;
141
142
0
        proto_tree_add_item(nflog_tree, hf_nflog_resid, tvb, offset, 2, ENC_BIG_ENDIAN);
143
        /*offset += 2;*/
144
0
    }
145
146
0
    offset = start_tlv_offset;
147
    /* TLVs */
148
0
    while (tvb_reported_length_remaining(tvb, offset) >= 4) {
149
0
        uint16_t tlv_len = tvb_get_uint16(tvb, offset + 0, tlv_encoding);
150
0
        uint16_t tlv_type;
151
0
        uint16_t value_len;
152
153
0
        proto_tree *tlv_tree;
154
155
        /* malformed */
156
0
        if (tlv_len < 4)
157
0
            return offset;
158
159
0
        value_len = tlv_len - 4;
160
0
        tlv_type = (tvb_get_uint16(tvb, offset + 2, tlv_encoding) & 0x7fff);
161
162
0
        bool handled = false;
163
0
        ti = proto_tree_add_bytes_format(nflog_tree, hf_nflog_tlv,
164
0
                         tvb, offset, tlv_len, NULL,
165
0
                         "TLV Type: %s (%u), Length: %u",
166
0
                         val_to_str_const(tlv_type, nflog_tlv_vals, "Unknown"),
167
0
                         tlv_type, tlv_len);
168
0
        tlv_tree = proto_item_add_subtree(ti, ett_nflog_tlv);
169
0
        proto_tree_add_item(tlv_tree, hf_nflog_tlv_length, tvb, offset + 0, 2, tlv_encoding);
170
0
        proto_tree_add_item(tlv_tree, hf_nflog_tlv_type, tvb, offset + 2, 2, tlv_encoding);
171
0
        switch (tlv_type) {
172
0
            case WS_NFULA_PACKET_HDR:
173
0
                if (value_len == 4) {
174
0
                    proto_tree_add_item(tlv_tree, hf_nflog_tlv_hwprotocol,
175
0
                                tvb, offset + 4, 2, ENC_BIG_ENDIAN);
176
0
                    proto_tree_add_item(tlv_tree, hf_nflog_tlv_hook,
177
0
                                tvb, offset + 6, 1, ENC_NA);
178
0
                    hw_protocol = tvb_get_ntohs(tvb, offset + 4);
179
0
                    handled = true;
180
0
                }
181
0
                break;
182
0
            case WS_NFULA_IFINDEX_INDEV:
183
0
                if (value_len == 4) {
184
0
                    proto_tree_add_item(tlv_tree, hf_nflog_tlv_ifindex_indev, tvb, offset + 4, value_len, ENC_BIG_ENDIAN);
185
0
                    handled = true;
186
0
                }
187
0
                break;
188
0
            case WS_NFULA_IFINDEX_OUTDEV:
189
0
                if (value_len == 4) {
190
0
                    proto_tree_add_item(tlv_tree, hf_nflog_tlv_ifindex_outdev, tvb, offset + 4, value_len, ENC_BIG_ENDIAN);
191
0
                    handled = true;
192
0
                }
193
0
                break;
194
0
            case WS_NFULA_IFINDEX_PHYSINDEV:
195
0
                if (value_len == 4) {
196
0
                    proto_tree_add_item(tlv_tree, hf_nflog_tlv_ifindex_physindev, tvb, offset + 4, value_len, ENC_BIG_ENDIAN);
197
0
                    handled = true;
198
0
                }
199
0
                break;
200
0
            case WS_NFULA_IFINDEX_PHYSOUTDEV:
201
0
                if (value_len == 4) {
202
0
                    proto_tree_add_item(tlv_tree, hf_nflog_tlv_ifindex_physoutdev, tvb, offset + 4, value_len, ENC_BIG_ENDIAN);
203
0
                    handled = true;
204
0
                }
205
0
                break;
206
0
            case WS_NFULA_PAYLOAD:
207
0
                payload.buffer = tvb_new_subset_length(tvb, offset + 4, value_len);
208
0
                payload.parent = tlv_tree;
209
0
                handled = true;
210
0
                break;
211
0
            case WS_NFULA_PREFIX:
212
0
                if (value_len >= 1) {
213
0
                    proto_tree_add_item(tlv_tree, hf_nflog_tlv_prefix,
214
0
                                tvb, offset + 4, value_len, ENC_ASCII);
215
0
                    handled = true;
216
0
                }
217
0
                break;
218
0
            case WS_NFULA_UID:
219
0
                if (value_len == 4) {
220
0
                    proto_tree_add_item(tlv_tree, hf_nflog_tlv_uid,
221
0
                                tvb, offset + 4, value_len, ENC_BIG_ENDIAN);
222
0
                    handled = true;
223
0
                }
224
0
                break;
225
0
            case WS_NFULA_GID:
226
0
                if (value_len == 4) {
227
0
                    proto_tree_add_item(tlv_tree, hf_nflog_tlv_gid,
228
0
                                tvb, offset + 4, value_len, ENC_BIG_ENDIAN);
229
0
                    handled = true;
230
0
                }
231
0
                break;
232
0
            case WS_NFULA_TIMESTAMP:
233
0
                if (value_len == 16) {
234
                    /*
235
                     * 64-bit seconds and 64-bit microseconds.
236
                     *
237
                     * XXX - add an "expert info" warning if the
238
                     * microseconds are >= 10^6?
239
                     */
240
0
                    proto_tree_add_item(tlv_tree, hf_nflog_tlv_timestamp,
241
0
                                tvb, offset + 4, value_len,
242
0
                                ENC_TIME_SECS_USECS|ENC_BIG_ENDIAN);
243
0
                    handled = true;
244
0
                }
245
0
                break;
246
0
            case WS_NFULA_HWTYPE:
247
0
                if (value_len == 2) {
248
0
                    proto_tree_add_item(tlv_tree, hf_nflog_tlv_hw_type,
249
0
                                tvb, offset + 4, value_len, ENC_BIG_ENDIAN);
250
0
                    handled = true;
251
0
                }
252
0
                break;
253
0
            case WS_NFULA_HWHEADER:
254
0
                hwheader.buffer = tvb_new_subset_length(tvb, offset + 4, value_len);
255
0
                hwheader.parent = tlv_tree;
256
0
                handled = true;
257
0
                break;
258
0
            case WS_NFULA_HWLEN:
259
0
                if (value_len == 2 || value_len == 4) {
260
0
                    proto_tree_add_item(tlv_tree, hf_nflog_tlv_hwheader_len,
261
0
                                tvb, offset + 4, value_len, ENC_BIG_ENDIAN);
262
0
                    handled = true;
263
0
                }
264
0
                break;
265
0
            case WS_NFULA_CT:
266
0
                if (value_len > 0) {
267
0
                    proto_tree_add_item(tlv_tree, hf_nflog_tlv_ct,
268
0
                                tvb, offset + 4, value_len, ENC_ASCII);
269
0
                    handled = true;
270
0
                }
271
0
                break;
272
0
            case WS_NFULA_CT_INFO:
273
0
                if (value_len == 4) {
274
0
                    proto_tree_add_item(tlv_tree, hf_nflog_tlv_ct_info,
275
0
                                tvb, offset + 4, value_len, ENC_BIG_ENDIAN);
276
0
                    handled = true;
277
0
                }
278
0
                break;
279
0
        }
280
281
0
        if (!handled)
282
0
                proto_tree_add_item(tlv_tree, hf_nflog_tlv_unknown,
283
0
                                    tvb, offset + 4, value_len, ENC_NA);
284
0
        offset += WS_ROUNDUP_4(tlv_len); /* next TLV aligned to 4B */
285
0
    }
286
287
0
    if (hwheader.buffer && eth_handle) {
288
0
        proto_tree *parent = eth_ip_layout_inside ? hwheader.parent : tree;
289
        // for others it is not guaranteed to be full, just for avoiding errors
290
0
        if (hw_protocol == ETHERTYPE_IP) {
291
0
            call_dissector(eth_handle, hwheader.buffer, pinfo, parent);
292
0
        } else {
293
0
            call_data_dissector(hwheader.buffer, pinfo, parent);
294
0
        }
295
0
    }
296
297
0
    if (payload.buffer) {
298
0
        proto_tree *parent = eth_ip_layout_inside ? payload.parent : tree;
299
0
               if (hw_protocol) {
300
0
            if (!dissector_try_uint(ethertype_table, hw_protocol, payload.buffer, pinfo, parent))
301
0
                call_data_dissector(payload.buffer, pinfo, parent);
302
0
        } else {
303
0
            switch (pf) {
304
                /* Note: NFPROTO_INET is not supposed to appear here, it is mapped
305
                 * to NFPROTO_IPV4 or NFPROTO_IPV6 */
306
0
                case WS_NFPROTO_IPV4:
307
0
                    call_dissector(ip_handle, payload.buffer, pinfo, parent);
308
0
                    break;
309
0
                case WS_NFPROTO_IPV6:
310
0
                    call_dissector(ip6_handle, payload.buffer, pinfo, parent);
311
0
                    break;
312
0
                default:
313
0
                    call_data_dissector(payload.buffer, pinfo, parent);
314
0
                    break;
315
0
            }
316
0
        }
317
0
    }
318
0
    return tvb_captured_length(tvb);
319
0
}
320
321
/* LINKTYPE_NFLOG: wiretap has byte-swapped the TLV length and type into
322
   the byte order of the reading host. */
323
static int
324
dissect_nflog(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
325
0
{
326
0
    return dissect_nflog_common(tvb, pinfo, tree, ENC_HOST_ENDIAN);
327
0
}
328
329
/* Called from Netlink (netlink-netfilter ULOG), with the Netlink byte
330
   order in the packet_netlink_data. */
331
static int
332
dissect_nflog_netlink(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
333
0
{
334
0
    struct packet_netlink_data *nl_data = (struct packet_netlink_data *)data;
335
336
0
    if (nl_data && nl_data->magic == PACKET_NETLINK_MAGIC)
337
0
        return dissect_nflog_common(tvb, pinfo, tree, (unsigned)nl_data->encoding);
338
0
    return dissect_nflog_common(tvb, pinfo, tree, ENC_HOST_ENDIAN);
339
0
}
340
341
void
342
proto_register_nflog(void)
343
16
{
344
16
    static hf_register_info hf[] = {
345
16
        { &hf_nflog_family,
346
16
            { "Family", "nflog.family",
347
16
              FT_UINT8, BASE_DEC, VALS(nfproto_family_vals), 0x00,
348
16
              NULL, HFILL }
349
16
        },
350
16
        { &hf_nflog_version,
351
16
            { "Version", "nflog.version",
352
16
              FT_UINT8, BASE_DEC, NULL, 0x00,
353
16
              NULL, HFILL }
354
16
        },
355
16
        { &hf_nflog_resid,
356
16
            { "Resource id", "nflog.res_id",
357
16
              FT_UINT16, BASE_DEC, NULL, 0x00,
358
16
              NULL, HFILL }
359
16
        },
360
16
        { &hf_nflog_tlv,
361
16
            { "TLV", "nflog.tlv",
362
16
              FT_BYTES, BASE_NONE, NULL, 0x00,
363
16
              NULL, HFILL }
364
16
        },
365
16
        { &hf_nflog_tlv_length,
366
16
            { "Length", "nflog.tlv_length",
367
16
              FT_UINT16, BASE_DEC, NULL, 0x00,
368
16
              "TLV Length", HFILL }
369
16
        },
370
16
        { &hf_nflog_tlv_type,
371
16
            { "Type", "nflog.tlv_type",
372
16
              FT_UINT16, BASE_DEC, VALS(nflog_tlv_vals), 0x7fff,
373
16
              "TLV Type", HFILL }
374
16
        },
375
16
        { &hf_nflog_tlv_hwprotocol,
376
16
            { "HW protocol", "nflog.protocol",
377
16
              FT_UINT16, BASE_HEX, VALS(etype_vals), 0x00,
378
16
              NULL, HFILL }
379
16
        },
380
16
        { &hf_nflog_tlv_hw_type,
381
16
            { "HW Type", "nflog.hwtype",
382
16
              FT_UINT16, BASE_DEC, VALS(arp_hrd_vals), 0x00,
383
16
              NULL, HFILL }
384
16
        },
385
16
        { &hf_nflog_tlv_hwheader_len,
386
16
            { "Hardware header length", "nflog.hwhdr_len",
387
16
              FT_UINT16, BASE_DEC, NULL, 0x00, NULL, HFILL }
388
16
        },
389
16
        { &hf_nflog_tlv_hook,
390
16
            { "Netfilter hook", "nflog.hook",
391
16
              FT_UINT8, BASE_DEC, VALS(netfilter_hooks_vals), 0x00,
392
16
              NULL, HFILL }
393
16
        },
394
16
        { &hf_nflog_tlv_ifindex_indev,
395
16
            { "IFINDEX_INDEV", "nflog.ifindex_indev",
396
16
              FT_UINT32, BASE_DEC, NULL, 0x00,
397
16
              NULL, HFILL }
398
16
        },
399
16
        { &hf_nflog_tlv_ifindex_outdev,
400
16
            { "IFINDEX_OUTDEV", "nflog.ifindex_outdev",
401
16
              FT_UINT32, BASE_DEC, NULL, 0x00,
402
16
              NULL, HFILL }
403
16
        },
404
16
        { &hf_nflog_tlv_ifindex_physindev,
405
16
            { "IFINDEX_PHYSINDEV", "nflog.ifindex_physindev",
406
16
              FT_UINT32, BASE_DEC, NULL, 0x00,
407
16
              NULL, HFILL }
408
16
        },
409
16
        { &hf_nflog_tlv_ifindex_physoutdev,
410
16
            { "IFINDEX_PHYSOUTDEV", "nflog.ifindex_physoutdev",
411
16
              FT_UINT32, BASE_DEC, NULL, 0x00,
412
16
              NULL, HFILL }
413
16
        },
414
16
        { &hf_nflog_tlv_prefix,
415
16
            { "Prefix", "nflog.prefix",
416
16
              FT_STRINGZ, BASE_NONE, NULL, 0x00,
417
16
              "TLV Prefix Value", HFILL }
418
16
        },
419
16
        { &hf_nflog_tlv_uid,
420
16
            { "UID", "nflog.uid",
421
16
              FT_INT32, BASE_DEC, NULL, 0x00,
422
16
              "TLV UID Value", HFILL }
423
16
        },
424
16
        { &hf_nflog_tlv_gid,
425
16
            { "GID", "nflog.gid",
426
16
              FT_INT32, BASE_DEC, NULL, 0x00,
427
16
              "TLV GID Value", HFILL }
428
16
        },
429
16
        { &hf_nflog_tlv_timestamp,
430
16
            { "Timestamp", "nflog.timestamp",
431
16
              FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x00,
432
16
              "TLV Timestamp Value", HFILL }
433
16
        },
434
16
        { &hf_nflog_tlv_ct_info,
435
16
            { "CT INFO", "nflog.ctinfo",
436
16
              FT_INT32, BASE_DEC, VALS(nfq_ctinfo_vals), 0x00,
437
16
              "TLV CT INFO", HFILL }
438
16
        },
439
16
        { &hf_nflog_tlv_ct,
440
16
            { "CT", "nflog.ct",
441
16
              FT_STRINGZ, BASE_NONE, NULL, 0x00,
442
16
              "TLV CT", HFILL }
443
16
        },
444
16
        { &hf_nflog_tlv_unknown,
445
16
            { "Value", "nflog.tlv_value",
446
16
              FT_BYTES, BASE_NONE, NULL, 0x00,
447
16
              "TLV Value", HFILL }
448
16
        },
449
16
    };
450
451
16
    static int *ett[] = {
452
16
        &ett_nflog,
453
16
        &ett_nflog_tlv
454
16
    };
455
456
16
    module_t *nflog_module;
457
458
16
    proto_nflog = proto_register_protocol("Linux Netfilter NFLOG", "NFLOG", "nflog");
459
16
    nflog_handle = register_dissector("nflog", dissect_nflog, proto_nflog);
460
16
    register_dissector_with_description("nflog_netlink", "NFLOG over Netlink", dissect_nflog_netlink, proto_nflog);
461
16
    proto_register_field_array(proto_nflog, hf, array_length(hf));
462
16
    proto_register_subtree_array(ett, array_length(ett));
463
    // user preferences
464
16
    nflog_module = prefs_register_protocol(proto_nflog, NULL);
465
16
    prefs_register_bool_preference(nflog_module, "eth_ip_layout_inside",
466
16
        "Place ip and eth inside",
467
16
        "Place ip and eth sections inside appropriate TLV's "
468
16
        "(payload and hw header respectively). If false, then place on top level",
469
16
        &eth_ip_layout_inside);
470
16
}
471
472
void
473
proto_reg_handoff_nflog(void)
474
16
{
475
16
    ip_handle  = find_dissector_add_dependency("ip", proto_nflog);
476
16
    ip6_handle = find_dissector_add_dependency("ipv6", proto_nflog);
477
16
    eth_handle = find_dissector_add_dependency("eth_header", proto_nflog);
478
479
16
    dissector_add_uint("wtap_encap", WTAP_ENCAP_NFLOG, nflog_handle);
480
16
    ethertype_table = find_dissector_table("ethertype");
481
16
}
482
483
/*
484
 * Editor modelines
485
 *
486
 * Local Variables:
487
 * c-basic-offset: 4
488
 * tab-width: 8
489
 * indent-tabs-mode: nil
490
 * End:
491
 *
492
 * ex: set shiftwidth=4 tabstop=8 expandtab:
493
 * :indentSize=4:tabSize=8:noTabs=true:
494
 */