]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/dhcp-identifier.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / libsystemd-network / dhcp-identifier.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright (C) 2015 Tom Gundersen <teg@jklmen>
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include "libudev.h"
22 #include "sd-id128.h"
23
24 #include "dhcp-identifier.h"
25 #include "dhcp6-protocol.h"
26 #include "network-internal.h"
27 #include "siphash24.h"
28 #include "sparse-endian.h"
29 #include "udev-util.h"
30 #include "virt.h"
31
32 #define SYSTEMD_PEN 43793
33 #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
34
35 int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len) {
36 struct duid d;
37
38 assert_cc(sizeof(d.raw) >= MAX_DUID_LEN);
39 if (duid_len > MAX_DUID_LEN)
40 return -EINVAL;
41
42 switch (duid_type) {
43 case DUID_TYPE_LLT:
44 if (duid_len <= sizeof(d.llt))
45 return -EINVAL;
46 break;
47 case DUID_TYPE_EN:
48 if (duid_len != sizeof(d.en))
49 return -EINVAL;
50 break;
51 case DUID_TYPE_LL:
52 if (duid_len <= sizeof(d.ll))
53 return -EINVAL;
54 break;
55 case DUID_TYPE_UUID:
56 if (duid_len != sizeof(d.uuid))
57 return -EINVAL;
58 break;
59 default:
60 /* accept unknown type in order to be forward compatible */
61 break;
62 }
63 return 0;
64 }
65
66 int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) {
67 sd_id128_t machine_id;
68 uint64_t hash;
69 int r;
70
71 assert(duid);
72 assert(len);
73
74 r = sd_id128_get_machine(&machine_id);
75 if (r < 0)
76 return r;
77
78 unaligned_write_be16(&duid->type, DUID_TYPE_EN);
79 unaligned_write_be32(&duid->en.pen, SYSTEMD_PEN);
80
81 *len = sizeof(duid->type) + sizeof(duid->en);
82
83 /* a bit of snake-oil perhaps, but no need to expose the machine-id
84 directly; duid->en.id might not be aligned, so we need to copy */
85 hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
86 memcpy(duid->en.id, &hash, sizeof(duid->en.id));
87
88 return 0;
89 }
90
91 int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_id) {
92 /* name is a pointer to memory in the udev_device struct, so must
93 have the same scope */
94 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
95 const char *name = NULL;
96 uint64_t id;
97
98 if (detect_container() <= 0) {
99 /* not in a container, udev will be around */
100 _cleanup_udev_unref_ struct udev *udev;
101 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
102
103 udev = udev_new();
104 if (!udev)
105 return -ENOMEM;
106
107 sprintf(ifindex_str, "n%d", ifindex);
108 device = udev_device_new_from_device_id(udev, ifindex_str);
109 if (device) {
110 if (udev_device_get_is_initialized(device) <= 0)
111 /* not yet ready */
112 return -EBUSY;
113
114 name = net_get_name(device);
115 }
116 }
117
118 if (name)
119 id = siphash24(name, strlen(name), HASH_KEY.bytes);
120 else
121 /* fall back to MAC address if no predictable name available */
122 id = siphash24(mac, mac_len, HASH_KEY.bytes);
123
124 id = htole64(id);
125
126 /* fold into 32 bits */
127 unaligned_write_be32(_id, (id & 0xffffffff) ^ (id >> 32));
128
129 return 0;
130 }