]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/dhcp-identifier.c
sd-dhcp: use MAC address when DUIDType=link-layer-time or link-layer but DUIDRawData...
[thirdparty/systemd.git] / src / libsystemd-network / dhcp-identifier.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <linux/if_infiniband.h>
4
5 #include "libudev.h"
6 #include "sd-id128.h"
7
8 #include "dhcp-identifier.h"
9 #include "dhcp6-protocol.h"
10 #include "network-internal.h"
11 #include "siphash24.h"
12 #include "sparse-endian.h"
13 #include "udev-util.h"
14 #include "virt.h"
15
16 #define SYSTEMD_PEN 43793
17 #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
18 #define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03)
19 #define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */
20
21 int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len) {
22 struct duid d;
23
24 assert_cc(sizeof(d.raw) >= MAX_DUID_LEN);
25 if (duid_len > MAX_DUID_LEN)
26 return -EINVAL;
27
28 switch (duid_type) {
29 case DUID_TYPE_LLT:
30 if (duid_len <= sizeof(d.llt))
31 return -EINVAL;
32 break;
33 case DUID_TYPE_EN:
34 if (duid_len != sizeof(d.en))
35 return -EINVAL;
36 break;
37 case DUID_TYPE_LL:
38 if (duid_len <= sizeof(d.ll))
39 return -EINVAL;
40 break;
41 case DUID_TYPE_UUID:
42 if (duid_len != sizeof(d.uuid))
43 return -EINVAL;
44 break;
45 default:
46 /* accept unknown type in order to be forward compatible */
47 break;
48 }
49 return 0;
50 }
51
52 int dhcp_identifier_set_duid_llt(struct duid *duid, usec_t t, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len) {
53 uint16_t time_from_2000y;
54
55 assert(duid);
56 assert(len);
57 assert(addr);
58
59 if (arp_type == ARPHRD_ETHER)
60 assert_return(addr_len == ETH_ALEN, -EINVAL);
61 else if (arp_type == ARPHRD_INFINIBAND)
62 assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
63 else
64 return -EINVAL;
65
66 if (t < USEC_2000)
67 time_from_2000y = 0;
68 else
69 time_from_2000y = (uint16_t) (((t - USEC_2000) / USEC_PER_SEC) & 0xffffffff);
70
71 unaligned_write_be16(&duid->type, DUID_TYPE_LLT);
72 unaligned_write_be16(&duid->llt.htype, arp_type);
73 unaligned_write_be32(&duid->llt.time, time_from_2000y);
74 memcpy(duid->llt.haddr, addr, addr_len);
75
76 *len = sizeof(duid->type) + sizeof(duid->llt.htype) + sizeof(duid->llt.time) + addr_len;
77
78 return 0;
79 }
80
81 int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len) {
82 assert(duid);
83 assert(len);
84 assert(addr);
85
86 if (arp_type == ARPHRD_ETHER)
87 assert_return(addr_len == ETH_ALEN, -EINVAL);
88 else if (arp_type == ARPHRD_INFINIBAND)
89 assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
90 else
91 return -EINVAL;
92
93 unaligned_write_be16(&duid->type, DUID_TYPE_LL);
94 unaligned_write_be16(&duid->ll.htype, arp_type);
95 memcpy(duid->ll.haddr, addr, addr_len);
96
97 *len = sizeof(duid->type) + sizeof(duid->ll.htype) + addr_len;
98
99 return 0;
100 }
101
102 int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) {
103 sd_id128_t machine_id;
104 uint64_t hash;
105 int r;
106
107 assert(duid);
108 assert(len);
109
110 r = sd_id128_get_machine(&machine_id);
111 if (r < 0)
112 return r;
113
114 unaligned_write_be16(&duid->type, DUID_TYPE_EN);
115 unaligned_write_be32(&duid->en.pen, SYSTEMD_PEN);
116
117 *len = sizeof(duid->type) + sizeof(duid->en);
118
119 /* a bit of snake-oil perhaps, but no need to expose the machine-id
120 * directly; duid->en.id might not be aligned, so we need to copy */
121 hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
122 memcpy(duid->en.id, &hash, sizeof(duid->en.id));
123
124 return 0;
125 }
126
127 int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len) {
128 sd_id128_t machine_id;
129 int r;
130
131 assert(duid);
132 assert(len);
133
134 r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id);
135 if (r < 0)
136 return r;
137
138 unaligned_write_be16(&duid->type, DUID_TYPE_UUID);
139 memcpy(&duid->raw.data, &machine_id, sizeof(machine_id));
140
141 *len = sizeof(duid->type) + sizeof(machine_id);
142
143 return 0;
144 }
145
146 int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_id) {
147 /* name is a pointer to memory in the udev_device struct, so must
148 have the same scope */
149 _cleanup_(udev_device_unrefp) struct udev_device *device = NULL;
150 const char *name = NULL;
151 uint64_t id;
152
153 if (detect_container() <= 0) {
154 /* not in a container, udev will be around */
155 _cleanup_(udev_unrefp) struct udev *udev;
156 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
157
158 udev = udev_new();
159 if (!udev)
160 return -ENOMEM;
161
162 sprintf(ifindex_str, "n%d", ifindex);
163 device = udev_device_new_from_device_id(udev, ifindex_str);
164 if (device) {
165 if (udev_device_get_is_initialized(device) <= 0)
166 /* not yet ready */
167 return -EBUSY;
168
169 name = net_get_name(device);
170 }
171 }
172
173 if (name)
174 id = siphash24(name, strlen(name), HASH_KEY.bytes);
175 else
176 /* fall back to MAC address if no predictable name available */
177 id = siphash24(mac, mac_len, HASH_KEY.bytes);
178
179 id = htole64(id);
180
181 /* fold into 32 bits */
182 unaligned_write_be32(_id, (id & 0xffffffff) ^ (id >> 32));
183
184 return 0;
185 }