]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/netif-util.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
4 #include <linux/if_arp.h>
6 #include "arphrd-util.h"
7 #include "device-util.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"
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)
19 bool netif_has_carrier(uint8_t operstate
, unsigned flags
) {
20 /* see Documentation/networking/operstates.txt in the kernel sources */
22 if (operstate
== IF_OPER_UP
)
25 if (operstate
!= IF_OPER_UNKNOWN
)
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
);
33 int net_get_type_string(sd_device
*device
, uint16_t iftype
, char **ret
) {
38 sd_device_get_devtype(device
, &t
) >= 0 &&
40 return strdup_to(ret
, t
);
42 t
= arphrd_to_name(iftype
);
50 *ret
= ascii_strlower(p
);
54 const char *net_get_persistent_name(sd_device
*device
) {
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") {
61 if (sd_device_get_property_value(device
, field
, &name
) >= 0)
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)
71 int net_get_unique_predictable_data(sd_device
*device
, bool use_sysname
, uint64_t *ret
) {
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
);
83 return log_device_debug_errno(device
, SYNTHETIC_ERRNO(ENODATA
),
84 "No stable identifying information found");
86 log_device_debug(device
, "Using \"%s\" as stable identifying information", name
);
88 return net_get_unique_predictable_data_from_name(name
, &HASH_KEY
, ret
);
91 int net_get_unique_predictable_data_from_name(
93 const sd_id128_t
*key
,
105 sz
= sizeof(sd_id128_t
) + l
;
106 v
= newa(uint8_t, sz
);
108 /* Fetch some persistent data unique to this machine */
109 r
= sd_id128_get_machine((sd_id128_t
*) v
);
113 memcpy(v
+ sizeof(sd_id128_t
), name
, l
);
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
));
121 typedef struct Link
{
125 int net_verify_hardware_address(
129 const struct hw_addr_data
*ib_hw_addr
, /* current or parent HW address */
130 struct hw_addr_data
*new_hw_addr
) {
132 Link link
= { .ifname
= ifname
};
136 if (new_hw_addr
->length
== 0)
139 if (new_hw_addr
->length
!= arphrd_to_hw_addr_len(iftype
)) {
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
));
149 /* see eth_random_addr() in the kernel */
151 if (ether_addr_is_null(&new_hw_addr
->ether
)) {
153 log_link_warning(&link
, "Specified MAC address is null, refusing.");
157 if (ether_addr_is_broadcast(&new_hw_addr
->ether
)) {
159 log_link_warning(&link
, "Specified MAC address is broadcast, refusing.");
163 if (ether_addr_is_multicast(&new_hw_addr
->ether
)) {
165 log_link_warning(&link
, "Specified MAC address has the multicast bit set, clearing the bit.");
167 new_hw_addr
->bytes
[0] &= 0xfe;
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;
176 case ARPHRD_INFINIBAND
:
177 /* see ipoib_check_lladdr() in the kernel */
180 assert(ib_hw_addr
->length
== INFINIBAND_ALEN
);
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.");
187 if (memeqzero(new_hw_addr
->bytes
+ INFINIBAND_ALEN
- 8, 8)) {
189 log_link_warning(&link
, "The last 8 bytes of the InfiniBand MAC address cannot be null, refusing.");
193 memcpy(new_hw_addr
->bytes
, ib_hw_addr
->bytes
, INFINIBAND_ALEN
- 8);
198 log_link_warning(&link
, "Unsupported interface type %s%u to set MAC address, refusing.",
199 strna(arphrd_to_name(iftype
)), iftype
);
206 int net_generate_mac(
207 const char *machine_name
,
208 struct ether_addr
*mac
,
217 l
= strlen(machine_name
);
218 sz
= sizeof(sd_id128_t
) + l
;
222 v
= newa(uint8_t, sz
);
224 /* fetch some persistent data unique to the host */
225 r
= sd_id128_get_machine((sd_id128_t
*) v
);
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
);
234 memcpy(i
, &idx
, sizeof(idx
));
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
));
241 assert_cc(ETH_ALEN
<= sizeof(result
));
242 memcpy(mac
->ether_addr_octet
, &result
, ETH_ALEN
);
244 ether_addr_mark_random(mac
);
249 int net_shorten_ifname(char *ifname
, bool check_naming_scheme
) {
250 char new_ifname
[IFNAMSIZ
];
254 if (strlen(ifname
) < IFNAMSIZ
) /* Name is short enough */
257 if (!check_naming_scheme
|| naming_scheme_has(NAMING_NSPAWN_LONG_HASH
)) {
260 /* Calculate 64-bit hash value */
261 h
= siphash24(ifname
, strlen(ifname
), SHORTEN_IFNAME_HASH_KEY
.bytes
);
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
);
270 /* On old nspawn versions we just truncated the name, provide compatibility */
271 memcpy(new_ifname
, ifname
, IFNAMSIZ
-1);
273 new_ifname
[IFNAMSIZ
- 1] = 0;
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
);
278 strcpy(ifname
, new_ifname
);