]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/netif-util.c
Merge pull request #32326 from jonathan-conder/man_pam_loadkey
[thirdparty/systemd.git] / src / shared / netif-util.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <linux/if.h>
4 #include <linux/if_arp.h>
5
6 #include "arphrd-util.h"
7 #include "device-util.h"
8 #include "hexdecoct.h"
9 #include "log-link.h"
10 #include "memory-util.h"
11 #include "netif-naming-scheme.h"
12 #include "netif-util.h"
13 #include "siphash24.h"
14 #include "sparse-endian.h"
15 #include "strv.h"
16
17 #define SHORTEN_IFNAME_HASH_KEY SD_ID128_MAKE(e1,90,a4,04,a8,ef,4b,51,8c,cc,c3,3a,9f,11,fc,a2)
18
19 bool netif_has_carrier(uint8_t operstate, unsigned flags) {
20 /* see Documentation/networking/operstates.txt in the kernel sources */
21
22 if (operstate == IF_OPER_UP)
23 return true;
24
25 if (operstate != IF_OPER_UNKNOWN)
26 return false;
27
28 /* operstate may not be implemented, so fall back to flags */
29 return FLAGS_SET(flags, IFF_LOWER_UP | IFF_RUNNING) &&
30 !FLAGS_SET(flags, IFF_DORMANT);
31 }
32
33 int net_get_type_string(sd_device *device, uint16_t iftype, char **ret) {
34 const char *t;
35 char *p;
36
37 if (device &&
38 sd_device_get_devtype(device, &t) >= 0 &&
39 !isempty(t))
40 return strdup_to(ret, t);
41
42 t = arphrd_to_name(iftype);
43 if (!t)
44 return -ENOENT;
45
46 p = strdup(t);
47 if (!p)
48 return -ENOMEM;
49
50 *ret = ascii_strlower(p);
51 return 0;
52 }
53
54 const char *net_get_persistent_name(sd_device *device) {
55 assert(device);
56
57 /* fetch some persistent data unique (on this machine) to this device */
58 FOREACH_STRING(field, "ID_NET_NAME_ONBOARD", "ID_NET_NAME_SLOT", "ID_NET_NAME_PATH", "ID_NET_NAME_MAC") {
59 const char *name;
60
61 if (sd_device_get_property_value(device, field, &name) >= 0)
62 return name;
63 }
64
65 return NULL;
66 }
67
68 /* Used when generating hardware address by udev, and IPv4LL seed by networkd. */
69 #define HASH_KEY SD_ID128_MAKE(d3,1e,48,fa,90,fe,4b,4c,9d,af,d5,d7,a1,b1,2e,8a)
70
71 int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *ret) {
72 const char *name;
73
74 assert(device);
75 assert(ret);
76
77 /* net_get_persistent_name() will return one of the device names based on stable information about
78 * the device. If this is not available, we fall back to using the actual device name. */
79 name = net_get_persistent_name(device);
80 if (!name && use_sysname)
81 (void) sd_device_get_sysname(device, &name);
82 if (!name)
83 return log_device_debug_errno(device, SYNTHETIC_ERRNO(ENODATA),
84 "No stable identifying information found");
85
86 log_device_debug(device, "Using \"%s\" as stable identifying information", name);
87
88 return net_get_unique_predictable_data_from_name(name, &HASH_KEY, ret);
89 }
90
91 int net_get_unique_predictable_data_from_name(
92 const char *name,
93 const sd_id128_t *key,
94 uint64_t *ret) {
95
96 size_t l, sz;
97 uint8_t *v;
98 int r;
99
100 assert(name);
101 assert(key);
102 assert(ret);
103
104 l = strlen(name);
105 sz = sizeof(sd_id128_t) + l;
106 v = newa(uint8_t, sz);
107
108 /* Fetch some persistent data unique to this machine */
109 r = sd_id128_get_machine((sd_id128_t*) v);
110 if (r < 0)
111 return r;
112
113 memcpy(v + sizeof(sd_id128_t), name, l);
114
115 /* Let's hash the machine ID plus the device name. We use
116 * a fixed, but originally randomly created hash key here. */
117 *ret = htole64(siphash24(v, sz, key->bytes));
118 return 0;
119 }
120
121 typedef struct Link {
122 const char *ifname;
123 } Link;
124
125 int net_verify_hardware_address(
126 const char *ifname,
127 bool is_static,
128 uint16_t iftype,
129 const struct hw_addr_data *ib_hw_addr, /* current or parent HW address */
130 struct hw_addr_data *new_hw_addr) {
131
132 Link link = { .ifname = ifname };
133
134 assert(new_hw_addr);
135
136 if (new_hw_addr->length == 0)
137 return 0;
138
139 if (new_hw_addr->length != arphrd_to_hw_addr_len(iftype)) {
140 if (is_static)
141 log_link_warning(&link,
142 "Specified MAC address with invalid length (%zu, expected %zu), refusing.",
143 new_hw_addr->length, arphrd_to_hw_addr_len(iftype));
144 return -EINVAL;
145 }
146
147 switch (iftype) {
148 case ARPHRD_ETHER:
149 /* see eth_random_addr() in the kernel */
150
151 if (ether_addr_is_null(&new_hw_addr->ether)) {
152 if (is_static)
153 log_link_warning(&link, "Specified MAC address is null, refusing.");
154 return -EINVAL;
155 }
156
157 if (ether_addr_is_broadcast(&new_hw_addr->ether)) {
158 if (is_static)
159 log_link_warning(&link, "Specified MAC address is broadcast, refusing.");
160 return -EINVAL;
161 }
162
163 if (ether_addr_is_multicast(&new_hw_addr->ether)) {
164 if (is_static)
165 log_link_warning(&link, "Specified MAC address has the multicast bit set, clearing the bit.");
166
167 new_hw_addr->bytes[0] &= 0xfe;
168 }
169
170 if (!is_static && !ether_addr_is_local(&new_hw_addr->ether))
171 /* Adjust local assignment bit when the MAC address is generated randomly. */
172 new_hw_addr->bytes[0] |= 0x02;
173
174 break;
175
176 case ARPHRD_INFINIBAND:
177 /* see ipoib_check_lladdr() in the kernel */
178
179 assert(ib_hw_addr);
180 assert(ib_hw_addr->length == INFINIBAND_ALEN);
181
182 if (is_static &&
183 (!memeqzero(new_hw_addr->bytes, INFINIBAND_ALEN - 8) ||
184 memcmp(new_hw_addr->bytes, ib_hw_addr->bytes, INFINIBAND_ALEN - 8) != 0))
185 log_link_warning(&link, "Only the last 8 bytes of the InifniBand MAC address can be changed, ignoring the first 12 bytes.");
186
187 if (memeqzero(new_hw_addr->bytes + INFINIBAND_ALEN - 8, 8)) {
188 if (is_static)
189 log_link_warning(&link, "The last 8 bytes of the InfiniBand MAC address cannot be null, refusing.");
190 return -EINVAL;
191 }
192
193 memcpy(new_hw_addr->bytes, ib_hw_addr->bytes, INFINIBAND_ALEN - 8);
194 break;
195
196 default:
197 if (is_static)
198 log_link_warning(&link, "Unsupported interface type %s%u to set MAC address, refusing.",
199 strna(arphrd_to_name(iftype)), iftype);
200 return -EINVAL;
201 }
202
203 return 0;
204 }
205
206 int net_generate_mac(
207 const char *machine_name,
208 struct ether_addr *mac,
209 sd_id128_t hash_key,
210 uint64_t idx) {
211
212 uint64_t result;
213 size_t l, sz;
214 uint8_t *v, *i;
215 int r;
216
217 l = strlen(machine_name);
218 sz = sizeof(sd_id128_t) + l;
219 if (idx > 0)
220 sz += sizeof(idx);
221
222 v = newa(uint8_t, sz);
223
224 /* fetch some persistent data unique to the host */
225 r = sd_id128_get_machine((sd_id128_t*) v);
226 if (r < 0)
227 return r;
228
229 /* combine with some data unique (on this host) to this
230 * container instance */
231 i = mempcpy(v + sizeof(sd_id128_t), machine_name, l);
232 if (idx > 0) {
233 idx = htole64(idx);
234 memcpy(i, &idx, sizeof(idx));
235 }
236
237 /* Let's hash the host machine ID plus the container name. We
238 * use a fixed, but originally randomly created hash key here. */
239 result = htole64(siphash24(v, sz, hash_key.bytes));
240
241 assert_cc(ETH_ALEN <= sizeof(result));
242 memcpy(mac->ether_addr_octet, &result, ETH_ALEN);
243
244 ether_addr_mark_random(mac);
245
246 return 0;
247 }
248
249 int net_shorten_ifname(char *ifname, bool check_naming_scheme) {
250 char new_ifname[IFNAMSIZ];
251
252 assert(ifname);
253
254 if (strlen(ifname) < IFNAMSIZ) /* Name is short enough */
255 return 0;
256
257 if (!check_naming_scheme || naming_scheme_has(NAMING_NSPAWN_LONG_HASH)) {
258 uint64_t h;
259
260 /* Calculate 64-bit hash value */
261 h = siphash24(ifname, strlen(ifname), SHORTEN_IFNAME_HASH_KEY.bytes);
262
263 /* Set the final four bytes (i.e. 32-bit) to the lower 24bit of the hash, encoded in url-safe base64 */
264 memcpy(new_ifname, ifname, IFNAMSIZ - 5);
265 new_ifname[IFNAMSIZ - 5] = urlsafe_base64char(h >> 18);
266 new_ifname[IFNAMSIZ - 4] = urlsafe_base64char(h >> 12);
267 new_ifname[IFNAMSIZ - 3] = urlsafe_base64char(h >> 6);
268 new_ifname[IFNAMSIZ - 2] = urlsafe_base64char(h);
269 } else
270 /* On old nspawn versions we just truncated the name, provide compatibility */
271 memcpy(new_ifname, ifname, IFNAMSIZ-1);
272
273 new_ifname[IFNAMSIZ - 1] = 0;
274
275 /* Log the incident to make it more discoverable */
276 log_warning("Network interface name '%s' has been changed to '%s' to fit length constraints.", ifname, new_ifname);
277
278 strcpy(ifname, new_ifname);
279 return 1;
280 }