]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/dhcp-identifier.c
7afba75f29608a6d4c36f2f8ad82596ecb58ebc6
[thirdparty/systemd.git] / src / libsystemd-network / dhcp-identifier.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 Copyright (C) 2015 Tom Gundersen <teg@jklmen>
4 ***/
5
6 #include "libudev.h"
7 #include "sd-id128.h"
8
9 #include "dhcp-identifier.h"
10 #include "dhcp6-protocol.h"
11 #include "network-internal.h"
12 #include "siphash24.h"
13 #include "sparse-endian.h"
14 #include "udev-util.h"
15 #include "virt.h"
16
17 #define SYSTEMD_PEN 43793
18 #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
19
20 int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len) {
21 struct duid d;
22
23 assert_cc(sizeof(d.raw) >= MAX_DUID_LEN);
24 if (duid_len > MAX_DUID_LEN)
25 return -EINVAL;
26
27 switch (duid_type) {
28 case DUID_TYPE_LLT:
29 if (duid_len <= sizeof(d.llt))
30 return -EINVAL;
31 break;
32 case DUID_TYPE_EN:
33 if (duid_len != sizeof(d.en))
34 return -EINVAL;
35 break;
36 case DUID_TYPE_LL:
37 if (duid_len <= sizeof(d.ll))
38 return -EINVAL;
39 break;
40 case DUID_TYPE_UUID:
41 if (duid_len != sizeof(d.uuid))
42 return -EINVAL;
43 break;
44 default:
45 /* accept unknown type in order to be forward compatible */
46 break;
47 }
48 return 0;
49 }
50
51 int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) {
52 sd_id128_t machine_id;
53 uint64_t hash;
54 int r;
55
56 assert(duid);
57 assert(len);
58
59 r = sd_id128_get_machine(&machine_id);
60 if (r < 0)
61 return r;
62
63 unaligned_write_be16(&duid->type, DUID_TYPE_EN);
64 unaligned_write_be32(&duid->en.pen, SYSTEMD_PEN);
65
66 *len = sizeof(duid->type) + sizeof(duid->en);
67
68 /* a bit of snake-oil perhaps, but no need to expose the machine-id
69 directly; duid->en.id might not be aligned, so we need to copy */
70 hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
71 memcpy(duid->en.id, &hash, sizeof(duid->en.id));
72
73 return 0;
74 }
75
76 int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_id) {
77 /* name is a pointer to memory in the udev_device struct, so must
78 have the same scope */
79 _cleanup_(udev_device_unrefp) struct udev_device *device = NULL;
80 const char *name = NULL;
81 uint64_t id;
82
83 if (detect_container() <= 0) {
84 /* not in a container, udev will be around */
85 _cleanup_(udev_unrefp) struct udev *udev;
86 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
87
88 udev = udev_new();
89 if (!udev)
90 return -ENOMEM;
91
92 sprintf(ifindex_str, "n%d", ifindex);
93 device = udev_device_new_from_device_id(udev, ifindex_str);
94 if (device) {
95 if (udev_device_get_is_initialized(device) <= 0)
96 /* not yet ready */
97 return -EBUSY;
98
99 name = net_get_name(device);
100 }
101 }
102
103 if (name)
104 id = siphash24(name, strlen(name), HASH_KEY.bytes);
105 else
106 /* fall back to MAC address if no predictable name available */
107 id = siphash24(mac, mac_len, HASH_KEY.bytes);
108
109 id = htole64(id);
110
111 /* fold into 32 bits */
112 unaligned_write_be32(_id, (id & 0xffffffff) ^ (id >> 32));
113
114 return 0;
115 }