]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
cfb5b380 | 2 | |
335f80a6 | 3 | #include <linux/if_infiniband.h> |
dc7f6c9b | 4 | #include <net/ethernet.h> |
c07fe6d0 | 5 | #include <net/if_arp.h> |
335f80a6 | 6 | |
cfb5b380 | 7 | #include "dhcp-identifier.h" |
b5cc5591 | 8 | #include "netif-util.h" |
8b50b319 | 9 | #include "network-common.h" |
07630cea LP |
10 | #include "siphash24.h" |
11 | #include "sparse-endian.h" | |
f9971018 | 12 | #include "string-table.h" |
cfb5b380 | 13 | |
27eba50e YW |
14 | #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09) |
15 | #define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03) | |
335f80a6 | 16 | #define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */ |
cfb5b380 | 17 | |
f9971018 | 18 | static const char * const duid_type_table[_DUID_TYPE_MAX] = { |
6ed69be9 YW |
19 | [DUID_TYPE_LLT] = "DUID-LLT", |
20 | [DUID_TYPE_EN] = "DUID-EN/Vendor", | |
21 | [DUID_TYPE_LL] = "DUID-LL", | |
22 | [DUID_TYPE_UUID] = "UUID", | |
f9971018 YW |
23 | }; |
24 | ||
25 | DEFINE_STRING_TABLE_LOOKUP_TO_STRING(duid_type, DUIDType); | |
26 | ||
53488ea3 | 27 | int dhcp_identifier_set_duid_llt( |
8cad358e YW |
28 | const struct hw_addr_data *hw_addr, |
29 | uint16_t arp_type, | |
30 | usec_t t, | |
31 | struct duid *ret_duid, | |
32 | size_t *ret_len) { | |
33 | ||
335f80a6 YW |
34 | uint16_t time_from_2000y; |
35 | ||
8cad358e | 36 | assert(hw_addr); |
5e1618fa YW |
37 | assert(ret_duid); |
38 | assert(ret_len); | |
39 | ||
8cad358e | 40 | if (hw_addr->length == 0) |
5e1618fa | 41 | return -EOPNOTSUPP; |
335f80a6 YW |
42 | |
43 | if (arp_type == ARPHRD_ETHER) | |
8cad358e | 44 | assert_return(hw_addr->length == ETH_ALEN, -EINVAL); |
335f80a6 | 45 | else if (arp_type == ARPHRD_INFINIBAND) |
8cad358e | 46 | assert_return(hw_addr->length == INFINIBAND_ALEN, -EINVAL); |
335f80a6 | 47 | else |
5e1618fa | 48 | return -EOPNOTSUPP; |
335f80a6 YW |
49 | |
50 | if (t < USEC_2000) | |
51 | time_from_2000y = 0; | |
52 | else | |
53 | time_from_2000y = (uint16_t) (((t - USEC_2000) / USEC_PER_SEC) & 0xffffffff); | |
54 | ||
5e1618fa YW |
55 | unaligned_write_be16(&ret_duid->type, DUID_TYPE_LLT); |
56 | unaligned_write_be16(&ret_duid->llt.htype, arp_type); | |
57 | unaligned_write_be32(&ret_duid->llt.time, time_from_2000y); | |
8cad358e | 58 | memcpy(ret_duid->llt.haddr, hw_addr->bytes, hw_addr->length); |
335f80a6 | 59 | |
8cad358e | 60 | *ret_len = offsetof(struct duid, llt.haddr) + hw_addr->length; |
335f80a6 YW |
61 | |
62 | return 0; | |
63 | } | |
64 | ||
53488ea3 | 65 | int dhcp_identifier_set_duid_ll( |
8cad358e YW |
66 | const struct hw_addr_data *hw_addr, |
67 | uint16_t arp_type, | |
68 | struct duid *ret_duid, | |
69 | size_t *ret_len) { | |
70 | ||
71 | assert(hw_addr); | |
5e1618fa YW |
72 | assert(ret_duid); |
73 | assert(ret_len); | |
74 | ||
8cad358e | 75 | if (hw_addr->length == 0) |
5e1618fa | 76 | return -EOPNOTSUPP; |
335f80a6 YW |
77 | |
78 | if (arp_type == ARPHRD_ETHER) | |
8cad358e | 79 | assert_return(hw_addr->length == ETH_ALEN, -EINVAL); |
335f80a6 | 80 | else if (arp_type == ARPHRD_INFINIBAND) |
8cad358e | 81 | assert_return(hw_addr->length == INFINIBAND_ALEN, -EINVAL); |
335f80a6 | 82 | else |
5e1618fa | 83 | return -EOPNOTSUPP; |
335f80a6 | 84 | |
5e1618fa YW |
85 | unaligned_write_be16(&ret_duid->type, DUID_TYPE_LL); |
86 | unaligned_write_be16(&ret_duid->ll.htype, arp_type); | |
8cad358e | 87 | memcpy(ret_duid->ll.haddr, hw_addr->bytes, hw_addr->length); |
335f80a6 | 88 | |
8cad358e | 89 | *ret_len = offsetof(struct duid, ll.haddr) + hw_addr->length; |
335f80a6 YW |
90 | |
91 | return 0; | |
92 | } | |
93 | ||
8b50b319 | 94 | int dhcp_identifier_set_duid_en(struct duid *ret_duid, size_t *ret_len) { |
cfb5b380 | 95 | sd_id128_t machine_id; |
8b50b319 | 96 | bool test_mode; |
dbe81cbd | 97 | uint64_t hash; |
ac680f76 | 98 | int r; |
cfb5b380 | 99 | |
5e1618fa YW |
100 | assert(ret_duid); |
101 | assert(ret_len); | |
cfb5b380 | 102 | |
8b50b319 YW |
103 | test_mode = network_test_mode_enabled(); |
104 | ||
ac680f76 YW |
105 | if (!test_mode) { |
106 | r = sd_id128_get_machine(&machine_id); | |
107 | if (r < 0) | |
108 | return r; | |
109 | } else | |
110 | /* For tests, especially for fuzzers, reproducibility is important. | |
111 | * Hence, use a static and constant machine ID. | |
112 | * See 9216fddc5a8ac2742e6cfa7660f95c20ca4f2193. */ | |
113 | machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10); | |
cfb5b380 | 114 | |
5e1618fa YW |
115 | unaligned_write_be16(&ret_duid->type, DUID_TYPE_EN); |
116 | unaligned_write_be32(&ret_duid->en.pen, SYSTEMD_PEN); | |
cfb5b380 TG |
117 | |
118 | /* a bit of snake-oil perhaps, but no need to expose the machine-id | |
27eba50e | 119 | * directly; duid->en.id might not be aligned, so we need to copy */ |
933f9cae | 120 | hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes)); |
2da796ca | 121 | memcpy(ret_duid->en.id, &hash, sizeof(hash)); |
5e1618fa | 122 | |
2da796ca | 123 | *ret_len = offsetof(struct duid, en.id) + sizeof(hash); |
cfb5b380 | 124 | |
ac680f76 YW |
125 | if (test_mode) |
126 | assert_se(memcmp(ret_duid, (const uint8_t[]) { 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2 }, *ret_len) == 0); | |
127 | ||
cfb5b380 TG |
128 | return 0; |
129 | } | |
130 | ||
53488ea3 | 131 | int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len) { |
27eba50e YW |
132 | sd_id128_t machine_id; |
133 | int r; | |
134 | ||
5e1618fa YW |
135 | assert(ret_duid); |
136 | assert(ret_len); | |
27eba50e YW |
137 | |
138 | r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id); | |
139 | if (r < 0) | |
140 | return r; | |
141 | ||
5e1618fa | 142 | unaligned_write_be16(&ret_duid->type, DUID_TYPE_UUID); |
c9333c23 | 143 | memcpy(&ret_duid->uuid.uuid, &machine_id, sizeof(machine_id)); |
27eba50e | 144 | |
c9333c23 | 145 | *ret_len = offsetof(struct duid, uuid.uuid) + sizeof(machine_id); |
27eba50e YW |
146 | |
147 | return 0; | |
148 | } | |
149 | ||
53488ea3 | 150 | int dhcp_identifier_set_duid_raw( |
5e1618fa | 151 | DUIDType duid_type, |
53488ea3 YW |
152 | const uint8_t *buf, |
153 | size_t buf_len, | |
5e1618fa YW |
154 | struct duid *ret_duid, |
155 | size_t *ret_len) { | |
156 | ||
53488ea3 YW |
157 | assert(buf || buf_len == 0); |
158 | assert(ret_duid); | |
159 | assert(ret_len); | |
160 | ||
161 | if (duid_type < 0 || duid_type > UINT16_MAX) | |
162 | return -EINVAL; | |
163 | ||
92914960 | 164 | if (buf_len > MAX_DUID_DATA_LEN) |
5e1618fa | 165 | return -EINVAL; |
53488ea3 YW |
166 | |
167 | unaligned_write_be16(&ret_duid->type, duid_type); | |
168 | memcpy_safe(ret_duid->raw.data, buf, buf_len); | |
169 | ||
170 | *ret_len = offsetof(struct duid, raw.data) + buf_len; | |
171 | return 0; | |
5e1618fa YW |
172 | } |
173 | ||
6d13616b | 174 | int dhcp_identifier_set_iaid( |
14805b14 | 175 | sd_device *dev, |
3b75435d | 176 | const struct hw_addr_data *hw_addr, |
6d13616b | 177 | bool legacy_unstable_byteorder, |
5e1618fa | 178 | void *ret) { |
af7b405d | 179 | |
cfb5b380 | 180 | const char *name = NULL; |
6d13616b | 181 | uint32_t id32; |
af7b405d | 182 | uint64_t id; |
cfb5b380 | 183 | |
3b75435d YW |
184 | assert(hw_addr); |
185 | assert(ret); | |
186 | ||
14805b14 YW |
187 | if (dev) |
188 | name = net_get_persistent_name(dev); | |
cfb5b380 | 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 */ |
3b75435d | 193 | id = siphash24(hw_addr->bytes, hw_addr->length, HASH_KEY.bytes); |
933f9cae | 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. */ |
fe92eb79 | 200 | id32 = bswap_32(id32); |
6d13616b TH |
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 | |
5e1618fa | 207 | unaligned_write_ne32(ret, id32); |
cfb5b380 TG |
208 | return 0; |
209 | } |