]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
cfb5b380 | 2 | |
335f80a6 | 3 | #include <linux/if_infiniband.h> |
c07fe6d0 | 4 | #include <net/if_arp.h> |
335f80a6 | 5 | |
51517f9e | 6 | #include "sd-device.h" |
07630cea | 7 | #include "sd-id128.h" |
cfb5b380 | 8 | |
cfb5b380 | 9 | #include "dhcp-identifier.h" |
07630cea | 10 | #include "dhcp6-protocol.h" |
cfb5b380 | 11 | #include "network-internal.h" |
07630cea LP |
12 | #include "siphash24.h" |
13 | #include "sparse-endian.h" | |
29cf0ff8 | 14 | #include "stdio-util.h" |
07630cea | 15 | #include "virt.h" |
cfb5b380 | 16 | |
27eba50e YW |
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 | #define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03) | |
335f80a6 | 20 | #define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */ |
cfb5b380 | 21 | |
ab4a88bc | 22 | int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict) { |
3b6a4e97 ZJS |
23 | struct duid d; |
24 | ||
25 | assert_cc(sizeof(d.raw) >= MAX_DUID_LEN); | |
26 | if (duid_len > MAX_DUID_LEN) | |
27 | return -EINVAL; | |
28 | ||
ab4a88bc TH |
29 | if (!strict) { |
30 | /* Strict validation is not requested. We only ensure that the | |
31 | * DUID is not too long. */ | |
32 | return 0; | |
33 | } | |
34 | ||
3b6a4e97 ZJS |
35 | switch (duid_type) { |
36 | case DUID_TYPE_LLT: | |
37 | if (duid_len <= sizeof(d.llt)) | |
38 | return -EINVAL; | |
39 | break; | |
40 | case DUID_TYPE_EN: | |
41 | if (duid_len != sizeof(d.en)) | |
42 | return -EINVAL; | |
43 | break; | |
44 | case DUID_TYPE_LL: | |
45 | if (duid_len <= sizeof(d.ll)) | |
46 | return -EINVAL; | |
47 | break; | |
48 | case DUID_TYPE_UUID: | |
49 | if (duid_len != sizeof(d.uuid)) | |
50 | return -EINVAL; | |
51 | break; | |
52 | default: | |
53 | /* accept unknown type in order to be forward compatible */ | |
54 | break; | |
55 | } | |
56 | return 0; | |
57 | } | |
58 | ||
335f80a6 YW |
59 | 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) { |
60 | uint16_t time_from_2000y; | |
61 | ||
62 | assert(duid); | |
63 | assert(len); | |
64 | assert(addr); | |
65 | ||
66 | if (arp_type == ARPHRD_ETHER) | |
67 | assert_return(addr_len == ETH_ALEN, -EINVAL); | |
68 | else if (arp_type == ARPHRD_INFINIBAND) | |
69 | assert_return(addr_len == INFINIBAND_ALEN, -EINVAL); | |
70 | else | |
71 | return -EINVAL; | |
72 | ||
73 | if (t < USEC_2000) | |
74 | time_from_2000y = 0; | |
75 | else | |
76 | time_from_2000y = (uint16_t) (((t - USEC_2000) / USEC_PER_SEC) & 0xffffffff); | |
77 | ||
78 | unaligned_write_be16(&duid->type, DUID_TYPE_LLT); | |
79 | unaligned_write_be16(&duid->llt.htype, arp_type); | |
80 | unaligned_write_be32(&duid->llt.time, time_from_2000y); | |
81 | memcpy(duid->llt.haddr, addr, addr_len); | |
82 | ||
83 | *len = sizeof(duid->type) + sizeof(duid->llt.htype) + sizeof(duid->llt.time) + addr_len; | |
84 | ||
85 | return 0; | |
86 | } | |
87 | ||
88 | int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len) { | |
89 | assert(duid); | |
90 | assert(len); | |
91 | assert(addr); | |
92 | ||
93 | if (arp_type == ARPHRD_ETHER) | |
94 | assert_return(addr_len == ETH_ALEN, -EINVAL); | |
95 | else if (arp_type == ARPHRD_INFINIBAND) | |
96 | assert_return(addr_len == INFINIBAND_ALEN, -EINVAL); | |
97 | else | |
98 | return -EINVAL; | |
99 | ||
100 | unaligned_write_be16(&duid->type, DUID_TYPE_LL); | |
101 | unaligned_write_be16(&duid->ll.htype, arp_type); | |
102 | memcpy(duid->ll.haddr, addr, addr_len); | |
103 | ||
104 | *len = sizeof(duid->type) + sizeof(duid->ll.htype) + addr_len; | |
105 | ||
106 | return 0; | |
107 | } | |
108 | ||
cfb5b380 TG |
109 | int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) { |
110 | sd_id128_t machine_id; | |
dbe81cbd | 111 | uint64_t hash; |
cfb5b380 TG |
112 | int r; |
113 | ||
114 | assert(duid); | |
115 | assert(len); | |
116 | ||
117 | r = sd_id128_get_machine(&machine_id); | |
45a7becf EV |
118 | if (r < 0) { |
119 | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION | |
120 | machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10); | |
121 | #else | |
cfb5b380 | 122 | return r; |
45a7becf EV |
123 | #endif |
124 | } | |
cfb5b380 | 125 | |
413708d1 | 126 | unaligned_write_be16(&duid->type, DUID_TYPE_EN); |
9ee18af3 TG |
127 | unaligned_write_be32(&duid->en.pen, SYSTEMD_PEN); |
128 | ||
cfb5b380 TG |
129 | *len = sizeof(duid->type) + sizeof(duid->en); |
130 | ||
131 | /* a bit of snake-oil perhaps, but no need to expose the machine-id | |
27eba50e | 132 | * directly; duid->en.id might not be aligned, so we need to copy */ |
933f9cae | 133 | hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes)); |
dbe81cbd | 134 | memcpy(duid->en.id, &hash, sizeof(duid->en.id)); |
cfb5b380 TG |
135 | |
136 | return 0; | |
137 | } | |
138 | ||
27eba50e YW |
139 | int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len) { |
140 | sd_id128_t machine_id; | |
141 | int r; | |
142 | ||
143 | assert(duid); | |
144 | assert(len); | |
145 | ||
146 | r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id); | |
147 | if (r < 0) | |
148 | return r; | |
149 | ||
150 | unaligned_write_be16(&duid->type, DUID_TYPE_UUID); | |
151 | memcpy(&duid->raw.data, &machine_id, sizeof(machine_id)); | |
152 | ||
153 | *len = sizeof(duid->type) + sizeof(machine_id); | |
154 | ||
155 | return 0; | |
156 | } | |
157 | ||
6d13616b TH |
158 | int dhcp_identifier_set_iaid( |
159 | int ifindex, | |
160 | const uint8_t *mac, | |
161 | size_t mac_len, | |
162 | bool legacy_unstable_byteorder, | |
163 | void *_id) { | |
51517f9e YW |
164 | /* name is a pointer to memory in the sd_device struct, so must |
165 | * have the same scope */ | |
166 | _cleanup_(sd_device_unrefp) sd_device *device = NULL; | |
cfb5b380 TG |
167 | const char *name = NULL; |
168 | uint64_t id; | |
6d13616b | 169 | uint32_t id32; |
cfb5b380 | 170 | |
75f86906 | 171 | if (detect_container() <= 0) { |
cfb5b380 | 172 | /* not in a container, udev will be around */ |
29cf0ff8 | 173 | char ifindex_str[1 + DECIMAL_STR_MAX(int)]; |
5a937ea2 | 174 | int r; |
cfb5b380 | 175 | |
29cf0ff8 | 176 | xsprintf(ifindex_str, "n%d", ifindex); |
51517f9e | 177 | if (sd_device_new_from_device_id(&device, ifindex_str) >= 0) { |
5a937ea2 | 178 | r = sd_device_get_is_initialized(device); |
51517f9e YW |
179 | if (r < 0) |
180 | return r; | |
5a937ea2 | 181 | if (r == 0) |
40862866 TG |
182 | /* not yet ready */ |
183 | return -EBUSY; | |
cfb5b380 | 184 | |
40862866 TG |
185 | name = net_get_name(device); |
186 | } | |
cfb5b380 TG |
187 | } |
188 | ||
189 | if (name) | |
933f9cae | 190 | id = siphash24(name, strlen(name), HASH_KEY.bytes); |
cfb5b380 | 191 | else |
e2acdb6b | 192 | /* fall back to MAC address if no predictable name available */ |
933f9cae DM |
193 | id = siphash24(mac, mac_len, HASH_KEY.bytes); |
194 | ||
6d13616b | 195 | id32 = (id & 0xffffffff) ^ (id >> 32); |
cfb5b380 | 196 | |
6d13616b TH |
197 | if (legacy_unstable_byteorder) |
198 | /* for historical reasons (a bug), the bits were swapped and thus | |
01dab40b | 199 | * the result was endianness dependent. Preserve that behavior. */ |
6d13616b TH |
200 | id32 = __bswap_32(id32); |
201 | else | |
202 | /* the fixed behavior returns a stable byte order. Since LE is expected | |
203 | * to be more common, swap the bytes on LE to give the same as legacy | |
204 | * behavior. */ | |
205 | id32 = be32toh(id32); | |
cfb5b380 | 206 | |
6d13616b | 207 | unaligned_write_ne32(_id, id32); |
cfb5b380 TG |
208 | return 0; |
209 | } |