Coverage Report

Created: 2026-05-30 06:50

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.51k
G_DEFINE_TYPE(FuRedfishSmbios, fu_redfish_smbios, FU_TYPE_FIRMWARE)
25
1.51k
26
1.51k
FuRedfishSmbiosInterfaceType
27
1.51k
fu_redfish_smbios_get_interface_type(FuRedfishSmbios *self)
28
1.51k
{
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.10k
{
71
1.10k
  g_free(self->hostname);
72
1.10k
  self->hostname = g_strdup(hostname);
73
1.10k
}
74
75
static void
76
fu_redfish_smbios_set_mac_addr(FuRedfishSmbios *self, const gchar *mac_addr)
77
71
{
78
71
  g_free(self->mac_addr);
79
71
  self->mac_addr = g_strdup(mac_addr);
80
71
}
81
82
static void
83
fu_redfish_smbios_set_ip_addr(FuRedfishSmbios *self, const gchar *ip_addr)
84
1.59k
{
85
1.59k
  g_free(self->ip_addr);
86
1.59k
  self->ip_addr = g_strdup(ip_addr);
87
1.59k
}
88
89
static void
90
fu_redfish_smbios_set_port(FuRedfishSmbios *self, guint16 port)
91
1.60k
{
92
1.60k
  self->port = port;
93
1.60k
}
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
172
{
147
172
  gsize offset_mac_addr = G_MAXSIZE;
148
172
  gsize offset_vid_pid = G_MAXSIZE;
149
172
  guint8 interface_type = 0x0;
150
151
  /* parse the data depending on the interface type */
152
172
  if (!fu_input_stream_read_u8(stream, offset, &interface_type, error))
153
0
    return FALSE;
154
172
  g_debug("interface_type: %s [0x%x]",
155
172
    fu_redfish_interface_type_to_string(interface_type),
156
172
    interface_type);
157
172
  offset++;
158
172
  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
43
  case FU_REDFISH_INTERFACE_TYPE_USB_NETWORK_V2:
164
43
    offset_vid_pid = 0x01;
165
43
    offset_mac_addr = 0x06;
166
43
    break;
167
33
  case FU_REDFISH_INTERFACE_TYPE_PCI_NETWORK_V2:
168
33
    offset_vid_pid = 0x01;
169
33
    offset_mac_addr = 0x09;
170
33
    break;
171
75
  default:
172
75
    g_debug("unknown Network Interface");
173
75
    break;
174
172
  }
175
176
  /* MAC address */
177
172
  if (offset_mac_addr != G_MAXSIZE) {
178
76
    guint8 mac_addr[6] = {0x0};
179
76
    g_autofree gchar *mac_addr_str = NULL;
180
76
    if (!fu_input_stream_read_safe(stream,
181
76
                 mac_addr,
182
76
                 sizeof(mac_addr),
183
76
                 0x0,      /* dst */
184
76
                 offset + offset_mac_addr, /* src */
185
76
                 sizeof(mac_addr),
186
76
                 error))
187
5
      return FALSE;
188
71
    mac_addr_str = fu_redfish_common_buffer_to_mac(mac_addr);
189
71
    fu_redfish_smbios_set_mac_addr(self, mac_addr_str);
190
71
  }
191
192
  /* VID:PID */
193
167
  if (offset_vid_pid != G_MAXSIZE) {
194
92
    if (!fu_input_stream_read_u16(stream,
195
92
                offset + offset_vid_pid,
196
92
                &self->vid,
197
92
                G_LITTLE_ENDIAN,
198
92
                error))
199
0
      return FALSE;
200
92
    if (!fu_input_stream_read_u16(stream,
201
92
                offset + offset_vid_pid + 0x02,
202
92
                &self->pid,
203
92
                G_LITTLE_ENDIAN,
204
92
                error))
205
4
      return FALSE;
206
92
  }
207
208
  /* success */
209
163
  return TRUE;
210
167
}
211
212
static gboolean
213
fu_redfish_smbios_parse_over_ip(FuRedfishSmbios *self,
214
        GInputStream *stream,
215
        gsize offset,
216
        GError **error)
217
1.66k
{
218
1.66k
  guint8 hostname_length;
219
1.66k
  guint8 service_ip_address_format;
220
1.66k
  g_autoptr(FuStructRedfishProtocolOverIp) st = NULL;
221
222
  /* port + IP address */
223
1.66k
  st = fu_struct_redfish_protocol_over_ip_parse_stream(stream, offset, error);
224
1.66k
  if (st == NULL)
225
54
    return FALSE;
226
1.60k
  fu_redfish_smbios_set_port(self,
227
1.60k
           fu_struct_redfish_protocol_over_ip_get_service_ip_port(st));
228
1.60k
  service_ip_address_format =
229
1.60k
      fu_struct_redfish_protocol_over_ip_get_service_ip_address_format(st);
230
1.60k
  if (service_ip_address_format == FU_REDFISH_IP_ADDRESS_FORMAT_V4) {
231
1.14k
    const guint8 *ip_address =
232
1.14k
        fu_struct_redfish_protocol_over_ip_get_service_ip_address(st, NULL);
233
1.14k
    g_autofree gchar *tmp = fu_redfish_common_buffer_to_ipv4(ip_address);
234
1.14k
    fu_redfish_smbios_set_ip_addr(self, tmp);
235
1.14k
  } else if (service_ip_address_format == FU_REDFISH_IP_ADDRESS_FORMAT_V6) {
236
459
    const guint8 *ip_address =
237
459
        fu_struct_redfish_protocol_over_ip_get_service_ip_address(st, NULL);
238
459
    g_autofree gchar *tmp = fu_redfish_common_buffer_to_ipv6(ip_address);
239
459
    fu_redfish_smbios_set_ip_addr(self, tmp);
240
459
  } else {
241
9
    g_set_error_literal(error,
242
9
            FWUPD_ERROR,
243
9
            FWUPD_ERROR_INVALID_FILE,
244
9
            "address format is invalid");
245
9
    return FALSE;
246
9
  }
247
248
  /* hostname */
249
1.59k
  hostname_length = fu_struct_redfish_protocol_over_ip_get_service_hostname_len(st);
250
1.59k
  if (hostname_length > 0) {
251
1.13k
    g_autofree gchar *hostname = g_malloc0(hostname_length + 1);
252
1.13k
    if (!fu_input_stream_read_safe(stream,
253
1.13k
                 (guint8 *)hostname,
254
1.13k
                 hostname_length,
255
1.13k
                 0x0,         /* dst */
256
1.13k
                 offset + st->buf->len, /* seek */
257
1.13k
                 hostname_length,
258
1.13k
                 error))
259
32
      return FALSE;
260
1.10k
    fu_redfish_smbios_set_hostname(self, hostname);
261
1.10k
  }
262
263
  /* success */
264
1.56k
  return TRUE;
265
1.59k
}
266
267
static gboolean
268
fu_redfish_smbios_parse(FuFirmware *firmware,
269
      GInputStream *stream,
270
      FuFirmwareParseFlags flags,
271
      GError **error)
272
483
{
273
483
  FuRedfishSmbios *self = FU_REDFISH_SMBIOS(firmware);
274
483
  gsize offset = 0;
275
483
  gsize streamsz = 0;
276
483
  g_autoptr(FuStructRedfishSmbiosType42) st = NULL;
277
278
  /* check size */
279
483
  if (!fu_input_stream_size(stream, &streamsz, error))
280
0
    return FALSE;
281
483
  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
470
  st = fu_struct_redfish_smbios_type42_parse_stream(stream, offset, error);
292
470
  if (st == NULL)
293
73
    return FALSE;
294
295
  /* check length */
296
397
  if (fu_struct_redfish_smbios_type42_get_length(st) != streamsz) {
297
84
    g_set_error(error,
298
84
          FWUPD_ERROR,
299
84
          FWUPD_ERROR_INVALID_FILE,
300
84
          "size of table 0x%x does not match binary 0x%x",
301
84
          fu_struct_redfish_smbios_type42_get_length(st),
302
84
          (guint)streamsz);
303
84
    return FALSE;
304
84
  }
305
306
  /* check length */
307
313
  if (!fu_size_checked_inc(&offset, FU_STRUCT_REDFISH_SMBIOS_TYPE42_SIZE, error)) {
308
0
    g_prefix_error_literal(error, "header offset overflow: ");
309
0
    return FALSE;
310
0
  }
311
313
  if (fu_struct_redfish_smbios_type42_get_data_length(st) > 0) {
312
172
    if (!fu_redfish_smbios_parse_interface_data(self, stream, offset, error))
313
9
      return FALSE;
314
172
  }
315
304
  if (!fu_size_checked_inc(&offset,
316
304
         fu_struct_redfish_smbios_type42_get_data_length(st),
317
304
         error))
318
0
    return FALSE;
319
320
  /* parse protocol records */
321
304
  self->interface_type = fu_struct_redfish_smbios_type42_get_interface_type(st);
322
304
  if (self->interface_type == FU_REDFISH_SMBIOS_INTERFACE_TYPE_NETWORK) {
323
285
    guint8 protocol_rcds = 0;
324
285
    if (!fu_input_stream_read_u8(stream, offset, &protocol_rcds, error))
325
7
      return FALSE;
326
278
    if (!fu_size_checked_inc(&offset, 1, error)) {
327
0
      g_prefix_error_literal(error, "protocol records offset overflow: ");
328
0
      return FALSE;
329
0
    }
330
278
    g_debug("protocol_rcds: %u", protocol_rcds);
331
8.19k
    for (guint i = 0; i < protocol_rcds; i++) {
332
8.15k
      guint8 protocol_id = 0;
333
8.15k
      guint8 protocol_sz = 0;
334
8.15k
      if (!fu_input_stream_read_u8(stream, offset, &protocol_id, error))
335
69
        return FALSE;
336
8.08k
      if (!fu_input_stream_read_u8(stream, offset + 0x1, &protocol_sz, error))
337
72
        return FALSE;
338
8.01k
      if (protocol_id == REDFISH_PROTOCOL_REDFISH_OVER_IP) {
339
1.66k
        if (!fu_redfish_smbios_parse_over_ip(self,
340
1.66k
                     stream,
341
1.66k
                     offset + 0x2,
342
1.66k
                     error))
343
95
          return FALSE;
344
6.35k
      } else {
345
6.35k
        g_debug("ignoring protocol ID 0x%02x", protocol_id);
346
6.35k
      }
347
7.92k
      if (!fu_size_checked_inc(&offset, protocol_sz + 1, error))
348
0
        return FALSE;
349
7.92k
    }
350
278
  }
351
352
  /* success */
353
61
  return TRUE;
354
304
}
355
356
static GByteArray *
357
fu_redfish_smbios_write(FuFirmware *firmware, GError **error)
358
61
{
359
61
  FuRedfishSmbios *self = FU_REDFISH_SMBIOS(firmware);
360
61
  gsize hostname_sz = 0;
361
61
  g_autoptr(FuStructRedfishProtocolOverIp) st = fu_struct_redfish_protocol_over_ip_new();
362
61
  g_autoptr(GByteArray) buf = g_byte_array_new();
363
364
61
  if (self->hostname != NULL)
365
25
    hostname_sz = strlen(self->hostname);
366
61
  fu_byte_array_append_uint8(buf, REDFISH_SMBIOS_TABLE_TYPE);
367
61
  fu_byte_array_append_uint8(buf, 0x6D + hostname_sz);     /* length */
368
61
  fu_byte_array_append_uint16(buf, 0x1234, G_LITTLE_ENDIAN); /* handle */
369
61
  fu_byte_array_append_uint8(buf, FU_REDFISH_SMBIOS_INTERFACE_TYPE_NETWORK);
370
61
  fu_byte_array_append_uint8(buf, 0x09);          /* iface datalen */
371
61
  fu_byte_array_append_uint8(buf, FU_REDFISH_INTERFACE_TYPE_USB_NETWORK); /* iface */
372
61
  fu_byte_array_append_uint16(buf, self->vid, G_LITTLE_ENDIAN);    /* iface:VID */
373
61
  fu_byte_array_append_uint16(buf, self->pid, G_LITTLE_ENDIAN);    /* iface:PID */
374
61
  fu_byte_array_append_uint8(buf, 0x02);          /* iface:serialsz */
375
61
  fu_byte_array_append_uint8(buf, 0x03);          /* iType */
376
61
  fu_byte_array_append_uint8(buf, 'S');         /* iface:serial */
377
61
  fu_byte_array_append_uint8(buf, 'n');         /* iface:serial */
378
61
  fu_byte_array_append_uint8(buf, 0x1); /* nr protocol rcds */
379
380
  /* protocol record */
381
61
  fu_byte_array_append_uint8(buf, REDFISH_PROTOCOL_REDFISH_OVER_IP);
382
61
  fu_byte_array_append_uint8(buf, st->buf->len + hostname_sz);
383
384
61
  if (self->hostname != NULL)
385
25
    hostname_sz = strlen(self->hostname);
386
61
  fu_struct_redfish_protocol_over_ip_set_service_ip_port(st, self->port);
387
61
  fu_struct_redfish_protocol_over_ip_set_service_ip_address_format(
388
61
      st,
389
61
      FU_REDFISH_IP_ADDRESS_FORMAT_V4);
390
61
  fu_struct_redfish_protocol_over_ip_set_service_ip_assignment_type(
391
61
      st,
392
61
      FU_REDFISH_IP_ASSIGNMENT_TYPE_STATIC);
393
61
  fu_struct_redfish_protocol_over_ip_set_service_hostname_len(st, hostname_sz);
394
61
  g_byte_array_append(buf, st->buf->data, st->buf->len);
395
61
  if (hostname_sz > 0)
396
23
    g_byte_array_append(buf, (guint8 *)self->hostname, hostname_sz);
397
61
  return g_steal_pointer(&buf);
398
61
}
399
400
static void
401
fu_redfish_smbios_finalize(GObject *object)
402
483
{
403
483
  FuRedfishSmbios *self = FU_REDFISH_SMBIOS(object);
404
483
  g_free(self->hostname);
405
483
  g_free(self->mac_addr);
406
483
  g_free(self->ip_addr);
407
483
  G_OBJECT_CLASS(fu_redfish_smbios_parent_class)->finalize(object);
408
483
}
409
410
static void
411
fu_redfish_smbios_init(FuRedfishSmbios *self)
412
483
{
413
483
  fu_firmware_set_size_max(FU_FIRMWARE(self), 1 * FU_MB);
414
483
}
415
416
static void
417
fu_redfish_smbios_class_init(FuRedfishSmbiosClass *klass)
418
1
{
419
1
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
420
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
421
1
  object_class->finalize = fu_redfish_smbios_finalize;
422
1
  firmware_class->parse = fu_redfish_smbios_parse;
423
1
  firmware_class->write = fu_redfish_smbios_write;
424
1
  firmware_class->build = fu_redfish_smbios_build;
425
1
  firmware_class->export = fu_redfish_smbios_export;
426
1
}
427
428
FuRedfishSmbios *
429
fu_redfish_smbios_new(void)
430
0
{
431
0
  return FU_REDFISH_SMBIOS(g_object_new(FU_TYPE_REDFISH_SMBIOS, NULL));
432
0
}