summaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-sinecap.c
blob: 4bdc85f6750d1e60a42c946e61406b2a73a16ed6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
/* packet-sinecap.c
 *
 * Author:      Nikolas Koesling, 2023 (nikolas@koesling.info)
 * Description: Wireshark dissector for the SINEC AP protocol according to
 *      https://cache.industry.siemens.com/dl/files/274/22090274/att_83836/v1/447_840_840C_880_Computer_Link_General_Description.pdf
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include "config.h"
#include <epan/packet.h>

#define PROTO_TAG_AP "SINEC-AP"

/* Min. telegram length for heuristic check */
#define TXP_MIN_TELEGRAM_LENGTH 22

/* Wireshark ID of the AP1 protocol */
static gint proto_ap = -1;

static gint hf_ap_protoid = -1;
static gint hf_ap_mpxadr = -1;
static gint hf_ap_comcls = -1;
static gint hf_ap_comcod = -1;
static gint hf_ap_modfr1 = -1;
static gint hf_ap_modfr2 = -1;
static gint hf_ap_errcls = -1;
static gint hf_ap_errcod = -1;
static gint hf_ap_rosctr = -1;
static gint hf_ap_sgsqnr = -1;
static gint hf_ap_tactid = -1;
static gint hf_ap_tasqnr = -1;
static gint hf_ap_spare = -1;
static gint hf_ap_pduref = -1;
static gint hf_ap_pduid = -1;
static gint hf_ap_pdulg = -1;
static gint hf_ap_parlg = -1;
static gint hf_ap_datlg = -1;

static gint ett_ap = -1;

static heur_dissector_list_t ap_heur_subdissector_list;

static const value_string vs_comcls[] = {
        {0x0, "ACK without data"},
        {0x4, "Serial transfer"},
        {0, NULL}
};

static const value_string vs_protid[] = {
        {0x0, "SINEC AP 1.0"},
        {0, NULL}
};

static gboolean
dissect_ap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, void *data _U_)
{
    /*----------------- Heuristic Checks - Begin */
    /* 1) check for minimum length */
    if (tvb_captured_length(tvb) < TXP_MIN_TELEGRAM_LENGTH)
        return FALSE;

    /* 2) protocol id == 0 */
    if (tvb_get_guint8(tvb, 0) != 0)
        return FALSE;
    /*----------------- Heuristic Checks - End */

    col_set_str(pinfo->cinfo, COL_PROTOCOL, PROTO_TAG_AP);
    col_clear(pinfo->cinfo, COL_INFO);

    guint8 comcls = tvb_get_guint8(tvb, 2);

    gint offset = 16;
    guint16 pdulg = tvb_get_guint16(tvb, offset, ENC_BIG_ENDIAN);
    offset += 4;

    guint16 datlg = tvb_get_guint16(tvb, offset, ENC_BIG_ENDIAN);
    offset += 2;

    ws_assert(offset == 22);

    /* check pdu and data length */
    if (pdulg != tvb_captured_length(tvb))
        return FALSE;
    if (datlg != tvb_captured_length(tvb) - 22)
        return FALSE;

    switch (comcls) {
        case 0x0: {
            // ack without data
            col_append_fstr(pinfo->cinfo, COL_INFO, "%s", "ACK without data");
            break;
        }
        case 0x4: {
            // serial transfer
            col_append_fstr(pinfo->cinfo, COL_INFO, "%s", "Serial transfer");
            break;
        }
        default:
            col_append_fstr(pinfo->cinfo, COL_INFO, "%s", "UNKNOWN command class");
    }

    proto_item *ap_item = proto_tree_add_item(tree, proto_ap, tvb, 0, -1, ENC_NA);
    proto_tree *ap_tree = proto_item_add_subtree(ap_item, ett_ap);

    offset = 0;
    proto_tree_add_item(ap_tree, hf_ap_protoid, tvb, offset++, 1, ENC_BIG_ENDIAN);
    proto_tree_add_item(ap_tree, hf_ap_mpxadr, tvb, offset++, 1, ENC_BIG_ENDIAN);
    proto_tree_add_item(ap_tree, hf_ap_comcls, tvb, offset++, 1, ENC_BIG_ENDIAN);
    proto_tree_add_item(ap_tree, hf_ap_comcod, tvb, offset++, 1, ENC_BIG_ENDIAN);

    switch (comcls) {
        case 0x0: {
            // ack without data
            proto_tree_add_item(ap_tree, hf_ap_errcls, tvb, offset++, 1, ENC_BIG_ENDIAN);
            proto_tree_add_item(ap_tree, hf_ap_errcod, tvb, offset++, 1, ENC_BIG_ENDIAN);
            break;
        }
        case 0x4: {
            // serial transfer
            proto_tree_add_item(ap_tree, hf_ap_modfr1, tvb, offset++, 1, ENC_BIG_ENDIAN);
            proto_tree_add_item(ap_tree, hf_ap_modfr2, tvb, offset++, 1, ENC_BIG_ENDIAN);
            break;
        }
        default:
            proto_tree_add_item(ap_tree, hf_ap_modfr1, tvb, offset++, 1, ENC_BIG_ENDIAN);
            proto_tree_add_item(ap_tree, hf_ap_modfr2, tvb, offset++, 1, ENC_BIG_ENDIAN);
    }

    proto_tree_add_item(ap_tree, hf_ap_rosctr, tvb, offset++, 1, ENC_BIG_ENDIAN);
    proto_tree_add_item(ap_tree, hf_ap_sgsqnr, tvb, offset++, 1, ENC_BIG_ENDIAN);
    proto_tree_add_item(ap_tree, hf_ap_tactid, tvb, offset++, 1, ENC_BIG_ENDIAN);
    proto_tree_add_item(ap_tree, hf_ap_tasqnr, tvb, offset++, 1, ENC_BIG_ENDIAN);
    proto_tree_add_item(ap_tree, hf_ap_spare, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(ap_tree, hf_ap_pduref, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(ap_tree, hf_ap_pduid, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(ap_tree, hf_ap_pdulg, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(ap_tree, hf_ap_parlg, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(ap_tree, hf_ap_datlg, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;

    ws_assert(offset == 22);

    if (tvb_reported_length_remaining(tvb, offset) > 0) {
        struct tvbuff *next_tvb = tvb_new_subset_remaining(tvb,  offset);
        heur_dtbl_entry_t *hdtbl_entry;
        if (!dissector_try_heuristic(ap_heur_subdissector_list, next_tvb, pinfo, tree, &hdtbl_entry, NULL)) {
            call_data_dissector(next_tvb, pinfo, tree);
        }
    }

    return TRUE;
}

void
proto_register_ap(void)
{
    static hf_register_info hf[] = {
            {&hf_ap_protoid, {"PROTID", "sinecap.protid", FT_UINT8, BASE_HEX, VALS(vs_protid), 0x0, "Protocol version", HFILL}},
            {&hf_ap_mpxadr, {"MPXADR", "sinecap.mpxadr", FT_UINT8, BASE_HEX, NULL, 0x0, "Multiplex address", HFILL}},
            {&hf_ap_comcls, {"COMCLS", "sinecap.comcls", FT_UINT8, BASE_HEX, VALS(vs_comcls), 0x0, "Command class", HFILL}},
            {&hf_ap_comcod, {"COMCOD", "sinecap.comcod", FT_UINT8, BASE_HEX, NULL, 0x0, "Command code", HFILL}},
            {&hf_ap_modfr1, {"MODFR1", "sinecap.modfr1", FT_UINT8, BASE_HEX, NULL, 0x0, "Modifier 1", HFILL}},
            {&hf_ap_errcls, {"ERRCLS", "sinecap.errcls", FT_UINT8, BASE_HEX, NULL, 0x0, "Error class", HFILL}},
            {&hf_ap_modfr2, {"MODFR2", "sinecap.modfr2", FT_UINT8, BASE_HEX, NULL, 0x0, "Modifier 2", HFILL}},
            {&hf_ap_errcod, {"ERRCOD", "sinecap.errcod", FT_UINT8, BASE_HEX, NULL, 0x0, "Error code", HFILL}},
            {&hf_ap_rosctr, {"ROSCTR", "sinecap.rosctr", FT_UINT8, BASE_HEX, NULL, 0x0, "Remote operating service", HFILL}},
            {&hf_ap_sgsqnr, {"SGSQNR", "sinecap.sgsqnr", FT_UINT8, BASE_HEX_DEC, NULL, 0x0, "Segment sequence number", HFILL}},
            {&hf_ap_tactid, {"TACTID", "sinecap.tactid", FT_UINT8, BASE_HEX, NULL, 0x0, "Transaction identifier", HFILL}},
            {&hf_ap_tasqnr, {"TASQNR", "sinecap.tasqnr", FT_UINT8, BASE_HEX, NULL, 0x0, "Transaction sequence number", HFILL}},
            {&hf_ap_spare, {"SPARE", "sinecap.spare", FT_UINT16, BASE_HEX, NULL, 0x0, "Free space", HFILL}},
            {&hf_ap_pduref, {"PDUREF", "sinecap.pduref", FT_UINT16, BASE_HEX, NULL, 0x0, "Protocol Data Unit reference", HFILL}},
            {&hf_ap_pduid, {"PDUID", "sinecap.pduid", FT_UINT16, BASE_HEX, NULL, 0x0, "Protocol Data Unit identifier", HFILL}},
            {&hf_ap_pdulg, {"PDULG", "sinecap.pdulg", FT_UINT16, BASE_HEX_DEC, NULL, 0x0, "Protocol Data Unit length", HFILL}},
            {&hf_ap_parlg, {"PARLG", "sinecap.parlg", FT_UINT16, BASE_HEX_DEC, NULL, 0x0, "Parameter length", HFILL}},
            {&hf_ap_datlg, {"DATLG", "sinecap.datlg", FT_UINT16, BASE_HEX_DEC, NULL, 0x0, "Data length", HFILL}},
    };

    proto_ap = proto_register_protocol (
            "SINEC AP Telegram",    /* name        */
            "SINEC AP",             /* short name  */
            "sinecap"               /* filter_name */
    );

    static gint *ett[] = {
            &ett_ap,
    };

    proto_register_field_array(proto_ap, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length (ett));
    ap_heur_subdissector_list = register_heur_dissector_list("sinecap", proto_ap);
}

void
proto_reg_handoff_ap(void)
{
    heur_dissector_add("cotp", dissect_ap, "SINEC AP Telegram over COTP", "sinecap", proto_ap, HEURISTIC_ENABLE);
    heur_dissector_add("cotp_is", dissect_ap, "SINEC AP Telegram over COTP", "sinecap_is", proto_ap, HEURISTIC_ENABLE);
}