1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include "alloc-util.h"
4 #include "ether-addr-util.h"
5 #include "hashmap.h"
6 #include "networkd-dhcp-server-static-lease.h"
7 #include "networkd-network.h"
8 #include "networkd-util.h"
9 
10 DEFINE_SECTION_CLEANUP_FUNCTIONS(DHCPStaticLease, dhcp_static_lease_free);
11 
dhcp_static_lease_free(DHCPStaticLease * static_lease)12 DHCPStaticLease *dhcp_static_lease_free(DHCPStaticLease *static_lease) {
13         if (!static_lease)
14                 return NULL;
15 
16         if (static_lease->network && static_lease->section)
17                 hashmap_remove(static_lease->network->dhcp_static_leases_by_section, static_lease->section);
18 
19         config_section_free(static_lease->section);
20         free(static_lease->client_id);
21         return mfree(static_lease);
22 }
23 
dhcp_static_lease_new(DHCPStaticLease ** ret)24 static int dhcp_static_lease_new(DHCPStaticLease **ret) {
25         DHCPStaticLease *p;
26 
27         assert(ret);
28 
29         p = new0(DHCPStaticLease, 1);
30         if (!p)
31                 return -ENOMEM;
32 
33         *ret = TAKE_PTR(p);
34         return 0;
35 }
36 
lease_new_static(Network * network,const char * filename,unsigned section_line,DHCPStaticLease ** ret)37 static int lease_new_static(Network *network, const char *filename, unsigned section_line, DHCPStaticLease **ret) {
38         _cleanup_(config_section_freep) ConfigSection *n = NULL;
39         _cleanup_(dhcp_static_lease_freep) DHCPStaticLease *static_lease = NULL;
40         int r;
41 
42         assert(network);
43         assert(filename);
44         assert(section_line > 0);
45         assert(ret);
46 
47         r = config_section_new(filename, section_line, &n);
48         if (r < 0)
49                 return r;
50 
51         static_lease = hashmap_get(network->dhcp_static_leases_by_section, n);
52         if (static_lease) {
53                 *ret = TAKE_PTR(static_lease);
54                 return 0;
55         }
56 
57         r = dhcp_static_lease_new(&static_lease);
58         if (r < 0)
59                 return r;
60 
61         static_lease->network = network;
62         static_lease->section = TAKE_PTR(n);
63         r = hashmap_ensure_put(&network->dhcp_static_leases_by_section, &config_section_hash_ops, static_lease->section, static_lease);
64         if (r < 0)
65                 return r;
66 
67         *ret = TAKE_PTR(static_lease);
68         return 0;
69 }
70 
static_lease_verify(DHCPStaticLease * static_lease)71 static int static_lease_verify(DHCPStaticLease *static_lease) {
72         if (section_is_invalid(static_lease->section))
73                 return -EINVAL;
74 
75         if (in4_addr_is_null(&static_lease->address))
76                 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
77                                          "%s: DHCP static lease without Address= field configured. "
78                                          "Ignoring [DHCPServerStaticLease] section from line %u.",
79                                          static_lease->section->filename, static_lease->section->line);
80 
81         /* TODO: check that the address is in the pool. */
82 
83         if (static_lease->client_id_size == 0 || !static_lease->client_id)
84                 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
85                                          "%s: DHCP static lease without MACAddress= field configured. "
86                                          "Ignoring [DHCPServerStaticLease] section from line %u.",
87                                          static_lease->section->filename, static_lease->section->line);
88 
89         assert(static_lease->client_id_size == ETH_ALEN + 1);
90 
91         return 0;
92 }
93 
network_drop_invalid_static_leases(Network * network)94 void network_drop_invalid_static_leases(Network *network) {
95         DHCPStaticLease *static_lease;
96 
97         assert(network);
98 
99         HASHMAP_FOREACH(static_lease, network->dhcp_static_leases_by_section)
100                 if (static_lease_verify(static_lease) < 0)
101                         dhcp_static_lease_free(static_lease);
102 }
103 
config_parse_dhcp_static_lease_address(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)104 int config_parse_dhcp_static_lease_address(
105                 const char *unit,
106                 const char *filename,
107                 unsigned line,
108                 const char *section,
109                 unsigned section_line,
110                 const char *lvalue,
111                 int ltype,
112                 const char *rvalue,
113                 void *data,
114                 void *userdata) {
115 
116         _cleanup_(dhcp_static_lease_free_or_set_invalidp) DHCPStaticLease *lease = NULL;
117         Network *network = userdata;
118         union in_addr_union addr;
119         int r;
120 
121         assert(filename);
122         assert(lvalue);
123         assert(rvalue);
124         assert(network);
125 
126         r = lease_new_static(network, filename, section_line, &lease);
127         if (r < 0)
128                 return log_oom();
129 
130         if (isempty(rvalue)) {
131                 lease->address.s_addr = 0;
132                 TAKE_PTR(lease);
133                 return 0;
134         }
135 
136         r = in_addr_from_string(AF_INET, rvalue, &addr);
137         if (r < 0) {
138                 log_syntax(unit, LOG_WARNING, filename, line, r,
139                            "Failed to parse IPv4 address for DHCPv4 static lease, ignoring assignment: %s", rvalue);
140                 return 0;
141         }
142         if (in4_addr_is_null(&addr.in)) {
143                 log_syntax(unit, LOG_WARNING, filename, line, 0,
144                            "IPv4 address for DHCPv4 static lease cannot be the ANY address, ignoring assignment: %s", rvalue);
145                 return 0;
146         }
147 
148         lease->address = addr.in;
149 
150         TAKE_PTR(lease);
151         return 0;
152 }
153 
config_parse_dhcp_static_lease_hwaddr(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)154 int config_parse_dhcp_static_lease_hwaddr(
155                 const char *unit,
156                 const char *filename,
157                 unsigned line,
158                 const char *section,
159                 unsigned section_line,
160                 const char *lvalue,
161                 int ltype,
162                 const char *rvalue,
163                 void *data,
164                 void *userdata) {
165 
166         _cleanup_(dhcp_static_lease_free_or_set_invalidp) DHCPStaticLease *lease = NULL;
167         Network *network = userdata;
168         struct ether_addr hwaddr;
169         uint8_t *c;
170         int r;
171 
172         assert(filename);
173         assert(lvalue);
174         assert(rvalue);
175         assert(network);
176 
177         r = lease_new_static(network, filename, section_line, &lease);
178         if (r < 0)
179                 return log_oom();
180 
181         if (isempty(rvalue)) {
182                 lease->client_id = mfree(lease->client_id);
183                 lease->client_id_size = 0;
184                 return 0;
185         }
186 
187         r = parse_ether_addr(rvalue, &hwaddr);
188         if (r < 0) {
189                 log_syntax(unit, LOG_WARNING, filename, line, r,
190                            "Failed to parse MAC address for DHCPv4 static lease, ignoring assignment: %s", rvalue);
191                 return 0;
192         }
193         if (ether_addr_is_null(&hwaddr) || (hwaddr.ether_addr_octet[0] & 0x01)) {
194                 log_syntax(unit, LOG_WARNING, filename, line, 0,
195                            "MAC address for DHCPv4 static lease cannot be null or multicast, ignoring assignment: %s", rvalue);
196                 return 0;
197         }
198 
199         c = new(uint8_t, ETH_ALEN + 1);
200         if (!c)
201                 return log_oom();
202 
203         /* set client id type to 1: Ethernet Link-Layer (RFC 2132) */
204         c[0] = 0x01;
205         memcpy(c + 1, &hwaddr, ETH_ALEN);
206 
207         free_and_replace(lease->client_id, c);
208         lease->client_id_size = ETH_ALEN + 1;
209 
210         TAKE_PTR(lease);
211         return 0;
212 }
213