Coverage Report

Created: 2026-07-30 06:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/systemd/src/network/networkd-serialize.c
Line
Count
Source
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3
#include "af-list.h"
4
#include "alloc-util.h"
5
#include "daemon-util.h"
6
#include "errno-util.h"
7
#include "fd-util.h"
8
#include "hashmap.h"
9
#include "iovec-util.h"
10
#include "json-util.h"
11
#include "memfd-util.h"
12
#include "networkd-address.h"
13
#include "networkd-json.h"
14
#include "networkd-link.h"
15
#include "networkd-manager.h"
16
#include "networkd-nexthop.h"
17
#include "networkd-route.h"
18
#include "networkd-serialize.h"
19
#include "string-util.h"
20
21
int manager_serialize(Manager *manager) {
22
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *array = NULL;
23
        int r;
24
25
        assert(manager);
26
27
        log_debug("Serializing...");
28
29
        Link *link;
30
        HASHMAP_FOREACH(link, manager->links_by_index) {
31
                _cleanup_(sd_json_variant_unrefp) sd_json_variant *e = NULL;
32
33
                /* ignore unmanaged, failed, or removed interfaces. */
34
                if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_INITIALIZED, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
35
                        continue;
36
37
                r = sd_json_buildo(
38
                                &e,
39
                                SD_JSON_BUILD_PAIR_INTEGER("Index", link->ifindex));
40
                if (r < 0)
41
                        return r;
42
43
                r = addresses_append_json(link, /* serializing= */ true, &e);
44
                if (r < 0)
45
                        return r;
46
47
                r = sd_json_variant_append_array(&array, e);
48
                if (r < 0)
49
                        return r;
50
        }
51
52
        r = json_variant_set_field_non_null(&v, "Interfaces", array);
53
        if (r < 0)
54
                return r;
55
56
        r = nexthops_append_json(manager, /* ifindex= */ -1, &v);
57
        if (r < 0)
58
                return r;
59
60
        r = routes_append_json(manager, /* ifindex= */ -1, &v);
61
        if (r < 0)
62
                return r;
63
64
        if (!v) {
65
                log_debug("There is nothing to serialize.");
66
                (void) notify_remove_fd_warn("manager-serialization");
67
                return 0;
68
        }
69
70
        _cleanup_free_ char *dump = NULL;
71
        r = sd_json_variant_format(v, /* flags= */ 0, &dump);
72
        if (r < 0)
73
                return r;
74
75
        _cleanup_close_ int fd = -EBADF;
76
        fd = memfd_new_and_seal_string("serialization", dump);
77
        if (fd < 0)
78
                return fd;
79
80
        r = notify_push_fd(fd, "manager-serialization");
81
        if (r < 0)
82
                return log_debug_errno(r, "Failed to push serialization file descriptor: %m");
83
84
        log_debug("Serialization completed.");
85
        return 0;
86
}
87
88
0
int manager_set_serialization_fd(Manager *manager, int fd, const char *name) {
89
0
        assert(manager);
90
0
        assert(fd >= 0);
91
0
        assert(name);
92
93
0
        if (!startswith(name, "manager-serialization"))
94
0
                return -EINVAL;
95
96
0
        if (manager->serialization_fd >= 0)
97
0
                return -EEXIST;
98
99
0
        manager->serialization_fd = fd;
100
0
        return 0;
101
0
}
102
103
0
static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_network_config_source, NetworkConfigSource, network_config_source_from_string);
104
105
typedef struct AddressParam {
106
        int family;
107
        struct iovec address;
108
        struct iovec peer;
109
        uint8_t prefixlen;
110
        NetworkConfigSource source;
111
        struct iovec provider;
112
} AddressParam;
113
114
0
static void address_param_done(AddressParam *p) {
115
0
        assert(p);
116
117
0
        iovec_done(&p->address);
118
0
        iovec_done(&p->peer);
119
0
        iovec_done(&p->provider);
120
0
}
121
122
0
static int link_deserialize_address(Link *link, sd_json_variant *v) {
123
0
        static const sd_json_dispatch_field dispatch_table[] = {
124
0
                { "Family",         _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_address_family,        offsetof(AddressParam, family),    SD_JSON_MANDATORY },
125
0
                { "Address",        SD_JSON_VARIANT_ARRAY,         json_dispatch_byte_array_iovec,      offsetof(AddressParam, address),   SD_JSON_MANDATORY },
126
0
                { "Peer",           SD_JSON_VARIANT_ARRAY,         json_dispatch_byte_array_iovec,      offsetof(AddressParam, peer),      0                 },
127
0
                { "PrefixLength",   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint8,              offsetof(AddressParam, prefixlen), SD_JSON_MANDATORY },
128
0
                { "ConfigSource",   SD_JSON_VARIANT_STRING,        json_dispatch_network_config_source, offsetof(AddressParam, source),    SD_JSON_MANDATORY },
129
0
                { "ConfigProvider", SD_JSON_VARIANT_ARRAY,         json_dispatch_byte_array_iovec,      offsetof(AddressParam, provider),  0                 },
130
0
                {},
131
0
        };
132
133
0
        int r;
134
135
0
        assert(link);
136
0
        assert(v);
137
138
0
        _cleanup_(address_param_done) AddressParam p = {};
139
0
        r = sd_json_dispatch(v, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
140
0
        if (r < 0)
141
0
                return log_link_debug_errno(link, r, "Failed to dispatch address from json variant: %m");
142
143
0
        if (p.address.iov_len != FAMILY_ADDRESS_SIZE(p.family))
144
0
                return log_link_debug_errno(link, SYNTHETIC_ERRNO(EINVAL),
145
0
                                            "Dispatched address size (%zu) is incompatible with the family (%s).",
146
0
                                            p.address.iov_len, af_to_ipv4_ipv6(p.family));
147
148
0
        if (p.peer.iov_len != 0 && p.peer.iov_len != FAMILY_ADDRESS_SIZE(p.family))
149
0
                return log_link_debug_errno(link, SYNTHETIC_ERRNO(EINVAL),
150
0
                                            "Dispatched peer address size (%zu) is incompatible with the family (%s).",
151
0
                                            p.peer.iov_len, af_to_ipv4_ipv6(p.family));
152
153
0
        if (p.provider.iov_len != 0 && p.provider.iov_len != FAMILY_ADDRESS_SIZE(p.family))
154
0
                return log_link_debug_errno(link, SYNTHETIC_ERRNO(EINVAL),
155
0
                                            "Dispatched provider address size (%zu) is incompatible with the family (%s).",
156
0
                                            p.provider.iov_len, af_to_ipv4_ipv6(p.family));
157
158
0
        Address tmp = {
159
0
                .family = p.family,
160
0
                .prefixlen = p.prefixlen,
161
0
        };
162
163
0
        memcpy_safe(&tmp.in_addr, p.address.iov_base, p.address.iov_len);
164
0
        memcpy_safe(&tmp.in_addr_peer, p.peer.iov_base, p.peer.iov_len);
165
166
0
        Address *address;
167
0
        r = address_get(link, &tmp, &address);
168
0
        if (r < 0) {
169
0
                log_link_debug_errno(link, r, "Cannot find deserialized address %s: %m",
170
0
                                     IN_ADDR_PREFIX_TO_STRING(tmp.family, &tmp.in_addr, tmp.prefixlen));
171
0
                return 0; /* Already removed? */
172
0
        }
173
174
0
        if (address->source != NETWORK_CONFIG_SOURCE_FOREIGN)
175
0
                return 0; /* Huh?? Already deserialized?? */
176
177
0
        address->source = p.source;
178
0
        memcpy_safe(&address->provider, p.provider.iov_base, p.provider.iov_len);
179
180
0
        log_address_debug(address, "Deserialized", link);
181
0
        return 0;
182
0
}
183
184
0
static int manager_deserialize_link(Manager *manager, sd_json_variant *v) {
185
0
        typedef struct LinkParam {
186
0
                int ifindex;
187
0
                sd_json_variant *addresses;
188
0
        } LinkParam;
189
190
0
        static const sd_json_dispatch_field dispatch_table[] = {
191
0
                { "Index",     _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_ifindex,          offsetof(LinkParam, ifindex),   SD_JSON_MANDATORY | SD_JSON_REFUSE_NULL },
192
0
                { "Addresses", SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_variant_noref, offsetof(LinkParam, addresses), 0                                       },
193
0
                {},
194
0
        };
195
196
0
        int r, ret = 0;
197
198
0
        assert(manager);
199
0
        assert(v);
200
201
0
        LinkParam p = {};
202
0
        r = sd_json_dispatch(v, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
203
0
        if (r < 0)
204
0
                return log_debug_errno(r, "Failed to dispatch interface from json variant: %m");
205
206
0
        Link *link;
207
0
        r = link_get_by_index(manager, p.ifindex, &link);
208
0
        if (r < 0) {
209
0
                log_debug_errno(r, "No interface with deserialized ifindex (%i) found: %m", p.ifindex);
210
0
                return 0; /* Already removed? */
211
0
        }
212
213
0
        sd_json_variant *i;
214
0
        JSON_VARIANT_ARRAY_FOREACH(i, p.addresses)
215
0
                RET_GATHER(ret, link_deserialize_address(link, i));
216
217
0
        return ret;
218
0
}
219
220
typedef struct NextHopParam {
221
        uint32_t id;
222
        int family;
223
        NetworkConfigSource source;
224
        struct iovec provider;
225
} NextHopParam;
226
227
0
static void nexthop_param_done(NextHopParam *p) {
228
0
        assert(p);
229
230
0
        iovec_done(&p->provider);
231
0
}
232
233
0
static int manager_deserialize_nexthop(Manager *manager, sd_json_variant *v) {
234
0
        static const sd_json_dispatch_field dispatch_table[] = {
235
0
                { "ID",             _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32,             offsetof(NextHopParam, id),        SD_JSON_MANDATORY },
236
0
                { "Family",         _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_address_family,        offsetof(NextHopParam, family),    SD_JSON_MANDATORY },
237
0
                { "ConfigSource",   SD_JSON_VARIANT_STRING,        json_dispatch_network_config_source, offsetof(NextHopParam, source),    SD_JSON_MANDATORY },
238
0
                { "ConfigProvider", SD_JSON_VARIANT_ARRAY,         json_dispatch_byte_array_iovec,      offsetof(NextHopParam, provider),  0                 },
239
0
                {},
240
0
        };
241
242
0
        int r;
243
244
0
        assert(manager);
245
0
        assert(v);
246
247
0
        _cleanup_(nexthop_param_done) NextHopParam p = {};
248
0
        r = sd_json_dispatch(v, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
249
0
        if (r < 0)
250
0
                return log_debug_errno(r, "Failed to dispatch nexthop from json variant: %m");
251
252
0
        if (p.id == 0)
253
0
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Dispatched nexthop ID is zero.");
254
255
0
        if (p.provider.iov_len != 0 && p.provider.iov_len != FAMILY_ADDRESS_SIZE(p.family))
256
0
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
257
0
                                       "Dispatched provider address size (%zu) is incompatible with the family (%s).",
258
0
                                       p.provider.iov_len, af_to_ipv4_ipv6(p.family));
259
260
0
        NextHop *nexthop;
261
0
        r = nexthop_get_by_id(manager, p.id, &nexthop);
262
0
        if (r < 0) {
263
0
                log_debug_errno(r, "Cannot find deserialized nexthop (ID=%"PRIu32"): %m", p.id);
264
0
                return 0; /* Already removed? */
265
0
        }
266
267
0
        if (nexthop->source != NETWORK_CONFIG_SOURCE_FOREIGN)
268
0
                return 0; /* Huh?? Already deserialized?? */
269
270
0
        nexthop->source = p.source;
271
0
        memcpy_safe(&nexthop->provider, p.provider.iov_base, p.provider.iov_len);
272
273
0
        log_nexthop_debug(nexthop, "Deserialized", manager);
274
0
        return 0;
275
0
}
276
277
typedef struct RouteParam {
278
        Route route;
279
280
        struct iovec dst;
281
        struct iovec src;
282
        struct iovec prefsrc;
283
        struct iovec gw;
284
        struct iovec metrics;
285
        struct iovec provider;
286
} RouteParam;
287
288
0
static void route_param_done(RouteParam *p) {
289
0
        assert(p);
290
291
0
        free(p->route.metric.metrics);
292
293
0
        iovec_done(&p->dst);
294
0
        iovec_done(&p->src);
295
0
        iovec_done(&p->prefsrc);
296
0
        iovec_done(&p->gw);
297
0
        iovec_done(&p->metrics);
298
0
        iovec_done(&p->provider);
299
0
}
300
301
0
static int manager_deserialize_route(Manager *manager, sd_json_variant *v) {
302
0
        static const sd_json_dispatch_field dispatch_table[] = {
303
                /* rtmsg header */
304
0
                { "Family",                        _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_address_family,        offsetof(RouteParam, route.family),                             SD_JSON_MANDATORY                 },
305
0
                { "DestinationPrefixLength",       _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint8,              offsetof(RouteParam, route.dst_prefixlen),                      SD_JSON_MANDATORY                 },
306
0
                { "SourcePrefixLength",            _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint8,              offsetof(RouteParam, route.src_prefixlen),                      0                                 },
307
0
                { "TOS",                           _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint8,              offsetof(RouteParam, route.tos),                                SD_JSON_MANDATORY                 },
308
0
                { "Protocol",                      _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint8,              offsetof(RouteParam, route.protocol),                           SD_JSON_MANDATORY                 },
309
0
                { "Scope",                         _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint8,              offsetof(RouteParam, route.scope),                              SD_JSON_MANDATORY                 },
310
0
                { "Type",                          _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint8,              offsetof(RouteParam, route.type),                               SD_JSON_MANDATORY                 },
311
0
                { "Flags",                         _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32,             offsetof(RouteParam, route.flags),                              SD_JSON_MANDATORY                 },
312
                /* attributes */
313
0
                { "Destination",                   SD_JSON_VARIANT_ARRAY,         json_dispatch_byte_array_iovec,      offsetof(RouteParam, dst),                                      SD_JSON_MANDATORY                 },
314
0
                { "Source",                        SD_JSON_VARIANT_ARRAY,         json_dispatch_byte_array_iovec,      offsetof(RouteParam, src),                                      0                                 },
315
0
                { "Priority",                      _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32,             offsetof(RouteParam, route.priority),                           SD_JSON_MANDATORY                 },
316
0
                { "PreferredSource",               SD_JSON_VARIANT_ARRAY,         json_dispatch_byte_array_iovec,      offsetof(RouteParam, prefsrc),                                  0                                 },
317
0
                { "Table",                         _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32,             offsetof(RouteParam, route.table),                              SD_JSON_MANDATORY                 },
318
                /* nexthops */
319
0
                { "Gateway",                       SD_JSON_VARIANT_ARRAY,         json_dispatch_byte_array_iovec,      offsetof(RouteParam, gw),                                       0                                 },
320
0
                { "InterfaceIndex",                _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_ifindex,               offsetof(RouteParam, route.nexthop.ifindex),                    SD_JSON_MANDATORY | SD_JSON_RELAX },
321
0
                { "NextHopID",                     _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32,             offsetof(RouteParam, route.nexthop_id),                         0                                 },
322
                /* metrics */
323
0
                { "Metrics",                       SD_JSON_VARIANT_ARRAY,         json_dispatch_byte_array_iovec,      offsetof(RouteParam, metrics),                                  0                                 },
324
0
                { "TCPCongestionControlAlgorithm", SD_JSON_VARIANT_STRING,        sd_json_dispatch_const_string,       offsetof(RouteParam, route.metric.tcp_congestion_control_algo), 0                                 },
325
                /* config */
326
0
                { "ConfigSource",                  SD_JSON_VARIANT_STRING,        json_dispatch_network_config_source, offsetof(RouteParam, route.source),                             SD_JSON_MANDATORY                 },
327
0
                { "ConfigProvider",                SD_JSON_VARIANT_ARRAY,         json_dispatch_byte_array_iovec,      offsetof(RouteParam, provider),                                 0                                 },
328
0
                {},
329
0
        };
330
331
0
        int r;
332
333
0
        assert(manager);
334
0
        assert(v);
335
336
0
        _cleanup_(route_param_done) RouteParam p = {};
337
0
        r = sd_json_dispatch(v, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
338
0
        if (r < 0)
339
0
                return log_debug_errno(r, "Failed to dispatch route from json variant: %m");
340
341
0
        if (p.dst.iov_len != FAMILY_ADDRESS_SIZE(p.route.family))
342
0
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
343
0
                                       "Dispatched destination address size (%zu) is incompatible with the family (%s).",
344
0
                                       p.dst.iov_len, af_to_ipv4_ipv6(p.route.family));
345
346
0
        if (p.src.iov_len != 0 && p.src.iov_len != FAMILY_ADDRESS_SIZE(p.route.family))
347
0
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
348
0
                                       "Dispatched source address size (%zu) is incompatible with the family (%s).",
349
0
                                       p.src.iov_len, af_to_ipv4_ipv6(p.route.family));
350
351
0
        if (p.prefsrc.iov_len != 0 && p.prefsrc.iov_len != FAMILY_ADDRESS_SIZE(p.route.family))
352
0
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
353
0
                                       "Dispatched preferred source address size (%zu) is incompatible with the family (%s).",
354
0
                                       p.prefsrc.iov_len, af_to_ipv4_ipv6(p.route.family));
355
356
0
        switch (p.gw.iov_len) {
357
0
        case 0:
358
0
                p.route.nexthop.family = AF_UNSPEC;
359
0
                break;
360
0
        case sizeof(struct in_addr):
361
0
                p.route.nexthop.family = AF_INET;
362
0
                break;
363
0
        case sizeof(struct in6_addr):
364
0
                p.route.nexthop.family = AF_INET6;
365
0
                break;
366
0
        default:
367
0
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
368
0
                                       "Dispatched gateway address size (%zu) is invalid.",
369
0
                                       p.prefsrc.iov_len);
370
0
        }
371
372
0
        if (p.metrics.iov_len % sizeof(uint32_t) != 0)
373
0
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
374
0
                                       "Dispatched route metric size (%zu) is invalid.",
375
0
                                       p.metrics.iov_len);
376
377
0
        if (p.provider.iov_len != 0 && p.provider.iov_len != FAMILY_ADDRESS_SIZE(p.route.family))
378
0
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
379
0
                                       "Dispatched provider address size (%zu) is incompatible with the family (%s).",
380
0
                                       p.provider.iov_len, af_to_ipv4_ipv6(p.route.family));
381
382
0
        memcpy_safe(&p.route.dst, p.dst.iov_base, p.dst.iov_len);
383
0
        memcpy_safe(&p.route.src, p.src.iov_base, p.src.iov_len);
384
0
        memcpy_safe(&p.route.prefsrc, p.prefsrc.iov_base, p.prefsrc.iov_len);
385
0
        memcpy_safe(&p.route.nexthop.gw, p.gw.iov_base, p.gw.iov_len);
386
387
0
        p.route.metric.n_metrics = p.metrics.iov_len / sizeof(uint32_t);
388
0
        p.route.metric.metrics = new(uint32_t, p.route.metric.n_metrics);
389
0
        if (!p.route.metric.metrics)
390
0
                return log_oom_debug();
391
392
0
        memcpy_safe(p.route.metric.metrics, p.metrics.iov_base, p.metrics.iov_len);
393
394
0
        Route *route;
395
0
        r = route_get(manager, &p.route, &route);
396
0
        if (r < 0) {
397
0
                log_route_debug(&p.route, "Cannot find deserialized", manager);
398
0
                return 0; /* Already removed? */
399
0
        }
400
401
0
        if (route->source != NETWORK_CONFIG_SOURCE_FOREIGN)
402
0
                return 0; /* Huh?? Already deserialized?? */
403
404
0
        route->source = p.route.source;
405
0
        memcpy_safe(&route->provider, p.provider.iov_base, p.provider.iov_len);
406
407
0
        log_route_debug(route, "Deserialized", manager);
408
0
        return 0;
409
0
}
410
411
0
int manager_deserialize(Manager *manager) {
412
0
        int r, ret = 0;
413
414
0
        assert(manager);
415
416
0
        _cleanup_close_ int fd = TAKE_FD(manager->serialization_fd);
417
0
        if (fd < 0)
418
0
                return 0;
419
420
0
        log_debug("Deserializing...");
421
422
0
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
423
0
        unsigned err_line = 0, err_column = 0;
424
0
        r = sd_json_parse_fd(
425
0
                        /* path= */ NULL,
426
0
                        TAKE_FD(fd),
427
0
                        SD_JSON_PARSE_DONATE_FD,
428
0
                        &v,
429
0
                        &err_line,
430
0
                        &err_column);
431
0
        if (r < 0)
432
0
                return log_debug_errno(r, "Failed to parse json (line=%u, column=%u): %m", err_line, err_column);
433
434
0
        sd_json_variant *i;
435
0
        JSON_VARIANT_ARRAY_FOREACH(i, sd_json_variant_by_key(v, "Interfaces"))
436
0
                RET_GATHER(ret, manager_deserialize_link(manager, i));
437
438
0
        JSON_VARIANT_ARRAY_FOREACH(i, sd_json_variant_by_key(v, "NextHops"))
439
0
                RET_GATHER(ret, manager_deserialize_nexthop(manager, i));
440
441
0
        JSON_VARIANT_ARRAY_FOREACH(i, sd_json_variant_by_key(v, "Routes"))
442
0
                RET_GATHER(ret, manager_deserialize_route(manager, i));
443
444
0
        log_debug("Deserialization completed.");
445
0
        return ret;
446
0
}