Coverage Report

Created: 2026-10-28 07:47

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
625
G_DEFINE_TYPE(FuRedfishSmbios, fu_redfish_smbios, FU_TYPE_FIRMWARE)
25
625
26
625
FuRedfishSmbiosInterfaceType
27
625
fu_redfish_smbios_get_interface_type(FuRedfishSmbios *self)
28
625
{
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.69k
{
71
1.69k
  g_set_str(&self->hostname, hostname);
72
1.69k
}
73
74
static void
75
fu_redfish_smbios_set_mac_addr(FuRedfishSmbios *self, const gchar *mac_addr)
76
44
{
77
44
  g_set_str(&self->mac_addr, mac_addr);
78
44
}
79
80
static void
81
fu_redfish_smbios_set_ip_addr(FuRedfishSmbios *self, const gchar *ip_addr)
82
2.35k
{
83
2.35k
  g_set_str(&self->ip_addr, ip_addr);
84
2.35k
}
85
86
static void
87
fu_redfish_smbios_set_port(FuRedfishSmbios *self, guint16 port)
88
2.39k
{
89
2.39k
  self->port = port;
90
2.39k
}
91
92
static void
93
fu_redfish_smbios_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
94
0
{
95
0
  FuRedfishSmbios *self = FU_REDFISH_SMBIOS(firmware);
96
0
  fu_xmlb_builder_insert_kv(bn,
97
0
          "interface_type",
98
0
          fu_redfish_smbios_interface_type_to_string(self->interface_type));
99
0
  fu_xmlb_builder_insert_kx(bn, "port", self->port);
100
0
  fu_xmlb_builder_insert_kv(bn, "hostname", self->hostname);
101
0
  fu_xmlb_builder_insert_kv(bn, "mac_addr", self->mac_addr);
102
0
  fu_xmlb_builder_insert_kv(bn, "ip_addr", self->ip_addr);
103
0
  fu_xmlb_builder_insert_kx(bn, "vid", self->vid);
104
0
  fu_xmlb_builder_insert_kx(bn, "pid", self->pid);
105
0
}
106
107
static gboolean
108
fu_redfish_smbios_build(FuFirmware *firmware, XbNode *n, GError **error)
109
0
{
110
0
  FuRedfishSmbios *self = FU_REDFISH_SMBIOS(firmware);
111
0
  const gchar *tmp;
112
0
  guint64 tmpu;
113
114
  /* optional properties */
115
0
  tmpu = xb_node_query_text_as_uint(n, "port", NULL);
116
0
  if (tmpu != G_MAXUINT64)
117
0
    fu_redfish_smbios_set_port(self, (guint16)tmpu);
118
0
  tmpu = xb_node_query_text_as_uint(n, "vid", NULL);
119
0
  if (tmpu != G_MAXUINT64)
120
0
    self->vid = (guint16)tmpu;
121
0
  tmpu = xb_node_query_text_as_uint(n, "pid", NULL);
122
0
  if (tmpu != G_MAXUINT64)
123
0
    self->pid = (guint16)tmpu;
124
0
  tmp = xb_node_query_text(n, "hostname", NULL);
125
0
  if (tmp != NULL)
126
0
    fu_redfish_smbios_set_hostname(self, tmp);
127
0
  tmp = xb_node_query_text(n, "mac_addr", NULL);
128
0
  if (tmp != NULL)
129
0
    fu_redfish_smbios_set_mac_addr(self, tmp);
130
0
  tmp = xb_node_query_text(n, "ip_addr", NULL);
131
0
  if (tmp != NULL)
132
0
    fu_redfish_smbios_set_ip_addr(self, tmp);
133
134
  /* success */
135
0
  return TRUE;
136
0
}
137
138
static gboolean
139
fu_redfish_smbios_parse_interface_data(FuRedfishSmbios *self,
140
               FuInputStream *stream,
141
               gsize offset,
142
               GError **error)
143
107
{
144
107
  gsize offset_mac_addr = G_MAXSIZE;
145
107
  gsize offset_vid_pid = G_MAXSIZE;
146
107
  guint8 interface_type = 0x0;
147
148
  /* parse the data depending on the interface type */
149
107
  if (!fu_input_stream_read_u8(stream, offset, &interface_type, error))
150
0
    return FALSE;
151
107
  g_debug("interface_type: %s [0x%x]",
152
107
    fu_redfish_interface_type_to_string(interface_type),
153
107
    interface_type);
154
107
  offset++;
155
107
  switch (interface_type) {
156
5
  case FU_REDFISH_INTERFACE_TYPE_USB_NETWORK:
157
9
  case FU_REDFISH_INTERFACE_TYPE_PCI_NETWORK:
158
9
    offset_vid_pid = 0x00;
159
9
    break;
160
26
  case FU_REDFISH_INTERFACE_TYPE_USB_NETWORK_V2:
161
26
    offset_vid_pid = 0x01;
162
26
    offset_mac_addr = 0x06;
163
26
    break;
164
22
  case FU_REDFISH_INTERFACE_TYPE_PCI_NETWORK_V2:
165
22
    offset_vid_pid = 0x01;
166
22
    offset_mac_addr = 0x09;
167
22
    break;
168
50
  default:
169
50
    g_debug("unknown Network Interface");
170
50
    break;
171
107
  }
172
173
  /* MAC address */
174
107
  if (offset_mac_addr != G_MAXSIZE) {
175
48
    guint8 mac_addr[6] = {0x0};
176
48
    g_autofree gchar *mac_addr_str = NULL;
177
48
    if (!fu_input_stream_read_safe(stream,
178
48
                 mac_addr,
179
48
                 sizeof(mac_addr),
180
48
                 0x0,      /* dst */
181
48
                 offset + offset_mac_addr, /* src */
182
48
                 sizeof(mac_addr),
183
48
                 error))
184
4
      return FALSE;
185
44
    mac_addr_str = fu_redfish_common_buffer_to_mac(mac_addr);
186
44
    fu_redfish_smbios_set_mac_addr(self, mac_addr_str);
187
44
  }
188
189
  /* VID:PID */
190
103
  if (offset_vid_pid != G_MAXSIZE) {
191
53
    if (!fu_input_stream_read_u16(stream,
192
53
                offset + offset_vid_pid,
193
53
                &self->vid,
194
53
                G_LITTLE_ENDIAN,
195
53
                error))
196
0
      return FALSE;
197
53
    if (!fu_input_stream_read_u16(stream,
198
53
                offset + offset_vid_pid + 0x02,
199
53
                &self->pid,
200
53
                G_LITTLE_ENDIAN,
201
53
                error))
202
3
      return FALSE;
203
53
  }
204
205
  /* success */
206
100
  return TRUE;
207
103
}
208
209
static gboolean
210
fu_redfish_smbios_parse_over_ip(FuRedfishSmbios *self,
211
        FuInputStream *stream,
212
        gsize offset,
213
        GError **error)
214
2.51k
{
215
2.51k
  guint8 hostname_length;
216
2.51k
  guint8 service_ip_address_format;
217
2.51k
  g_autoptr(FuStructRedfishProtocolOverIp) st = NULL;
218
219
  /* port + IP address */
220
2.51k
  st = fu_struct_redfish_protocol_over_ip_parse_stream(stream, offset, error);
221
2.51k
  if (st == NULL)
222
122
    return FALSE;
223
2.39k
  fu_redfish_smbios_set_port(self,
224
2.39k
           fu_struct_redfish_protocol_over_ip_get_service_ip_port(st));
225
2.39k
  service_ip_address_format =
226
2.39k
      fu_struct_redfish_protocol_over_ip_get_service_ip_address_format(st);
227
2.39k
  if (service_ip_address_format == FU_REDFISH_IP_ADDRESS_FORMAT_V4) {
228
1.42k
    const guint8 *ip_address =
229
1.42k
        fu_struct_redfish_protocol_over_ip_get_service_ip_address(st, NULL);
230
1.42k
    g_autofree gchar *tmp = fu_redfish_common_buffer_to_ipv4(ip_address);
231
1.42k
    fu_redfish_smbios_set_ip_addr(self, tmp);
232
1.42k
  } else if (service_ip_address_format == FU_REDFISH_IP_ADDRESS_FORMAT_V6) {
233
926
    const guint8 *ip_address =
234
926
        fu_struct_redfish_protocol_over_ip_get_service_ip_address(st, NULL);
235
926
    g_autofree gchar *tmp = fu_redfish_common_buffer_to_ipv6(ip_address);
236
926
    fu_redfish_smbios_set_ip_addr(self, tmp);
237
926
  } else {
238
43
    g_set_error_literal(error,
239
43
            FWUPD_ERROR,
240
43
            FWUPD_ERROR_INVALID_FILE,
241
43
            "address format is invalid");
242
43
    return FALSE;
243
43
  }
244
245
  /* hostname */
246
2.35k
  hostname_length = fu_struct_redfish_protocol_over_ip_get_service_hostname_len(st);
247
2.35k
  if (hostname_length > 0) {
248
1.80k
    g_autofree gchar *hostname = g_malloc0(hostname_length + 1);
249
1.80k
    if (!fu_input_stream_read_safe(stream,
250
1.80k
                 (guint8 *)hostname,
251
1.80k
                 hostname_length,
252
1.80k
                 0x0,         /* dst */
253
1.80k
                 offset + st->buf->len, /* seek */
254
1.80k
                 hostname_length,
255
1.80k
                 error))
256
117
      return FALSE;
257
1.69k
    fu_redfish_smbios_set_hostname(self, hostname);
258
1.69k
  }
259
260
  /* success */
261
2.23k
  return TRUE;
262
2.35k
}
263
264
static gboolean
265
fu_redfish_smbios_parse(FuFirmware *firmware,
266
      FuInputStream *stream,
267
      FuFirmwareParseFlags flags,
268
      GError **error)
269
623
{
270
623
  FuRedfishSmbios *self = FU_REDFISH_SMBIOS(firmware);
271
623
  gsize offset = 0;
272
623
  gsize streamsz = 0;
273
623
  g_autoptr(FuStructRedfishSmbiosType42) st = NULL;
274
275
  /* check size */
276
623
  if (!fu_input_stream_size(stream, &streamsz, error))
277
0
    return FALSE;
278
623
  if (streamsz < 0x09) {
279
11
    g_set_error(error,
280
11
          FWUPD_ERROR,
281
11
          FWUPD_ERROR_INVALID_FILE,
282
11
          "SMBIOS entry too small: %zu",
283
11
          streamsz);
284
11
    return FALSE;
285
11
  }
286
287
  /* parse */
288
612
  st = fu_struct_redfish_smbios_type42_parse_stream(stream, offset, error);
289
612
  if (st == NULL)
290
86
    return FALSE;
291
292
  /* check length */
293
526
  if (fu_struct_redfish_smbios_type42_get_length(st) != streamsz) {
294
68
    g_set_error(error,
295
68
          FWUPD_ERROR,
296
68
          FWUPD_ERROR_INVALID_FILE,
297
68
          "size of table 0x%x does not match binary 0x%x",
298
68
          fu_struct_redfish_smbios_type42_get_length(st),
299
68
          (guint)streamsz);
300
68
    return FALSE;
301
68
  }
302
303
  /* check length */
304
458
  if (!fu_size_checked_inc(&offset, FU_STRUCT_REDFISH_SMBIOS_TYPE42_SIZE, error)) {
305
0
    g_prefix_error_literal(error, "header offset overflow: ");
306
0
    return FALSE;
307
0
  }
308
458
  if (fu_struct_redfish_smbios_type42_get_data_length(st) > 0) {
309
107
    if (!fu_redfish_smbios_parse_interface_data(self, stream, offset, error))
310
7
      return FALSE;
311
107
  }
312
451
  if (!fu_size_checked_inc(&offset,
313
451
         fu_struct_redfish_smbios_type42_get_data_length(st),
314
451
         error))
315
0
    return FALSE;
316
317
  /* parse protocol records */
318
451
  self->interface_type = fu_struct_redfish_smbios_type42_get_interface_type(st);
319
451
  if (self->interface_type == FU_REDFISH_SMBIOS_INTERFACE_TYPE_NETWORK) {
320
439
    guint8 protocol_rcds = 0;
321
439
    if (!fu_input_stream_read_u8(stream, offset, &protocol_rcds, error))
322
4
      return FALSE;
323
435
    if (!fu_size_checked_inc(&offset, 1, error)) {
324
0
      g_prefix_error_literal(error, "protocol records offset overflow: ");
325
0
      return FALSE;
326
0
    }
327
435
    g_debug("protocol_rcds: %u", protocol_rcds);
328
6.80k
    for (guint i = 0; i < protocol_rcds; i++) {
329
6.76k
      guint8 protocol_id = 0;
330
6.76k
      guint8 protocol_sz = 0;
331
6.76k
      if (!fu_input_stream_read_u8(stream, offset, &protocol_id, error))
332
78
        return FALSE;
333
6.69k
      if (!fu_input_stream_read_u8(stream, offset + 0x1, &protocol_sz, error))
334
39
        return FALSE;
335
6.65k
      if (protocol_id == REDFISH_PROTOCOL_REDFISH_OVER_IP) {
336
2.51k
        if (!fu_redfish_smbios_parse_over_ip(self,
337
2.51k
                     stream,
338
2.51k
                     offset + 0x2,
339
2.51k
                     error))
340
282
          return FALSE;
341
4.13k
      } else {
342
4.13k
        g_debug("ignoring protocol ID 0x%02x", protocol_id);
343
4.13k
      }
344
6.36k
      if (!fu_size_checked_inc(&offset, protocol_sz + 1, error))
345
0
        return FALSE;
346
6.36k
    }
347
435
  }
348
349
  /* success */
350
48
  return TRUE;
351
451
}
352
353
static GByteArray *
354
fu_redfish_smbios_write(FuFirmware *firmware, GError **error)
355
48
{
356
48
  FuRedfishSmbios *self = FU_REDFISH_SMBIOS(firmware);
357
48
  gsize hostname_sz = 0;
358
48
  g_autoptr(FuStructRedfishProtocolOverIp) st = fu_struct_redfish_protocol_over_ip_new();
359
48
  g_autoptr(GByteArray) buf = g_byte_array_new();
360
361
48
  if (self->hostname != NULL)
362
28
    hostname_sz = strlen(self->hostname);
363
48
  fu_byte_array_append_uint8(buf, REDFISH_SMBIOS_TABLE_TYPE);
364
48
  fu_byte_array_append_uint8(buf, 0x6D + hostname_sz);     /* length */
365
48
  fu_byte_array_append_uint16(buf, 0x1234, G_LITTLE_ENDIAN); /* handle */
366
48
  fu_byte_array_append_uint8(buf, FU_REDFISH_SMBIOS_INTERFACE_TYPE_NETWORK);
367
48
  fu_byte_array_append_uint8(buf, 0x09);          /* iface datalen */
368
48
  fu_byte_array_append_uint8(buf, FU_REDFISH_INTERFACE_TYPE_USB_NETWORK); /* iface */
369
48
  fu_byte_array_append_uint16(buf, self->vid, G_LITTLE_ENDIAN);    /* iface:VID */
370
48
  fu_byte_array_append_uint16(buf, self->pid, G_LITTLE_ENDIAN);    /* iface:PID */
371
48
  fu_byte_array_append_uint8(buf, 0x02);          /* iface:serialsz */
372
48
  fu_byte_array_append_uint8(buf, 0x03);          /* iType */
373
48
  fu_byte_array_append_uint8(buf, 'S');         /* iface:serial */
374
48
  fu_byte_array_append_uint8(buf, 'n');         /* iface:serial */
375
48
  fu_byte_array_append_uint8(buf, 0x1); /* nr protocol rcds */
376
377
  /* protocol record */
378
48
  fu_byte_array_append_uint8(buf, REDFISH_PROTOCOL_REDFISH_OVER_IP);
379
48
  fu_byte_array_append_uint8(buf, st->buf->len + hostname_sz);
380
381
48
  if (self->hostname != NULL)
382
28
    hostname_sz = strlen(self->hostname);
383
48
  fu_struct_redfish_protocol_over_ip_set_service_ip_port(st, self->port);
384
48
  fu_struct_redfish_protocol_over_ip_set_service_ip_address_format(
385
48
      st,
386
48
      FU_REDFISH_IP_ADDRESS_FORMAT_V4);
387
48
  fu_struct_redfish_protocol_over_ip_set_service_ip_assignment_type(
388
48
      st,
389
48
      FU_REDFISH_IP_ASSIGNMENT_TYPE_STATIC);
390
48
  fu_struct_redfish_protocol_over_ip_set_service_hostname_len(st, hostname_sz);
391
48
  g_byte_array_append(buf, st->buf->data, st->buf->len);
392
48
  if (hostname_sz > 0)
393
25
    g_byte_array_append(buf, (guint8 *)self->hostname, hostname_sz);
394
48
  return g_steal_pointer(&buf);
395
48
}
396
397
static void
398
fu_redfish_smbios_finalize(GObject *object)
399
623
{
400
623
  FuRedfishSmbios *self = FU_REDFISH_SMBIOS(object);
401
623
  g_free(self->hostname);
402
623
  g_free(self->mac_addr);
403
623
  g_free(self->ip_addr);
404
623
  G_OBJECT_CLASS(fu_redfish_smbios_parent_class)->finalize(object);
405
623
}
406
407
static void
408
fu_redfish_smbios_init(FuRedfishSmbios *self)
409
623
{
410
623
}
411
412
static void
413
fu_redfish_smbios_class_init(FuRedfishSmbiosClass *klass)
414
1
{
415
1
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
416
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
417
1
  object_class->finalize = fu_redfish_smbios_finalize;
418
1
  firmware_class->parse = fu_redfish_smbios_parse;
419
1
  firmware_class->write = fu_redfish_smbios_write;
420
1
  firmware_class->build = fu_redfish_smbios_build;
421
1
  firmware_class->export = fu_redfish_smbios_export;
422
1
  fu_firmware_set_size_max(firmware_class, 1 * FU_MB);
423
1
}
424
425
FuRedfishSmbios *
426
fu_redfish_smbios_new(void)
427
0
{
428
0
  return FU_REDFISH_SMBIOS(g_object_new(FU_TYPE_REDFISH_SMBIOS, NULL));
429
0
}