Coverage Report

Created: 2026-01-09 07:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/plugins/redfish/fu-redfish-smbios.c
Line
Count
Source
1
/*
2
 * Copyright 2017 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
#include "config.h"
8
9
#include "fu-redfish-common.h"
10
#include "fu-redfish-smbios.h"
11
#include "fu-redfish-struct.h"
12
13
struct _FuRedfishSmbios {
14
  FuFirmware parent_instance;
15
  FuRedfishSmbiosInterfaceType interface_type;
16
  guint16 port;
17
  gchar *hostname;
18
  gchar *mac_addr;
19
  gchar *ip_addr;
20
  guint16 vid;
21
  guint16 pid;
22
};
23
24
1.26k
G_DEFINE_TYPE(FuRedfishSmbios, fu_redfish_smbios, FU_TYPE_FIRMWARE)
25
1.26k
26
1.26k
FuRedfishSmbiosInterfaceType
27
1.26k
fu_redfish_smbios_get_interface_type(FuRedfishSmbios *self)
28
1.26k
{
29
0
  return self->interface_type;
30
0
}
31
32
guint16
33
fu_redfish_smbios_get_port(FuRedfishSmbios *self)
34
0
{
35
0
  return self->port;
36
0
}
37
38
guint16
39
fu_redfish_smbios_get_vid(FuRedfishSmbios *self)
40
0
{
41
0
  return self->vid;
42
0
}
43
44
guint16
45
fu_redfish_smbios_get_pid(FuRedfishSmbios *self)
46
0
{
47
0
  return self->pid;
48
0
}
49
50
const gchar *
51
fu_redfish_smbios_get_hostname(FuRedfishSmbios *self)
52
0
{
53
0
  return self->hostname;
54
0
}
55
56
const gchar *
57
fu_redfish_smbios_get_mac_addr(FuRedfishSmbios *self)
58
0
{
59
0
  return self->mac_addr;
60
0
}
61
62
const gchar *
63
fu_redfish_smbios_get_ip_addr(FuRedfishSmbios *self)
64
0
{
65
0
  return self->ip_addr;
66
0
}
67
68
static void
69
fu_redfish_smbios_set_hostname(FuRedfishSmbios *self, const gchar *hostname)
70
1.06k
{
71
1.06k
  g_free(self->hostname);
72
1.06k
  self->hostname = g_strdup(hostname);
73
1.06k
}
74
75
static void
76
fu_redfish_smbios_set_mac_addr(FuRedfishSmbios *self, const gchar *mac_addr)
77
73
{
78
73
  g_free(self->mac_addr);
79
73
  self->mac_addr = g_strdup(mac_addr);
80
73
}
81
82
static void
83
fu_redfish_smbios_set_ip_addr(FuRedfishSmbios *self, const gchar *ip_addr)
84
1.53k
{
85
1.53k
  g_free(self->ip_addr);
86
1.53k
  self->ip_addr = g_strdup(ip_addr);
87
1.53k
}
88
89
static void
90
fu_redfish_smbios_set_port(FuRedfishSmbios *self, guint16 port)
91
1.54k
{
92
1.54k
  self->port = port;
93
1.54k
}
94
95
static void
96
fu_redfish_smbios_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
97
0
{
98
0
  FuRedfishSmbios *self = FU_REDFISH_SMBIOS(firmware);
99
0
  fu_xmlb_builder_insert_kv(bn,
100
0
          "interface_type",
101
0
          fu_redfish_smbios_interface_type_to_string(self->interface_type));
102
0
  fu_xmlb_builder_insert_kx(bn, "port", self->port);
103
0
  fu_xmlb_builder_insert_kv(bn, "hostname", self->hostname);
104
0
  fu_xmlb_builder_insert_kv(bn, "mac_addr", self->mac_addr);
105
0
  fu_xmlb_builder_insert_kv(bn, "ip_addr", self->ip_addr);
106
0
  fu_xmlb_builder_insert_kx(bn, "vid", self->vid);
107
0
  fu_xmlb_builder_insert_kx(bn, "pid", self->pid);
108
0
}
109
110
static gboolean
111
fu_redfish_smbios_build(FuFirmware *firmware, XbNode *n, GError **error)
112
0
{
113
0
  FuRedfishSmbios *self = FU_REDFISH_SMBIOS(firmware);
114
0
  const gchar *tmp;
115
0
  guint64 tmpu;
116
117
  /* optional properties */
118
0
  tmpu = xb_node_query_text_as_uint(n, "port", NULL);
119
0
  if (tmpu != G_MAXUINT64)
120
0
    fu_redfish_smbios_set_port(self, (guint16)tmpu);
121
0
  tmpu = xb_node_query_text_as_uint(n, "vid", NULL);
122
0
  if (tmpu != G_MAXUINT64)
123
0
    self->vid = (guint16)tmpu;
124
0
  tmpu = xb_node_query_text_as_uint(n, "pid", NULL);
125
0
  if (tmpu != G_MAXUINT64)
126
0
    self->pid = (guint16)tmpu;
127
0
  tmp = xb_node_query_text(n, "hostname", NULL);
128
0
  if (tmp != NULL)
129
0
    fu_redfish_smbios_set_hostname(self, tmp);
130
0
  tmp = xb_node_query_text(n, "mac_addr", NULL);
131
0
  if (tmp != NULL)
132
0
    fu_redfish_smbios_set_mac_addr(self, tmp);
133
0
  tmp = xb_node_query_text(n, "ip_addr", NULL);
134
0
  if (tmp != NULL)
135
0
    fu_redfish_smbios_set_ip_addr(self, tmp);
136
137
  /* success */
138
0
  return TRUE;
139
0
}
140
141
static gboolean
142
fu_redfish_smbios_parse_interface_data(FuRedfishSmbios *self,
143
               GInputStream *stream,
144
               gsize offset,
145
               GError **error)
146
161
{
147
161
  gsize offset_mac_addr = G_MAXSIZE;
148
161
  gsize offset_vid_pid = G_MAXSIZE;
149
161
  guint8 interface_type = 0x0;
150
151
  /* parse the data depending on the interface type */
152
161
  if (!fu_input_stream_read_u8(stream, offset, &interface_type, error))
153
0
    return FALSE;
154
161
  g_debug("interface_type: %s [0x%x]",
155
161
    fu_redfish_interface_type_to_string(interface_type),
156
161
    interface_type);
157
161
  offset++;
158
161
  switch (interface_type) {
159
10
  case FU_REDFISH_INTERFACE_TYPE_USB_NETWORK:
160
21
  case FU_REDFISH_INTERFACE_TYPE_PCI_NETWORK:
161
21
    offset_vid_pid = 0x00;
162
21
    break;
163
32
  case FU_REDFISH_INTERFACE_TYPE_USB_NETWORK_V2:
164
32
    offset_vid_pid = 0x01;
165
32
    offset_mac_addr = 0x06;
166
32
    break;
167
46
  case FU_REDFISH_INTERFACE_TYPE_PCI_NETWORK_V2:
168
46
    offset_vid_pid = 0x01;
169
46
    offset_mac_addr = 0x09;
170
46
    break;
171
62
  default:
172
62
    g_debug("unknown Network Interface");
173
62
    break;
174
161
  }
175
176
  /* MAC address */
177
161
  if (offset_mac_addr != G_MAXSIZE) {
178
78
    guint8 mac_addr[6] = {0x0};
179
78
    g_autofree gchar *mac_addr_str = NULL;
180
78
    if (!fu_input_stream_read_safe(stream,
181
78
                 mac_addr,
182
78
                 sizeof(mac_addr),
183
78
                 0x0,      /* dst */
184
78
                 offset + offset_mac_addr, /* src */
185
78
                 sizeof(mac_addr),
186
78
                 error))
187
5
      return FALSE;
188
73
    mac_addr_str = fu_redfish_common_buffer_to_mac(mac_addr);
189
73
    fu_redfish_smbios_set_mac_addr(self, mac_addr_str);
190
73
  }
191
192
  /* VID:PID */
193
156
  if (offset_vid_pid != G_MAXSIZE) {
194
94
    if (!fu_input_stream_read_u16(stream,
195
94
                offset + offset_vid_pid,
196
94
                &self->vid,
197
94
                G_LITTLE_ENDIAN,
198
94
                error))
199
0
      return FALSE;
200
94
    if (!fu_input_stream_read_u16(stream,
201
94
                offset + offset_vid_pid + 0x02,
202
94
                &self->pid,
203
94
                G_LITTLE_ENDIAN,
204
94
                error))
205
4
      return FALSE;
206
94
  }
207
208
  /* success */
209
152
  return TRUE;
210
156
}
211
212
static gboolean
213
fu_redfish_smbios_parse_over_ip(FuRedfishSmbios *self,
214
        GInputStream *stream,
215
        gsize offset,
216
        GError **error)
217
1.59k
{
218
1.59k
  guint8 hostname_length;
219
1.59k
  guint8 service_ip_address_format;
220
1.59k
  g_autoptr(FuStructRedfishProtocolOverIp) st = NULL;
221
222
  /* port + IP address */
223
1.59k
  st = fu_struct_redfish_protocol_over_ip_parse_stream(stream, offset, error);
224
1.59k
  if (st == NULL)
225
55
    return FALSE;
226
1.54k
  fu_redfish_smbios_set_port(self,
227
1.54k
           fu_struct_redfish_protocol_over_ip_get_service_ip_port(st));
228
1.54k
  service_ip_address_format =
229
1.54k
      fu_struct_redfish_protocol_over_ip_get_service_ip_address_format(st);
230
1.54k
  if (service_ip_address_format == FU_REDFISH_IP_ADDRESS_FORMAT_V4) {
231
1.08k
    const guint8 *ip_address =
232
1.08k
        fu_struct_redfish_protocol_over_ip_get_service_ip_address(st, NULL);
233
1.08k
    g_autofree gchar *tmp = fu_redfish_common_buffer_to_ipv4(ip_address);
234
1.08k
    fu_redfish_smbios_set_ip_addr(self, tmp);
235
1.08k
  } else if (service_ip_address_format == FU_REDFISH_IP_ADDRESS_FORMAT_V6) {
236
452
    const guint8 *ip_address =
237
452
        fu_struct_redfish_protocol_over_ip_get_service_ip_address(st, NULL);
238
452
    g_autofree gchar *tmp = fu_redfish_common_buffer_to_ipv6(ip_address);
239
452
    fu_redfish_smbios_set_ip_addr(self, tmp);
240
452
  } else {
241
8
    g_set_error_literal(error,
242
8
            FWUPD_ERROR,
243
8
            FWUPD_ERROR_INVALID_FILE,
244
8
            "address format is invalid");
245
8
    return FALSE;
246
8
  }
247
248
  /* hostname */
249
1.53k
  hostname_length = fu_struct_redfish_protocol_over_ip_get_service_hostname_len(st);
250
1.53k
  if (hostname_length > 0) {
251
1.09k
    g_autofree gchar *hostname = g_malloc0(hostname_length + 1);
252
1.09k
    if (!fu_input_stream_read_safe(stream,
253
1.09k
                 (guint8 *)hostname,
254
1.09k
                 hostname_length,
255
1.09k
                 0x0,         /* dst */
256
1.09k
                 offset + st->buf->len, /* seek */
257
1.09k
                 hostname_length,
258
1.09k
                 error))
259
28
      return FALSE;
260
1.06k
    fu_redfish_smbios_set_hostname(self, hostname);
261
1.06k
  }
262
263
  /* success */
264
1.50k
  return TRUE;
265
1.53k
}
266
267
static gboolean
268
fu_redfish_smbios_parse(FuFirmware *firmware,
269
      GInputStream *stream,
270
      FuFirmwareParseFlags flags,
271
      GError **error)
272
404
{
273
404
  FuRedfishSmbios *self = FU_REDFISH_SMBIOS(firmware);
274
404
  gsize offset = 0;
275
404
  gsize streamsz = 0;
276
404
  g_autoptr(FuStructRedfishSmbiosType42) st = NULL;
277
278
  /* check size */
279
404
  if (!fu_input_stream_size(stream, &streamsz, error))
280
0
    return FALSE;
281
404
  if (streamsz < 0x09) {
282
13
    g_set_error(error,
283
13
          FWUPD_ERROR,
284
13
          FWUPD_ERROR_INVALID_FILE,
285
13
          "SMBIOS entry too small: %" G_GSIZE_FORMAT,
286
13
          streamsz);
287
13
    return FALSE;
288
13
  }
289
290
  /* parse */
291
391
  st = fu_struct_redfish_smbios_type42_parse_stream(stream, offset, error);
292
391
  if (st == NULL)
293
24
    return FALSE;
294
295
  /* check length */
296
367
  if (fu_struct_redfish_smbios_type42_get_length(st) != streamsz) {
297
78
    g_set_error(error,
298
78
          FWUPD_ERROR,
299
78
          FWUPD_ERROR_INVALID_FILE,
300
78
          "size of table 0x%x does not match binary 0x%x",
301
78
          fu_struct_redfish_smbios_type42_get_length(st),
302
78
          (guint)streamsz);
303
78
    return FALSE;
304
78
  }
305
306
  /* check length */
307
289
  offset += FU_STRUCT_REDFISH_SMBIOS_TYPE42_SIZE;
308
289
  if (fu_struct_redfish_smbios_type42_get_data_length(st) > 0) {
309
161
    if (!fu_redfish_smbios_parse_interface_data(self, stream, offset, error))
310
9
      return FALSE;
311
161
  }
312
280
  offset += fu_struct_redfish_smbios_type42_get_data_length(st);
313
314
  /* parse protocol records */
315
280
  self->interface_type = fu_struct_redfish_smbios_type42_get_interface_type(st);
316
280
  if (self->interface_type == FU_REDFISH_SMBIOS_INTERFACE_TYPE_NETWORK) {
317
264
    guint8 protocol_rcds = 0;
318
264
    if (!fu_input_stream_read_u8(stream, offset, &protocol_rcds, error))
319
5
      return FALSE;
320
259
    offset += 1;
321
259
    g_debug("protocol_rcds: %u", protocol_rcds);
322
8.26k
    for (guint i = 0; i < protocol_rcds; i++) {
323
8.22k
      guint8 protocol_id = 0;
324
8.22k
      guint8 protocol_sz = 0;
325
8.22k
      if (!fu_input_stream_read_u8(stream, offset, &protocol_id, error))
326
58
        return FALSE;
327
8.16k
      if (!fu_input_stream_read_u8(stream, offset + 0x1, &protocol_sz, error))
328
69
        return FALSE;
329
8.09k
      if (protocol_id == REDFISH_PROTOCOL_REDFISH_OVER_IP) {
330
1.59k
        if (!fu_redfish_smbios_parse_over_ip(self,
331
1.59k
                     stream,
332
1.59k
                     offset + 0x2,
333
1.59k
                     error))
334
91
          return FALSE;
335
6.49k
      } else {
336
6.49k
        g_debug("ignoring protocol ID 0x%02x", protocol_id);
337
6.49k
      }
338
8.00k
      offset += protocol_sz + 1;
339
8.00k
    }
340
259
  }
341
342
  /* success */
343
57
  return TRUE;
344
280
}
345
346
static GByteArray *
347
fu_redfish_smbios_write(FuFirmware *firmware, GError **error)
348
57
{
349
57
  FuRedfishSmbios *self = FU_REDFISH_SMBIOS(firmware);
350
57
  gsize hostname_sz = 0;
351
57
  g_autoptr(FuStructRedfishProtocolOverIp) st = fu_struct_redfish_protocol_over_ip_new();
352
57
  g_autoptr(GByteArray) buf = g_byte_array_new();
353
354
57
  if (self->hostname != NULL)
355
24
    hostname_sz = strlen(self->hostname);
356
57
  fu_byte_array_append_uint8(buf, REDFISH_SMBIOS_TABLE_TYPE);
357
57
  fu_byte_array_append_uint8(buf, 0x6D + hostname_sz);     /* length */
358
57
  fu_byte_array_append_uint16(buf, 0x1234, G_LITTLE_ENDIAN); /* handle */
359
57
  fu_byte_array_append_uint8(buf, FU_REDFISH_SMBIOS_INTERFACE_TYPE_NETWORK);
360
57
  fu_byte_array_append_uint8(buf, 0x09);          /* iface datalen */
361
57
  fu_byte_array_append_uint8(buf, FU_REDFISH_INTERFACE_TYPE_USB_NETWORK); /* iface */
362
57
  fu_byte_array_append_uint16(buf, self->vid, G_LITTLE_ENDIAN);    /* iface:VID */
363
57
  fu_byte_array_append_uint16(buf, self->pid, G_LITTLE_ENDIAN);    /* iface:PID */
364
57
  fu_byte_array_append_uint8(buf, 0x02);          /* iface:serialsz */
365
57
  fu_byte_array_append_uint8(buf, 0x03);          /* iType */
366
57
  fu_byte_array_append_uint8(buf, 'S');         /* iface:serial */
367
57
  fu_byte_array_append_uint8(buf, 'n');         /* iface:serial */
368
57
  fu_byte_array_append_uint8(buf, 0x1); /* nr protocol rcds */
369
370
  /* protocol record */
371
57
  fu_byte_array_append_uint8(buf, REDFISH_PROTOCOL_REDFISH_OVER_IP);
372
57
  fu_byte_array_append_uint8(buf, st->buf->len + hostname_sz);
373
374
57
  if (self->hostname != NULL)
375
24
    hostname_sz = strlen(self->hostname);
376
57
  fu_struct_redfish_protocol_over_ip_set_service_ip_port(st, self->port);
377
57
  fu_struct_redfish_protocol_over_ip_set_service_ip_address_format(
378
57
      st,
379
57
      FU_REDFISH_IP_ADDRESS_FORMAT_V4);
380
57
  fu_struct_redfish_protocol_over_ip_set_service_ip_assignment_type(
381
57
      st,
382
57
      FU_REDFISH_IP_ASSIGNMENT_TYPE_STATIC);
383
57
  fu_struct_redfish_protocol_over_ip_set_service_hostname_len(st, hostname_sz);
384
57
  g_byte_array_append(buf, st->buf->data, st->buf->len);
385
57
  if (hostname_sz > 0)
386
18
    g_byte_array_append(buf, (guint8 *)self->hostname, hostname_sz);
387
57
  return g_steal_pointer(&buf);
388
57
}
389
390
static void
391
fu_redfish_smbios_finalize(GObject *object)
392
404
{
393
404
  FuRedfishSmbios *self = FU_REDFISH_SMBIOS(object);
394
404
  g_free(self->hostname);
395
404
  g_free(self->mac_addr);
396
404
  g_free(self->ip_addr);
397
404
  G_OBJECT_CLASS(fu_redfish_smbios_parent_class)->finalize(object);
398
404
}
399
400
static void
401
fu_redfish_smbios_init(FuRedfishSmbios *self)
402
404
{
403
404
}
404
405
static void
406
fu_redfish_smbios_class_init(FuRedfishSmbiosClass *klass)
407
1
{
408
1
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
409
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
410
1
  object_class->finalize = fu_redfish_smbios_finalize;
411
1
  firmware_class->parse = fu_redfish_smbios_parse;
412
1
  firmware_class->write = fu_redfish_smbios_write;
413
1
  firmware_class->build = fu_redfish_smbios_build;
414
1
  firmware_class->export = fu_redfish_smbios_export;
415
1
}
416
417
FuRedfishSmbios *
418
fu_redfish_smbios_new(void)
419
0
{
420
0
  return FU_REDFISH_SMBIOS(g_object_new(FU_TYPE_REDFISH_SMBIOS, NULL));
421
0
}