blob: 9471f5cd03617f34b1f07360ccbf2ae46cc6b4d3 [file] [log] [blame]
Simon Kelley2a8710a2020-01-05 16:40:06 +00001/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
Simon Kelleyc72daea2012-01-05 21:33:27 +00002
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
15*/
16
17
18#include "dnsmasq.h"
19
20#ifdef HAVE_DHCP6
21
Simon Kelleya6ebfac2013-03-06 20:52:35 +000022struct state {
23 unsigned char *clid;
Dominik DL6ER456a3192019-10-20 18:51:52 +020024 int clid_len, ia_type, interface, hostname_auth, lease_allocate;
Simon Kelleya6ebfac2013-03-06 20:52:35 +000025 char *client_hostname, *hostname, *domain, *send_domain;
26 struct dhcp_context *context;
Simon Kelleyc3a04082014-01-11 22:18:19 +000027 struct in6_addr *link_address, *fallback, *ll_addr, *ula_addr;
Dominik DL6ER456a3192019-10-20 18:51:52 +020028 unsigned int xid, fqdn_flags, iaid;
Simon Kelleya6ebfac2013-03-06 20:52:35 +000029 char *iface_name;
30 void *packet_options, *end;
31 struct dhcp_netid *tags, *context_tags;
Simon Kelley8939c952013-09-25 11:49:34 +010032 unsigned char mac[DHCP_CHADDR_MAX];
Simon Kelley89500e32013-09-20 16:29:20 +010033 unsigned int mac_len, mac_type;
Simon Kelleya6ebfac2013-03-06 20:52:35 +000034};
35
Simon Kelley8939c952013-09-25 11:49:34 +010036static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz,
37 struct in6_addr *client_addr, int is_unicast, time_t now);
Simon Kelleyf1af2bb2013-09-24 09:16:28 +010038static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_t sz, int is_unicast, time_t now);
Simon Kelley6c8f21e2012-03-12 15:06:55 +000039static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_opts);
Simon Kelleya6ebfac2013-03-06 20:52:35 +000040static void log6_packet(struct state *state, char *type, struct in6_addr *addr, char *string);
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +010041static void log6_quiet(struct state *state, char *type, struct in6_addr *addr, char *string);
Simon Kelley4cb1b322012-02-06 14:30:41 +000042static void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsize);
43static void *opt6_next(void *opts, void *end);
44static unsigned int opt6_uint(unsigned char *opt, int offset, int size);
Simon Kelleya6ebfac2013-03-06 20:52:35 +000045static void get_context_tag(struct state *state, struct dhcp_context *context);
Simon Kelley3a654c52013-03-06 22:17:48 +000046static int check_ia(struct state *state, void *opt, void **endp, void **ia_option);
Simon Kelleya6ebfac2013-03-06 20:52:35 +000047static int build_ia(struct state *state, int *t1cntr);
48static void end_ia(int t1cntr, unsigned int min_time, int do_fuzz);
Simon Kelleyf1af2bb2013-09-24 09:16:28 +010049static void mark_context_used(struct state *state, struct in6_addr *addr);
Simon Kelleyde92b472013-03-15 18:25:10 +000050static void mark_config_used(struct dhcp_context *context, struct in6_addr *addr);
51static int check_address(struct state *state, struct in6_addr *addr);
Simon Kelleycc4baaa2013-08-05 15:03:44 +010052static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, void *ia_option,
Simon Kelleyc8f2dd82013-09-13 11:22:55 +010053 unsigned int *min_time, struct in6_addr *addr, time_t now);
Simon Kelleya6ebfac2013-03-06 20:52:35 +000054static void update_leases(struct state *state, struct dhcp_context *context, struct in6_addr *addr, unsigned int lease_time, time_t now);
55static int add_local_addrs(struct dhcp_context *context);
Simon Kelleyf1af2bb2013-09-24 09:16:28 +010056static struct dhcp_netid *add_options(struct state *state, int do_refresh);
Simon Kelleyde92b472013-03-15 18:25:10 +000057static void calculate_times(struct dhcp_context *context, unsigned int *min_time, unsigned int *valid_timep,
Simon Kelleycc4baaa2013-08-05 15:03:44 +010058 unsigned int *preferred_timep, unsigned int lease_time);
Simon Kelley62779782012-02-10 21:19:25 +000059
Simon Kelley4cb1b322012-02-06 14:30:41 +000060#define opt6_len(opt) ((int)(opt6_uint(opt, -2, 2)))
61#define opt6_type(opt) (opt6_uint(opt, -4, 2))
62#define opt6_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[4+(i)]))
63
Tanguy Bouzelocef1d7422013-10-03 11:06:31 +010064#define opt6_user_vendor_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[2+(i)]))
65#define opt6_user_vendor_len(opt) ((int)(opt6_uint(opt, -4, 2)))
66#define opt6_user_vendor_next(opt, end) (opt6_next(((void *) opt) - 2, end))
67
Simon Kelley4cb1b322012-02-06 14:30:41 +000068
Simon Kelley1d0f91c2012-03-12 11:56:22 +000069unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
Simon Kelleyc3a04082014-01-11 22:18:19 +000070 struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr,
71 size_t sz, struct in6_addr *client_addr, time_t now)
Simon Kelleyc72daea2012-01-05 21:33:27 +000072{
Simon Kelley4cb1b322012-02-06 14:30:41 +000073 struct dhcp_vendor *vendor;
Simon Kelley1d0f91c2012-03-12 11:56:22 +000074 int msg_type;
Simon Kelleyf1af2bb2013-09-24 09:16:28 +010075 struct state state;
Simon Kelley1d0f91c2012-03-12 11:56:22 +000076
77 if (sz <= 4)
78 return 0;
79
80 msg_type = *((unsigned char *)daemon->dhcp_packet.iov_base);
81
Simon Kelley4cb1b322012-02-06 14:30:41 +000082 /* Mark these so we only match each at most once, to avoid tangled linked lists */
83 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
84 vendor->netid.next = &vendor->netid;
Simon Kelleyc72daea2012-01-05 21:33:27 +000085
Simon Kelleyfa785732016-07-22 20:56:01 +010086 reset_counter();
Simon Kelleyf1af2bb2013-09-24 09:16:28 +010087 state.context = context;
88 state.interface = interface;
89 state.iface_name = iface_name;
90 state.fallback = fallback;
Simon Kelleyc3a04082014-01-11 22:18:19 +000091 state.ll_addr = ll_addr;
92 state.ula_addr = ula_addr;
Simon Kelley8939c952013-09-25 11:49:34 +010093 state.mac_len = 0;
Simon Kelleyf1af2bb2013-09-24 09:16:28 +010094 state.tags = NULL;
95 state.link_address = NULL;
96
Simon Kelley8939c952013-09-25 11:49:34 +010097 if (dhcp6_maybe_relay(&state, daemon->dhcp_packet.iov_base, sz, client_addr,
98 IN6_IS_ADDR_MULTICAST(client_addr), now))
Simon Kelley1d0f91c2012-03-12 11:56:22 +000099 return msg_type == DHCP6RELAYFORW ? DHCPV6_SERVER_PORT : DHCPV6_CLIENT_PORT;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000100
101 return 0;
102}
103
Simon Kelley4cb1b322012-02-06 14:30:41 +0000104/* This cost me blood to write, it will probably cost you blood to understand - srk. */
Simon Kelley8939c952013-09-25 11:49:34 +0100105static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz,
106 struct in6_addr *client_addr, int is_unicast, time_t now)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000107{
108 void *end = inbuff + sz;
109 void *opts = inbuff + 34;
110 int msg_type = *((unsigned char *)inbuff);
111 unsigned char *outmsgtypep;
112 void *opt;
113 struct dhcp_vendor *vendor;
114
Josh Soref730c6742017-02-06 16:14:04 +0000115 /* if not an encapsulated relayed message, just do the stuff */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000116 if (msg_type != DHCP6RELAYFORW)
117 {
118 /* if link_address != NULL if points to the link address field of the
119 innermost nested RELAYFORW message, which is where we find the
120 address of the network on which we can allocate an address.
Simon Kelley8939c952013-09-25 11:49:34 +0100121 Recalculate the available contexts using that information.
122
123 link_address == NULL means there's no relay in use, so we try and find the client's
124 MAC address from the local ND cache. */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000125
Simon Kelley8939c952013-09-25 11:49:34 +0100126 if (!state->link_address)
Simon Kelley33702ab2015-12-28 23:17:15 +0000127 get_client_mac(client_addr, state->interface, state->mac, &state->mac_len, &state->mac_type, now);
Simon Kelley8939c952013-09-25 11:49:34 +0100128 else
Simon Kelley4cb1b322012-02-06 14:30:41 +0000129 {
130 struct dhcp_context *c;
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000131 struct shared_network *share = NULL;
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100132 state->context = NULL;
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000133
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100134 if (!IN6_IS_ADDR_LOOPBACK(state->link_address) &&
135 !IN6_IS_ADDR_LINKLOCAL(state->link_address) &&
136 !IN6_IS_ADDR_MULTICAST(state->link_address))
Simon Kelleybaeb3ad2013-01-10 11:47:38 +0000137 for (c = daemon->dhcp6; c; c = c->next)
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000138 {
139 for (share = daemon->shared_networks; share; share = share->next)
140 {
141 if (share->shared_addr.s_addr != 0)
142 continue;
143
144 if (share->if_index != 0 ||
145 !IN6_ARE_ADDR_EQUAL(state->link_address, &share->match_addr6))
146 continue;
147
148 if ((c->flags & CONTEXT_DHCP) &&
149 !(c->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
150 is_same_net6(&share->shared_addr6, &c->start6, c->prefix) &&
151 is_same_net6(&share->shared_addr6, &c->end6, c->prefix))
152 break;
153 }
154
155 if (share ||
156 ((c->flags & CONTEXT_DHCP) &&
157 !(c->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
158 is_same_net6(state->link_address, &c->start6, c->prefix) &&
159 is_same_net6(state->link_address, &c->end6, c->prefix)))
160 {
161 c->preferred = c->valid = 0xffffffff;
162 c->current = state->context;
163 state->context = c;
164 }
165 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000166
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100167 if (!state->context)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000168 {
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100169 inet_ntop(AF_INET6, state->link_address, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000170 my_syslog(MS_DHCP | LOG_WARNING,
171 _("no address range available for DHCPv6 request from relay at %s"),
172 daemon->addrbuff);
173 return 0;
174 }
175 }
Simon Kelley8939c952013-09-25 11:49:34 +0100176
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100177 if (!state->context)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000178 {
179 my_syslog(MS_DHCP | LOG_WARNING,
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100180 _("no address range available for DHCPv6 request via %s"), state->iface_name);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000181 return 0;
182 }
183
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100184 return dhcp6_no_relay(state, msg_type, inbuff, sz, is_unicast, now);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000185 }
186
187 /* must have at least msg_type+hopcount+link_address+peer_address+minimal size option
188 which is 1 + 1 + 16 + 16 + 2 + 2 = 38 */
189 if (sz < 38)
190 return 0;
191
192 /* copy header stuff into reply message and set type to reply */
Simon Kelleyff7eea22013-09-04 18:01:38 +0100193 if (!(outmsgtypep = put_opt6(inbuff, 34)))
194 return 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000195 *outmsgtypep = DHCP6RELAYREPL;
196
197 /* look for relay options and set tags if found. */
198 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
199 {
200 int mopt;
201
202 if (vendor->match_type == MATCH_SUBSCRIBER)
203 mopt = OPTION6_SUBSCRIBER_ID;
204 else if (vendor->match_type == MATCH_REMOTE)
205 mopt = OPTION6_REMOTE_ID;
206 else
207 continue;
208
209 if ((opt = opt6_find(opts, end, mopt, 1)) &&
210 vendor->len == opt6_len(opt) &&
211 memcmp(vendor->data, opt6_ptr(opt, 0), vendor->len) == 0 &&
212 vendor->netid.next != &vendor->netid)
213 {
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100214 vendor->netid.next = state->tags;
215 state->tags = &vendor->netid;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000216 break;
217 }
218 }
219
Simon Kelley89500e32013-09-20 16:29:20 +0100220 /* RFC-6939 */
221 if ((opt = opt6_find(opts, end, OPTION6_CLIENT_MAC, 3)))
222 {
Simon Kelley3d4ff1b2017-09-25 18:52:50 +0100223 if (opt6_len(opt) - 2 > DHCP_CHADDR_MAX) {
224 return 0;
225 }
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100226 state->mac_type = opt6_uint(opt, 0, 2);
227 state->mac_len = opt6_len(opt) - 2;
Simon Kelley8939c952013-09-25 11:49:34 +0100228 memcpy(&state->mac[0], opt6_ptr(opt, 2), state->mac_len);
Simon Kelley89500e32013-09-20 16:29:20 +0100229 }
230
Simon Kelley4cb1b322012-02-06 14:30:41 +0000231 for (opt = opts; opt; opt = opt6_next(opt, end))
232 {
yiwenchen499d8dd2018-02-14 22:26:54 +0000233 if (opt6_ptr(opt, 0) + opt6_len(opt) > end)
Simon Kelley33e3f102017-09-25 20:05:11 +0100234 return 0;
yiwenchen499d8dd2018-02-14 22:26:54 +0000235
Simon Kelleyf8c77ed2019-01-10 21:58:18 +0000236 /* Don't copy MAC address into reply. */
237 if (opt6_type(opt) != OPTION6_CLIENT_MAC)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000238 {
Simon Kelleyf8c77ed2019-01-10 21:58:18 +0000239 int o = new_opt6(opt6_type(opt));
240 if (opt6_type(opt) == OPTION6_RELAY_MSG)
241 {
242 struct in6_addr align;
243 /* the packet data is unaligned, copy to aligned storage */
244 memcpy(&align, inbuff + 2, IN6ADDRSZ);
245 state->link_address = &align;
246 /* zero is_unicast since that is now known to refer to the
247 relayed packet, not the original sent by the client */
248 if (!dhcp6_maybe_relay(state, opt6_ptr(opt, 0), opt6_len(opt), client_addr, 0, now))
249 return 0;
250 }
251 else
252 put_opt6(opt6_ptr(opt, 0), opt6_len(opt));
253 end_opt6(o);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000254 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000255 }
256
257 return 1;
258}
259
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100260static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_t sz, int is_unicast, time_t now)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000261{
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000262 void *opt;
263 int i, o, o1, start_opts;
264 struct dhcp_opt *opt_cfg;
265 struct dhcp_netid *tagif;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000266 struct dhcp_config *config = NULL;
Simon Kelley0d28af82012-09-20 21:24:06 +0100267 struct dhcp_netid known_id, iface_id, v6_id;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000268 unsigned char *outmsgtypep;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000269 struct dhcp_vendor *vendor;
270 struct dhcp_context *context_tmp;
Simon Kelley89500e32013-09-20 16:29:20 +0100271 struct dhcp_mac *mac_opt;
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000272 unsigned int ignore = 0;
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000273
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100274 state->packet_options = inbuff + 4;
275 state->end = inbuff + sz;
276 state->clid = NULL;
277 state->clid_len = 0;
278 state->lease_allocate = 0;
279 state->context_tags = NULL;
280 state->domain = NULL;
281 state->send_domain = NULL;
282 state->hostname_auth = 0;
283 state->hostname = NULL;
284 state->client_hostname = NULL;
Josh Soref730c6742017-02-06 16:14:04 +0000285 state->fqdn_flags = 0x01; /* default to send if we receive no FQDN option */
Simon Kelley4cb1b322012-02-06 14:30:41 +0000286
Simon Kelleyceae00d2012-02-09 21:28:14 +0000287 /* set tag with name == interface */
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100288 iface_id.net = state->iface_name;
289 iface_id.next = state->tags;
290 state->tags = &iface_id;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000291
Simon Kelley23780dd2012-10-23 17:04:37 +0100292 /* set tag "dhcpv6" */
293 v6_id.net = "dhcpv6";
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100294 v6_id.next = state->tags;
295 state->tags = &v6_id;
Simon Kelley0d28af82012-09-20 21:24:06 +0100296
Simon Kelley4cb1b322012-02-06 14:30:41 +0000297 /* copy over transaction-id, and save pointer to message type */
Simon Kelleyff7eea22013-09-04 18:01:38 +0100298 if (!(outmsgtypep = put_opt6(inbuff, 4)))
299 return 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000300 start_opts = save_counter(-1);
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100301 state->xid = outmsgtypep[3] | outmsgtypep[2] << 8 | outmsgtypep[1] << 16;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000302
303 /* We're going to be linking tags from all context we use.
304 mark them as unused so we don't link one twice and break the list */
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100305 for (context_tmp = state->context; context_tmp; context_tmp = context_tmp->current)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000306 {
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100307 context_tmp->netid.next = &context_tmp->netid;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000308
309 if (option_bool(OPT_LOG_OPTS))
310 {
311 inet_ntop(AF_INET6, &context_tmp->start6, daemon->dhcp_buff, ADDRSTRLEN);
312 inet_ntop(AF_INET6, &context_tmp->end6, daemon->dhcp_buff2, ADDRSTRLEN);
313 if (context_tmp->flags & (CONTEXT_STATIC))
314 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCPv6 subnet: %s/%d"),
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100315 state->xid, daemon->dhcp_buff, context_tmp->prefix);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000316 else
317 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100318 state->xid, daemon->dhcp_buff, daemon->dhcp_buff2);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000319 }
320 }
321
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100322 if ((opt = opt6_find(state->packet_options, state->end, OPTION6_CLIENT_ID, 1)))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000323 {
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100324 state->clid = opt6_ptr(opt, 0);
325 state->clid_len = opt6_len(opt);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000326 o = new_opt6(OPTION6_CLIENT_ID);
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100327 put_opt6(state->clid, state->clid_len);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000328 end_opt6(o);
329 }
330 else if (msg_type != DHCP6IREQ)
331 return 0;
332
Ilya Ponetaev7f68f822014-09-13 20:52:27 +0100333 /* server-id must match except for SOLICIT, CONFIRM and REBIND messages */
334 if (msg_type != DHCP6SOLICIT && msg_type != DHCP6CONFIRM && msg_type != DHCP6IREQ && msg_type != DHCP6REBIND &&
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100335 (!(opt = opt6_find(state->packet_options, state->end, OPTION6_SERVER_ID, 1)) ||
Simon Kelley4cb1b322012-02-06 14:30:41 +0000336 opt6_len(opt) != daemon->duid_len ||
337 memcmp(opt6_ptr(opt, 0), daemon->duid, daemon->duid_len) != 0))
338 return 0;
339
340 o = new_opt6(OPTION6_SERVER_ID);
341 put_opt6(daemon->duid, daemon->duid_len);
342 end_opt6(o);
343
344 if (is_unicast &&
345 (msg_type == DHCP6REQUEST || msg_type == DHCP6RENEW || msg_type == DHCP6RELEASE || msg_type == DHCP6DECLINE))
346
347 {
Ilya Ponetaev976afc92014-09-13 20:56:14 +0100348 *outmsgtypep = DHCP6REPLY;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000349 o1 = new_opt6(OPTION6_STATUS_CODE);
350 put_opt6_short(DHCP6USEMULTI);
351 put_opt6_string("Use multicast");
352 end_opt6(o1);
353 return 1;
354 }
355
356 /* match vendor and user class options */
357 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
358 {
359 int mopt;
360
361 if (vendor->match_type == MATCH_VENDOR)
362 mopt = OPTION6_VENDOR_CLASS;
363 else if (vendor->match_type == MATCH_USER)
364 mopt = OPTION6_USER_CLASS;
365 else
366 continue;
367
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100368 if ((opt = opt6_find(state->packet_options, state->end, mopt, 2)))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000369 {
370 void *enc_opt, *enc_end = opt6_ptr(opt, opt6_len(opt));
Simon Kelleya5c72ab2012-02-10 13:42:47 +0000371 int offset = 0;
372
373 if (mopt == OPTION6_VENDOR_CLASS)
374 {
375 if (opt6_len(opt) < 4)
376 continue;
377
378 if (vendor->enterprise != opt6_uint(opt, 0, 4))
379 continue;
380
381 offset = 4;
382 }
383
Tanguy Bouzelocef1d7422013-10-03 11:06:31 +0100384 /* Note that format if user/vendor classes is different to DHCP options - no option types. */
385 for (enc_opt = opt6_ptr(opt, offset); enc_opt; enc_opt = opt6_user_vendor_next(enc_opt, enc_end))
386 for (i = 0; i <= (opt6_user_vendor_len(enc_opt) - vendor->len); i++)
387 if (memcmp(vendor->data, opt6_user_vendor_ptr(enc_opt, i), vendor->len) == 0)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000388 {
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100389 vendor->netid.next = state->tags;
390 state->tags = &vendor->netid;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000391 break;
392 }
393 }
394 }
Simon Kelley3634c542012-02-08 14:22:37 +0000395
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100396 if (option_bool(OPT_LOG_OPTS) && (opt = opt6_find(state->packet_options, state->end, OPTION6_VENDOR_CLASS, 4)))
397 my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %u"), state->xid, opt6_uint(opt, 0, 4));
Simon Kelley1567fea2012-03-12 22:15:35 +0000398
Simon Kelley3634c542012-02-08 14:22:37 +0000399 /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
400 Otherwise assume the option is an array, and look for a matching element.
Josh Soref730c6742017-02-06 16:14:04 +0000401 If no data given, existence of the option is enough. This code handles
Simon Kelley3634c542012-02-08 14:22:37 +0000402 V-I opts too. */
403 for (opt_cfg = daemon->dhcp_match6; opt_cfg; opt_cfg = opt_cfg->next)
404 {
Simon Kelleyceae00d2012-02-09 21:28:14 +0000405 int match = 0;
406
Simon Kelley3634c542012-02-08 14:22:37 +0000407 if (opt_cfg->flags & DHOPT_RFC3925)
408 {
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100409 for (opt = opt6_find(state->packet_options, state->end, OPTION6_VENDOR_OPTS, 4);
Simon Kelley3634c542012-02-08 14:22:37 +0000410 opt;
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100411 opt = opt6_find(opt6_next(opt, state->end), state->end, OPTION6_VENDOR_OPTS, 4))
Simon Kelley3634c542012-02-08 14:22:37 +0000412 {
413 void *vopt;
414 void *vend = opt6_ptr(opt, opt6_len(opt));
415
416 for (vopt = opt6_find(opt6_ptr(opt, 4), vend, opt_cfg->opt, 0);
417 vopt;
418 vopt = opt6_find(opt6_next(vopt, vend), vend, opt_cfg->opt, 0))
Simon Kelleyceae00d2012-02-09 21:28:14 +0000419 if ((match = match_bytes(opt_cfg, opt6_ptr(vopt, 0), opt6_len(vopt))))
Simon Kelley3634c542012-02-08 14:22:37 +0000420 break;
421 }
422 if (match)
423 break;
424 }
425 else
426 {
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100427 if (!(opt = opt6_find(state->packet_options, state->end, opt_cfg->opt, 1)))
Simon Kelley3634c542012-02-08 14:22:37 +0000428 continue;
429
430 match = match_bytes(opt_cfg, opt6_ptr(opt, 0), opt6_len(opt));
431 }
432
433 if (match)
434 {
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100435 opt_cfg->netid->next = state->tags;
436 state->tags = opt_cfg->netid;
Simon Kelley3634c542012-02-08 14:22:37 +0000437 }
438 }
Simon Kelley89500e32013-09-20 16:29:20 +0100439
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100440 if (state->mac_len != 0)
Simon Kelleyd81b42d2013-09-23 12:26:34 +0100441 {
442 if (option_bool(OPT_LOG_OPTS))
Simon Kelley89500e32013-09-20 16:29:20 +0100443 {
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100444 print_mac(daemon->dhcp_buff, state->mac, state->mac_len);
445 my_syslog(MS_DHCP | LOG_INFO, _("%u client MAC address: %s"), state->xid, daemon->dhcp_buff);
Simon Kelley89500e32013-09-20 16:29:20 +0100446 }
Simon Kelleyd81b42d2013-09-23 12:26:34 +0100447
448 for (mac_opt = daemon->dhcp_macs; mac_opt; mac_opt = mac_opt->next)
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100449 if ((unsigned)mac_opt->hwaddr_len == state->mac_len &&
450 ((unsigned)mac_opt->hwaddr_type == state->mac_type || mac_opt->hwaddr_type == 0) &&
451 memcmp_masked(mac_opt->hwaddr, state->mac, state->mac_len, mac_opt->mask))
Simon Kelleyd81b42d2013-09-23 12:26:34 +0100452 {
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100453 mac_opt->netid.next = state->tags;
454 state->tags = &mac_opt->netid;
Simon Kelleyd81b42d2013-09-23 12:26:34 +0100455 }
456 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000457
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100458 if ((opt = opt6_find(state->packet_options, state->end, OPTION6_FQDN, 1)))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000459 {
460 /* RFC4704 refers */
461 int len = opt6_len(opt) - 1;
462
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100463 state->fqdn_flags = opt6_uint(opt, 0, 1);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000464
465 /* Always force update, since the client has no way to do it itself. */
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100466 if (!option_bool(OPT_FQDN_UPDATE) && !(state->fqdn_flags & 0x01))
467 state->fqdn_flags |= 0x03;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000468
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100469 state->fqdn_flags &= ~0x04;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000470
471 if (len != 0 && len < 255)
472 {
Simon Kelleyfbbc1452012-03-30 20:48:20 +0100473 unsigned char *pp, *op = opt6_ptr(opt, 1);
474 char *pq = daemon->dhcp_buff;
475
476 pp = op;
477 while (*op != 0 && ((op + (*op)) - pp) < len)
478 {
479 memcpy(pq, op+1, *op);
480 pq += *op;
481 op += (*op)+1;
482 *(pq++) = '.';
483 }
484
485 if (pq != daemon->dhcp_buff)
486 pq--;
487 *pq = 0;
488
489 if (legal_hostname(daemon->dhcp_buff))
490 {
Simon Kelley34d41472019-12-05 23:44:29 +0000491 struct dhcp_match_name *m;
492 size_t nl = strlen(daemon->dhcp_buff);
493
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100494 state->client_hostname = daemon->dhcp_buff;
Simon Kelley34d41472019-12-05 23:44:29 +0000495
Simon Kelleyfbbc1452012-03-30 20:48:20 +0100496 if (option_bool(OPT_LOG_OPTS))
Simon Kelley34d41472019-12-05 23:44:29 +0000497 my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), state->xid, state->client_hostname);
498
499 for (m = daemon->dhcp_name_match; m; m = m->next)
500 {
501 size_t ml = strlen(m->name);
502 char save = 0;
503
504 if (nl < ml)
505 continue;
506 if (nl > ml)
507 {
508 save = state->client_hostname[ml];
509 state->client_hostname[ml] = 0;
510 }
511
512 if (hostname_isequal(state->client_hostname, m->name) &&
513 (save == 0 || m->wildcard))
514 {
515 m->netid->next = state->tags;
516 state->tags = m->netid;
517 }
518
519 if (save != 0)
520 state->client_hostname[ml] = save;
521 }
Simon Kelleyfbbc1452012-03-30 20:48:20 +0100522 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000523 }
524 }
525
Simon Kelley6ebdc952019-10-30 21:04:27 +0000526 if (state->clid &&
527 (config = find_config(daemon->dhcp_conf, state->context, state->clid, state->clid_len, state->mac, state->mac_len, state->mac_type, NULL)) &&
528 have_config(config, CONFIG_NAME))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000529 {
Simon Kelley6ebdc952019-10-30 21:04:27 +0000530 state->hostname = config->hostname;
531 state->domain = config->domain;
532 state->hostname_auth = 1;
533 }
534 else if (state->client_hostname)
535 {
536 state->domain = strip_hostname(state->client_hostname);
Simon Kelley34d41472019-12-05 23:44:29 +0000537
Simon Kelley6ebdc952019-10-30 21:04:27 +0000538 if (strlen(state->client_hostname) != 0)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000539 {
Simon Kelley6ebdc952019-10-30 21:04:27 +0000540 state->hostname = state->client_hostname;
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000541
Simon Kelley6ebdc952019-10-30 21:04:27 +0000542 if (!config)
Simon Kelley4cb1b322012-02-06 14:30:41 +0000543 {
Simon Kelley6ebdc952019-10-30 21:04:27 +0000544 /* Search again now we have a hostname.
545 Only accept configs without CLID here, (it won't match)
546 to avoid impersonation by name. */
547 struct dhcp_config *new = find_config(daemon->dhcp_conf, state->context, NULL, 0, NULL, 0, 0, state->hostname);
548 if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
549 config = new;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000550 }
551 }
552 }
Simon Kelley34d41472019-12-05 23:44:29 +0000553
Simon Kelley4cb1b322012-02-06 14:30:41 +0000554 if (config)
555 {
556 struct dhcp_netid_list *list;
557
558 for (list = config->netid; list; list = list->next)
559 {
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100560 list->list->next = state->tags;
561 state->tags = list->list;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000562 }
563
564 /* set "known" tag for known hosts */
565 known_id.net = "known";
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100566 known_id.next = state->tags;
567 state->tags = &known_id;
Simon Kelley3634c542012-02-08 14:22:37 +0000568
569 if (have_config(config, CONFIG_DISABLE))
570 ignore = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000571 }
Simon Kelleyb2a9c572017-04-30 18:21:31 +0100572 else if (state->clid &&
573 find_config(daemon->dhcp_conf, NULL, state->clid, state->clid_len, state->mac, state->mac_len, state->mac_type, NULL))
574 {
575 known_id.net = "known-othernet";
576 known_id.next = state->tags;
577 state->tags = &known_id;
578 }
579
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100580 tagif = run_tag_if(state->tags);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000581
Simon Kelley3634c542012-02-08 14:22:37 +0000582 /* if all the netids in the ignore list are present, ignore this client */
583 if (daemon->dhcp_ignore)
584 {
585 struct dhcp_netid_list *id_list;
586
Simon Kelley3634c542012-02-08 14:22:37 +0000587 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
588 if (match_netid(id_list->list, tagif, 0))
589 ignore = 1;
590 }
Simon Kelley00e9ad52012-02-16 21:53:11 +0000591
592 /* if all the netids in the ignore_name list are present, ignore client-supplied name */
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100593 if (!state->hostname_auth)
Simon Kelley00e9ad52012-02-16 21:53:11 +0000594 {
595 struct dhcp_netid_list *id_list;
596
597 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
598 if ((!id_list->list) || match_netid(id_list->list, tagif, 0))
599 break;
600 if (id_list)
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100601 state->hostname = NULL;
Simon Kelley00e9ad52012-02-16 21:53:11 +0000602 }
603
Simon Kelley4cb1b322012-02-06 14:30:41 +0000604
605 switch (msg_type)
606 {
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000607 default:
608 return 0;
Simon Kelleyc8f2dd82013-09-13 11:22:55 +0100609
610
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000611 case DHCP6SOLICIT:
612 {
Simon Kelleyde92b472013-03-15 18:25:10 +0000613 int address_assigned = 0;
Simon Kelleyc6309242013-03-07 20:59:28 +0000614 /* tags without all prefix-class tags */
Simon Kelley8f51a292013-09-21 14:07:12 +0100615 struct dhcp_netid *solicit_tags;
Simon Kelleyde92b472013-03-15 18:25:10 +0000616 struct dhcp_context *c;
Simon Kelleyc8f2dd82013-09-13 11:22:55 +0100617
618 *outmsgtypep = DHCP6ADVERTISE;
619
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100620 if (opt6_find(state->packet_options, state->end, OPTION6_RAPID_COMMIT, 0))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000621 {
Simon Kelleyc8f2dd82013-09-13 11:22:55 +0100622 *outmsgtypep = DHCP6REPLY;
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100623 state->lease_allocate = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000624 o = new_opt6(OPTION6_RAPID_COMMIT);
625 end_opt6(o);
626 }
Simon Kelley3634c542012-02-08 14:22:37 +0000627
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100628 log6_quiet(state, "DHCPSOLICIT", NULL, ignore ? _("ignored") : NULL);
Simon Kelleyc8f2dd82013-09-13 11:22:55 +0100629
630 request_no_address:
Simon Kelley8f51a292013-09-21 14:07:12 +0100631 solicit_tags = tagif;
632
Simon Kelley3634c542012-02-08 14:22:37 +0000633 if (ignore)
634 return 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000635
Simon Kelley52a1ae72013-03-06 22:43:26 +0000636 /* reset USED bits in leases */
637 lease6_reset();
Simon Kelleyde92b472013-03-15 18:25:10 +0000638
639 /* Can use configured address max once per prefix */
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100640 for (c = state->context; c; c = c->current)
Simon Kelleyde92b472013-03-15 18:25:10 +0000641 c->flags &= ~CONTEXT_CONF_USED;
642
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100643 for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000644 {
Simon Kelley4cb1b322012-02-06 14:30:41 +0000645 void *ia_option, *ia_end;
646 unsigned int min_time = 0xffffffff;
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000647 int t1cntr;
Simon Kelleyc6309242013-03-07 20:59:28 +0000648 int ia_counter;
649 /* set unless we're sending a particular prefix-class, when we
650 want only dhcp-ranges with the correct tags set and not those without any tags. */
651 int plain_range = 1;
Simon Kelleycc4baaa2013-08-05 15:03:44 +0100652 u32 lease_time;
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000653 struct dhcp_lease *ltmp;
Simon Kelley97f876b2018-08-21 22:06:36 +0100654 struct in6_addr req_addr, addr;
655
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100656 if (!check_ia(state, opt, &ia_end, &ia_option))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000657 continue;
658
Simon Kelley52a1ae72013-03-06 22:43:26 +0000659 /* reset USED bits in contexts - one address per prefix per IAID */
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100660 for (c = state->context; c; c = c->current)
Simon Kelley52a1ae72013-03-06 22:43:26 +0000661 c->flags &= ~CONTEXT_USED;
Simon Kelleyc6309242013-03-07 20:59:28 +0000662
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100663 o = build_ia(state, &t1cntr);
Vladislav Grishenkob9ff5c82014-10-06 14:34:24 +0100664 if (address_assigned)
665 address_assigned = 2;
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000666
Simon Kelleyc6309242013-03-07 20:59:28 +0000667 for (ia_counter = 0; ia_option; ia_counter++, ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000668 {
Simon Kelley97f876b2018-08-21 22:06:36 +0100669 /* worry about alignment here. */
670 memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
Simon Kelleycc4baaa2013-08-05 15:03:44 +0100671
Simon Kelley97f876b2018-08-21 22:06:36 +0100672 if ((c = address6_valid(state->context, &req_addr, solicit_tags, plain_range)))
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000673 {
674 lease_time = c->lease_time;
675 /* If the client asks for an address on the same network as a configured address,
676 offer the configured address instead, to make moving to newly-configured
677 addresses automatic. */
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100678 if (!(c->flags & CONTEXT_CONF_USED) && config_valid(config, c, &addr) && check_address(state, &addr))
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000679 {
Simon Kelley97f876b2018-08-21 22:06:36 +0100680 req_addr = addr;
Simon Kelleyde92b472013-03-15 18:25:10 +0000681 mark_config_used(c, &addr);
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000682 if (have_config(config, CONFIG_TIME))
683 lease_time = config->lease_time;
684 }
Simon Kelley97f876b2018-08-21 22:06:36 +0100685 else if (!(c = address6_available(state->context, &req_addr, solicit_tags, plain_range)))
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000686 continue; /* not an address we're allowed */
Simon Kelley97f876b2018-08-21 22:06:36 +0100687 else if (!check_address(state, &req_addr))
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000688 continue; /* address leased elsewhere */
689
690 /* add address to output packet */
Simon Kelley97f876b2018-08-21 22:06:36 +0100691 add_address(state, c, lease_time, ia_option, &min_time, &req_addr, now);
692 mark_context_used(state, &req_addr);
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100693 get_context_tag(state, c);
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000694 address_assigned = 1;
695 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000696 }
697
Simon Kelleyde92b472013-03-15 18:25:10 +0000698 /* Suggest configured address(es) */
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100699 for (c = state->context; c; c = c->current)
Simon Kelleya1a79ed2013-03-15 21:19:57 +0000700 if (!(c->flags & CONTEXT_CONF_USED) &&
701 match_netid(c->filter, solicit_tags, plain_range) &&
702 config_valid(config, c, &addr) &&
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100703 check_address(state, &addr))
Simon Kelleyde92b472013-03-15 18:25:10 +0000704 {
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100705 mark_config_used(state->context, &addr);
Simon Kelleyde92b472013-03-15 18:25:10 +0000706 if (have_config(config, CONFIG_TIME))
707 lease_time = config->lease_time;
Simon Kelleya1a79ed2013-03-15 21:19:57 +0000708 else
709 lease_time = c->lease_time;
Simon Kelley6c1e9ac2020-01-07 22:04:07 +0000710
Simon Kelleyde92b472013-03-15 18:25:10 +0000711 /* add address to output packet */
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100712 add_address(state, c, lease_time, NULL, &min_time, &addr, now);
713 mark_context_used(state, &addr);
714 get_context_tag(state, c);
Simon Kelleyde92b472013-03-15 18:25:10 +0000715 address_assigned = 1;
716 }
717
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000718 /* return addresses for existing leases */
719 ltmp = NULL;
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100720 while ((ltmp = lease6_find_by_client(ltmp, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, state->clid, state->clid_len, state->iaid)))
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000721 {
Simon Kelley97f876b2018-08-21 22:06:36 +0100722 req_addr = ltmp->addr6;
723 if ((c = address6_available(state->context, &req_addr, solicit_tags, plain_range)))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000724 {
Simon Kelley97f876b2018-08-21 22:06:36 +0100725 add_address(state, c, c->lease_time, NULL, &min_time, &req_addr, now);
726 mark_context_used(state, &req_addr);
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100727 get_context_tag(state, c);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000728 address_assigned = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000729 }
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000730 }
731
732 /* Return addresses for all valid contexts which don't yet have one */
Simon Kelley6586e832013-11-07 14:20:13 +0000733 while ((c = address6_allocate(state->context, state->clid, state->clid_len, state->ia_type == OPTION6_IA_TA,
734 state->iaid, ia_counter, solicit_tags, plain_range, &addr)))
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000735 {
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100736 add_address(state, c, c->lease_time, NULL, &min_time, &addr, now);
737 mark_context_used(state, &addr);
738 get_context_tag(state, c);
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000739 address_assigned = 1;
740 }
741
Vladislav Grishenkob9ff5c82014-10-06 14:34:24 +0100742 if (address_assigned != 1)
743 {
744 /* If the server will not assign any addresses to any IAs in a
745 subsequent Request from the client, the server MUST send an Advertise
746 message to the client that doesn't include any IA options. */
747 if (!state->lease_allocate)
748 {
749 save_counter(o);
750 continue;
751 }
752
753 /* If the server cannot assign any addresses to an IA in the message
754 from the client, the server MUST include the IA in the Reply message
755 with no addresses in the IA and a Status Code option in the IA
756 containing status code NoAddrsAvail. */
757 o1 = new_opt6(OPTION6_STATUS_CODE);
758 put_opt6_short(DHCP6NOADDRS);
759 put_opt6_string(_("address unavailable"));
760 end_opt6(o1);
761 }
762
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000763 end_ia(t1cntr, min_time, 0);
764 end_opt6(o);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000765 }
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000766
767 if (address_assigned)
768 {
769 o1 = new_opt6(OPTION6_STATUS_CODE);
770 put_opt6_short(DHCP6SUCCESS);
Simon Kelleyde92b472013-03-15 18:25:10 +0000771 put_opt6_string(_("success"));
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000772 end_opt6(o1);
773
774 /* If --dhcp-authoritative is set, we can tell client not to wait for
775 other possible servers */
776 o = new_opt6(OPTION6_PREFERENCE);
777 put_opt6_char(option_bool(OPT_AUTHORITATIVE) ? 255 : 0);
778 end_opt6(o);
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100779 tagif = add_options(state, 0);
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000780 }
781 else
782 {
783 /* no address, return error */
784 o1 = new_opt6(OPTION6_STATUS_CODE);
785 put_opt6_short(DHCP6NOADDRS);
Simon Kelleyde92b472013-03-15 18:25:10 +0000786 put_opt6_string(_("no addresses available"));
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000787 end_opt6(o1);
Simon Kelley338b3402015-04-20 21:34:05 +0100788
789 /* Some clients will ask repeatedly when we're not giving
790 out addresses because we're in stateless mode. Avoid spamming
791 the log in that case. */
792 for (c = state->context; c; c = c->current)
793 if (!(c->flags & CONTEXT_RA_STATELESS))
794 {
795 log6_packet(state, state->lease_allocate ? "DHCPREPLY" : "DHCPADVERTISE", NULL, _("no addresses available"));
796 break;
797 }
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000798 }
799
Simon Kelley4cb1b322012-02-06 14:30:41 +0000800 break;
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000801 }
802
803 case DHCP6REQUEST:
804 {
805 int address_assigned = 0;
Simon Kelleyc8f2dd82013-09-13 11:22:55 +0100806 int start = save_counter(-1);
807
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000808 /* set reply message type */
809 *outmsgtypep = DHCP6REPLY;
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100810 state->lease_allocate = 1;
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000811
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100812 log6_quiet(state, "DHCPREQUEST", NULL, ignore ? _("ignored") : NULL);
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000813
814 if (ignore)
815 return 0;
816
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100817 for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000818 {
819 void *ia_option, *ia_end;
820 unsigned int min_time = 0xffffffff;
821 int t1cntr;
822
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100823 if (!check_ia(state, opt, &ia_end, &ia_option))
Simon Kelley3a654c52013-03-06 22:17:48 +0000824 continue;
Simon Kelleyc8f2dd82013-09-13 11:22:55 +0100825
826 if (!ia_option)
827 {
Ville Skyttäfaaf3062018-01-14 17:32:52 +0000828 /* If we get a request with an IA_*A without addresses, treat it exactly like
Simon Kelleyc8f2dd82013-09-13 11:22:55 +0100829 a SOLICT with rapid commit set. */
830 save_counter(start);
831 goto request_no_address;
832 }
833
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100834 o = build_ia(state, &t1cntr);
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000835
836 for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
837 {
Simon Kelley97f876b2018-08-21 22:06:36 +0100838 struct in6_addr req_addr;
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000839 struct dhcp_context *dynamic, *c;
840 unsigned int lease_time;
Simon Kelleyde92b472013-03-15 18:25:10 +0000841 struct in6_addr addr;
842 int config_ok = 0;
Simon Kelley97f876b2018-08-21 22:06:36 +0100843
844 /* align. */
845 memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
Simon Kelleyde92b472013-03-15 18:25:10 +0000846
Simon Kelley97f876b2018-08-21 22:06:36 +0100847 if ((c = address6_valid(state->context, &req_addr, tagif, 1)))
848 config_ok = config_valid(config, c, &addr) && IN6_ARE_ADDR_EQUAL(&addr, &req_addr);
Simon Kelleyde92b472013-03-15 18:25:10 +0000849
Simon Kelley97f876b2018-08-21 22:06:36 +0100850 if ((dynamic = address6_available(state->context, &req_addr, tagif, 1)) || c)
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000851 {
Simon Kelleyde92b472013-03-15 18:25:10 +0000852 if (!dynamic && !config_ok)
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000853 {
854 /* Static range, not configured. */
855 o1 = new_opt6(OPTION6_STATUS_CODE);
Vladislav Grishenkob9ff5c82014-10-06 14:34:24 +0100856 put_opt6_short(DHCP6NOADDRS);
Simon Kelleyde92b472013-03-15 18:25:10 +0000857 put_opt6_string(_("address unavailable"));
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000858 end_opt6(o1);
859 }
Simon Kelley97f876b2018-08-21 22:06:36 +0100860 else if (!check_address(state, &req_addr))
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000861 {
862 /* Address leased to another DUID/IAID */
863 o1 = new_opt6(OPTION6_STATUS_CODE);
864 put_opt6_short(DHCP6UNSPEC);
Simon Kelleyde92b472013-03-15 18:25:10 +0000865 put_opt6_string(_("address in use"));
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000866 end_opt6(o1);
867 }
868 else
869 {
870 if (!dynamic)
871 dynamic = c;
872
873 lease_time = dynamic->lease_time;
874
Simon Kelleyde92b472013-03-15 18:25:10 +0000875 if (config_ok && have_config(config, CONFIG_TIME))
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000876 lease_time = config->lease_time;
877
Simon Kelley97f876b2018-08-21 22:06:36 +0100878 add_address(state, dynamic, lease_time, ia_option, &min_time, &req_addr, now);
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100879 get_context_tag(state, dynamic);
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000880 address_assigned = 1;
881 }
882 }
Simon Kelleyde92b472013-03-15 18:25:10 +0000883 else
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000884 {
885 /* requested address not on the correct link */
886 o1 = new_opt6(OPTION6_STATUS_CODE);
887 put_opt6_short(DHCP6NOTONLINK);
Simon Kelleyde92b472013-03-15 18:25:10 +0000888 put_opt6_string(_("not on link"));
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000889 end_opt6(o1);
890 }
891 }
892
893 end_ia(t1cntr, min_time, 0);
894 end_opt6(o);
895 }
896
897 if (address_assigned)
898 {
899 o1 = new_opt6(OPTION6_STATUS_CODE);
900 put_opt6_short(DHCP6SUCCESS);
Simon Kelleyde92b472013-03-15 18:25:10 +0000901 put_opt6_string(_("success"));
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000902 end_opt6(o1);
903 }
904 else
905 {
906 /* no address, return error */
907 o1 = new_opt6(OPTION6_STATUS_CODE);
908 put_opt6_short(DHCP6NOADDRS);
Simon Kelleyde92b472013-03-15 18:25:10 +0000909 put_opt6_string(_("no addresses available"));
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000910 end_opt6(o1);
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100911 log6_packet(state, "DHCPREPLY", NULL, _("no addresses available"));
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000912 }
913
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100914 tagif = add_options(state, 0);
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000915 break;
916 }
917
918
Simon Kelley4cb1b322012-02-06 14:30:41 +0000919 case DHCP6RENEW:
920 {
921 /* set reply message type */
922 *outmsgtypep = DHCP6REPLY;
923
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100924 log6_quiet(state, "DHCPRENEW", NULL, NULL);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000925
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100926 for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000927 {
Simon Kelley4cb1b322012-02-06 14:30:41 +0000928 void *ia_option, *ia_end;
929 unsigned int min_time = 0xffffffff;
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000930 int t1cntr, iacntr;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000931
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100932 if (!check_ia(state, opt, &ia_end, &ia_option))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000933 continue;
934
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100935 o = build_ia(state, &t1cntr);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000936 iacntr = save_counter(-1);
937
Simon Kelleya6ebfac2013-03-06 20:52:35 +0000938 for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000939 {
940 struct dhcp_lease *lease = NULL;
Simon Kelley97f876b2018-08-21 22:06:36 +0100941 struct in6_addr req_addr;
Simon Kelleycc4baaa2013-08-05 15:03:44 +0100942 unsigned int preferred_time = opt6_uint(ia_option, 16, 4);
943 unsigned int valid_time = opt6_uint(ia_option, 20, 4);
Simon Kelleyde92b472013-03-15 18:25:10 +0000944 char *message = NULL;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000945 struct dhcp_context *this_context;
Simon Kelley97f876b2018-08-21 22:06:36 +0100946
947 memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000948
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100949 if (!(lease = lease6_find(state->clid, state->clid_len,
950 state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
Simon Kelley97f876b2018-08-21 22:06:36 +0100951 state->iaid, &req_addr)))
Simon Kelley4cb1b322012-02-06 14:30:41 +0000952 {
953 /* If the server cannot find a client entry for the IA the server
954 returns the IA containing no addresses with a Status Code option set
955 to NoBinding in the Reply message. */
956 save_counter(iacntr);
957 t1cntr = 0;
958
Simon Kelley97f876b2018-08-21 22:06:36 +0100959 log6_packet(state, "DHCPREPLY", &req_addr, _("lease not found"));
Simon Kelley4cb1b322012-02-06 14:30:41 +0000960
961 o1 = new_opt6(OPTION6_STATUS_CODE);
962 put_opt6_short(DHCP6NOBINDING);
Simon Kelleyde92b472013-03-15 18:25:10 +0000963 put_opt6_string(_("no binding found"));
Simon Kelley4cb1b322012-02-06 14:30:41 +0000964 end_opt6(o1);
Simon Kelleycc4baaa2013-08-05 15:03:44 +0100965
966 preferred_time = valid_time = 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000967 break;
968 }
969
Simon Kelley00e9ad52012-02-16 21:53:11 +0000970
Simon Kelley97f876b2018-08-21 22:06:36 +0100971 if ((this_context = address6_available(state->context, &req_addr, tagif, 1)) ||
972 (this_context = address6_valid(state->context, &req_addr, tagif, 1)))
Simon Kelleyc8257542012-03-28 21:15:41 +0100973 {
Simon Kelleyde92b472013-03-15 18:25:10 +0000974 struct in6_addr addr;
975 unsigned int lease_time;
976
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100977 get_context_tag(state, this_context);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000978
Simon Kelley97f876b2018-08-21 22:06:36 +0100979 if (config_valid(config, this_context, &addr) && IN6_ARE_ADDR_EQUAL(&addr, &req_addr) && have_config(config, CONFIG_TIME))
Simon Kelleyde92b472013-03-15 18:25:10 +0000980 lease_time = config->lease_time;
981 else
982 lease_time = this_context->lease_time;
Simon Kelley4cb1b322012-02-06 14:30:41 +0000983
Simon Kelleycc4baaa2013-08-05 15:03:44 +0100984 calculate_times(this_context, &min_time, &valid_time, &preferred_time, lease_time);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000985
Simon Kelleyde92b472013-03-15 18:25:10 +0000986 lease_set_expires(lease, valid_time, now);
Simon Kelley89500e32013-09-20 16:29:20 +0100987 /* Update MAC record in case it's new information. */
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100988 if (state->mac_len != 0)
989 lease_set_hwaddr(lease, state->mac, state->clid, state->mac_len, state->mac_type, state->clid_len, now, 0);
990 if (state->ia_type == OPTION6_IA_NA && state->hostname)
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000991 {
Simon Kelley97f876b2018-08-21 22:06:36 +0100992 char *addr_domain = get_domain6(&req_addr);
Simon Kelleyf1af2bb2013-09-24 09:16:28 +0100993 if (!state->send_domain)
994 state->send_domain = addr_domain;
995 lease_set_hostname(lease, state->hostname, state->hostname_auth, addr_domain, state->domain);
996 message = state->hostname;
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000997 }
Simon Kelley4cb1b322012-02-06 14:30:41 +0000998
Simon Kelleyde92b472013-03-15 18:25:10 +0000999
1000 if (preferred_time == 0)
1001 message = _("deprecated");
Simon Kelley4cb1b322012-02-06 14:30:41 +00001002 }
Simon Kelleyde92b472013-03-15 18:25:10 +00001003 else
Simon Kelleycc4baaa2013-08-05 15:03:44 +01001004 {
1005 preferred_time = valid_time = 0;
1006 message = _("address invalid");
Simon Kelleya5ae1f82015-04-25 21:46:10 +01001007 }
Simon Kelleycc4baaa2013-08-05 15:03:44 +01001008
Simon Kelleya5ae1f82015-04-25 21:46:10 +01001009 if (message && (message != state->hostname))
Simon Kelley97f876b2018-08-21 22:06:36 +01001010 log6_packet(state, "DHCPREPLY", &req_addr, message);
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001011 else
Simon Kelley97f876b2018-08-21 22:06:36 +01001012 log6_quiet(state, "DHCPREPLY", &req_addr, message);
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001013
Simon Kelley4cb1b322012-02-06 14:30:41 +00001014 o1 = new_opt6(OPTION6_IAADDR);
Simon Kelley97f876b2018-08-21 22:06:36 +01001015 put_opt6(&req_addr, sizeof(req_addr));
Simon Kelleyde92b472013-03-15 18:25:10 +00001016 put_opt6_long(preferred_time);
1017 put_opt6_long(valid_time);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001018 end_opt6(o1);
1019 }
1020
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001021 end_ia(t1cntr, min_time, 1);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001022 end_opt6(o);
1023 }
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001024
Simon Kelleyf1af2bb2013-09-24 09:16:28 +01001025 tagif = add_options(state, 0);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001026 break;
1027
1028 }
1029
1030 case DHCP6CONFIRM:
1031 {
Ilya Ponetaev2d75f2e2014-09-13 21:11:16 +01001032 int good_addr = 0;
1033
Simon Kelley4cb1b322012-02-06 14:30:41 +00001034 /* set reply message type */
1035 *outmsgtypep = DHCP6REPLY;
1036
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001037 log6_quiet(state, "DHCPCONFIRM", NULL, NULL);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001038
Simon Kelleyf1af2bb2013-09-24 09:16:28 +01001039 for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
Simon Kelley4cb1b322012-02-06 14:30:41 +00001040 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001041 void *ia_option, *ia_end;
1042
Simon Kelleyf1af2bb2013-09-24 09:16:28 +01001043 for (check_ia(state, opt, &ia_end, &ia_option);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001044 ia_option;
1045 ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
1046 {
Simon Kelley97f876b2018-08-21 22:06:36 +01001047 struct in6_addr req_addr;
1048
1049 /* alignment */
1050 memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001051
Simon Kelley97f876b2018-08-21 22:06:36 +01001052 if (!address6_valid(state->context, &req_addr, tagif, 1))
Simon Kelley4cb1b322012-02-06 14:30:41 +00001053 {
1054 o1 = new_opt6(OPTION6_STATUS_CODE);
1055 put_opt6_short(DHCP6NOTONLINK);
Simon Kelleyde92b472013-03-15 18:25:10 +00001056 put_opt6_string(_("confirm failed"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001057 end_opt6(o1);
Simon Kelley97f876b2018-08-21 22:06:36 +01001058 log6_quiet(state, "DHCPREPLY", &req_addr, _("confirm failed"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001059 return 1;
1060 }
1061
Ilya Ponetaev2d75f2e2014-09-13 21:11:16 +01001062 good_addr = 1;
Simon Kelley97f876b2018-08-21 22:06:36 +01001063 log6_quiet(state, "DHCPREPLY", &req_addr, state->hostname);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001064 }
1065 }
Ilya Ponetaev2d75f2e2014-09-13 21:11:16 +01001066
1067 /* No addresses, no reply: RFC 3315 18.2.2 */
1068 if (!good_addr)
1069 return 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001070
1071 o1 = new_opt6(OPTION6_STATUS_CODE);
1072 put_opt6_short(DHCP6SUCCESS );
Simon Kelleyde92b472013-03-15 18:25:10 +00001073 put_opt6_string(_("all addresses still on link"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001074 end_opt6(o1);
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001075 break;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001076 }
1077
1078 case DHCP6IREQ:
1079 {
Simon Kelleyd1e9a582012-10-23 17:00:57 +01001080 /* We can't discriminate contexts based on address, as we don't know it.
1081 If there is only one possible context, we can use its tags */
Simon Kelleyf1af2bb2013-09-24 09:16:28 +01001082 if (state->context && state->context->netid.net && !state->context->current)
Simon Kelleyd1e9a582012-10-23 17:00:57 +01001083 {
Simon Kelleyf1af2bb2013-09-24 09:16:28 +01001084 state->context->netid.next = NULL;
1085 state->context_tags = &state->context->netid;
Simon Kelleyd1e9a582012-10-23 17:00:57 +01001086 }
Simon Kelley6bd109a2013-07-27 15:11:44 +01001087
1088 /* Similarly, we can't determine domain from address, but if the FQDN is
1089 given in --dhcp-host, we can use that, and failing that we can use the
1090 unqualified configured domain, if any. */
Simon Kelleyf1af2bb2013-09-24 09:16:28 +01001091 if (state->hostname_auth)
1092 state->send_domain = state->domain;
Simon Kelley6bd109a2013-07-27 15:11:44 +01001093 else
Simon Kelleyf1af2bb2013-09-24 09:16:28 +01001094 state->send_domain = get_domain6(NULL);
Simon Kelley6bd109a2013-07-27 15:11:44 +01001095
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001096 log6_quiet(state, "DHCPINFORMATION-REQUEST", NULL, ignore ? _("ignored") : state->hostname);
Simon Kelley3634c542012-02-08 14:22:37 +00001097 if (ignore)
1098 return 0;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001099 *outmsgtypep = DHCP6REPLY;
Simon Kelleyf1af2bb2013-09-24 09:16:28 +01001100 tagif = add_options(state, 1);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001101 break;
1102 }
1103
1104
1105 case DHCP6RELEASE:
1106 {
1107 /* set reply message type */
1108 *outmsgtypep = DHCP6REPLY;
1109
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001110 log6_quiet(state, "DHCPRELEASE", NULL, NULL);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001111
Simon Kelleyf1af2bb2013-09-24 09:16:28 +01001112 for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
Simon Kelley4cb1b322012-02-06 14:30:41 +00001113 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001114 void *ia_option, *ia_end;
1115 int made_ia = 0;
1116
Simon Kelleyf1af2bb2013-09-24 09:16:28 +01001117 for (check_ia(state, opt, &ia_end, &ia_option);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001118 ia_option;
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001119 ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
Simon Kelley4cb1b322012-02-06 14:30:41 +00001120 {
1121 struct dhcp_lease *lease;
Simon Kelley97f876b2018-08-21 22:06:36 +01001122 struct in6_addr addr;
1123
1124 /* align */
1125 memcpy(&addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
Simon Kelleyf1af2bb2013-09-24 09:16:28 +01001126 if ((lease = lease6_find(state->clid, state->clid_len, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
Simon Kelley97f876b2018-08-21 22:06:36 +01001127 state->iaid, &addr)))
Simon Kelley4cb1b322012-02-06 14:30:41 +00001128 lease_prune(lease, now);
1129 else
1130 {
1131 if (!made_ia)
1132 {
Simon Kelleyf1af2bb2013-09-24 09:16:28 +01001133 o = new_opt6(state->ia_type);
1134 put_opt6_long(state->iaid);
1135 if (state->ia_type == OPTION6_IA_NA)
Simon Kelley4cb1b322012-02-06 14:30:41 +00001136 {
1137 put_opt6_long(0);
1138 put_opt6_long(0);
1139 }
1140 made_ia = 1;
1141 }
1142
1143 o1 = new_opt6(OPTION6_IAADDR);
Simon Kelleyc5db8f92018-08-23 23:06:00 +01001144 put_opt6(&addr, IN6ADDRSZ);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001145 put_opt6_long(0);
1146 put_opt6_long(0);
1147 end_opt6(o1);
1148 }
1149 }
1150
1151 if (made_ia)
1152 {
1153 o1 = new_opt6(OPTION6_STATUS_CODE);
1154 put_opt6_short(DHCP6NOBINDING);
Simon Kelleyde92b472013-03-15 18:25:10 +00001155 put_opt6_string(_("no binding found"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001156 end_opt6(o1);
1157
1158 end_opt6(o);
1159 }
1160 }
1161
1162 o1 = new_opt6(OPTION6_STATUS_CODE);
1163 put_opt6_short(DHCP6SUCCESS);
Simon Kelleyde92b472013-03-15 18:25:10 +00001164 put_opt6_string(_("release received"));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001165 end_opt6(o1);
1166
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001167 break;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001168 }
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001169
1170 case DHCP6DECLINE:
1171 {
1172 /* set reply message type */
1173 *outmsgtypep = DHCP6REPLY;
1174
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001175 log6_quiet(state, "DHCPDECLINE", NULL, NULL);
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001176
Simon Kelleyf1af2bb2013-09-24 09:16:28 +01001177 for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001178 {
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001179 void *ia_option, *ia_end;
1180 int made_ia = 0;
1181
Simon Kelleyf1af2bb2013-09-24 09:16:28 +01001182 for (check_ia(state, opt, &ia_end, &ia_option);
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001183 ia_option;
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001184 ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001185 {
1186 struct dhcp_lease *lease;
Simon Kelley97f876b2018-08-21 22:06:36 +01001187 struct in6_addr addr;
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001188
Simon Kelley97f876b2018-08-21 22:06:36 +01001189 /* align */
1190 memcpy(&addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
1191
1192 if (have_config(config, CONFIG_ADDR6) && IN6_ARE_ADDR_EQUAL(&config->addr6, &addr))
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001193 {
Simon Kelleyceae00d2012-02-09 21:28:14 +00001194 prettyprint_time(daemon->dhcp_buff3, DECLINE_BACKOFF);
Simon Kelley97f876b2018-08-21 22:06:36 +01001195 inet_ntop(AF_INET6, &addr, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001196 my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"),
Simon Kelleyceae00d2012-02-09 21:28:14 +00001197 daemon->addrbuff, daemon->dhcp_buff3);
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001198 config->flags |= CONFIG_DECLINED;
1199 config->decline_time = now;
1200 }
1201 else
1202 /* make sure this host gets a different address next time. */
Simon Kelleyf1af2bb2013-09-24 09:16:28 +01001203 for (context_tmp = state->context; context_tmp; context_tmp = context_tmp->current)
1204 context_tmp->addr_epoch++;
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001205
Simon Kelleyf1af2bb2013-09-24 09:16:28 +01001206 if ((lease = lease6_find(state->clid, state->clid_len, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
Simon Kelley97f876b2018-08-21 22:06:36 +01001207 state->iaid, &addr)))
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001208 lease_prune(lease, now);
1209 else
1210 {
1211 if (!made_ia)
1212 {
Simon Kelleyf1af2bb2013-09-24 09:16:28 +01001213 o = new_opt6(state->ia_type);
1214 put_opt6_long(state->iaid);
1215 if (state->ia_type == OPTION6_IA_NA)
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001216 {
1217 put_opt6_long(0);
1218 put_opt6_long(0);
1219 }
1220 made_ia = 1;
1221 }
1222
1223 o1 = new_opt6(OPTION6_IAADDR);
Simon Kelley97f876b2018-08-21 22:06:36 +01001224 put_opt6(&addr, IN6ADDRSZ);
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001225 put_opt6_long(0);
1226 put_opt6_long(0);
1227 end_opt6(o1);
1228 }
1229 }
1230
1231 if (made_ia)
1232 {
1233 o1 = new_opt6(OPTION6_STATUS_CODE);
1234 put_opt6_short(DHCP6NOBINDING);
Simon Kelleyde92b472013-03-15 18:25:10 +00001235 put_opt6_string(_("no binding found"));
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001236 end_opt6(o1);
1237
1238 end_opt6(o);
1239 }
1240
1241 }
Ilya Ponetaev51943362014-09-13 21:19:01 +01001242
Josh Soref730c6742017-02-06 16:14:04 +00001243 /* We must answer with 'success' in global section anyway */
Ilya Ponetaev51943362014-09-13 21:19:01 +01001244 o1 = new_opt6(OPTION6_STATUS_CODE);
1245 put_opt6_short(DHCP6SUCCESS);
1246 put_opt6_string(_("success"));
1247 end_opt6(o1);
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001248 break;
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001249 }
1250
Simon Kelley4cb1b322012-02-06 14:30:41 +00001251 }
1252
Simon Kelleyf1af2bb2013-09-24 09:16:28 +01001253 log_tags(tagif, state->xid);
1254 log6_opts(0, state->xid, daemon->outpacket.iov_base + start_opts, daemon->outpacket.iov_base + save_counter(-1));
Simon Kelley4cb1b322012-02-06 14:30:41 +00001255
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001256 return 1;
1257
1258}
1259
Simon Kelleyf1af2bb2013-09-24 09:16:28 +01001260static struct dhcp_netid *add_options(struct state *state, int do_refresh)
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001261{
Simon Kelley2763d4b2013-03-06 21:24:56 +00001262 void *oro;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001263 /* filter options based on tags, those we want get DHOPT_TAGOK bit set */
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001264 struct dhcp_netid *tagif = option_filter(state->tags, state->context_tags, daemon->dhcp_opts6);
1265 struct dhcp_opt *opt_cfg;
Simon Kelley871d4562013-07-27 21:32:32 +01001266 int done_dns = 0, done_refresh = !do_refresh, do_encap = 0;
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001267 int i, o, o1;
1268
1269 oro = opt6_find(state->packet_options, state->end, OPTION6_ORO, 0);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001270
1271 for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next)
1272 {
1273 /* netids match and not encapsulated? */
1274 if (!(opt_cfg->flags & DHOPT_TAGOK))
1275 continue;
1276
1277 if (!(opt_cfg->flags & DHOPT_FORCE) && oro)
1278 {
1279 for (i = 0; i < opt6_len(oro) - 1; i += 2)
1280 if (opt6_uint(oro, i, 2) == (unsigned)opt_cfg->opt)
1281 break;
1282
1283 /* option not requested */
1284 if (i >= opt6_len(oro) - 1)
1285 continue;
1286 }
1287
Simon Kelley871d4562013-07-27 21:32:32 +01001288 if (opt_cfg->opt == OPTION6_REFRESH_TIME)
1289 done_refresh = 1;
Simon Kelley5e3e4642015-08-25 23:08:39 +01001290
1291 if (opt_cfg->opt == OPTION6_DNS_SERVER)
1292 done_dns = 1;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001293
Simon Kelley27b78d92015-09-26 21:40:45 +01001294 if (opt_cfg->flags & DHOPT_ADDR6)
Simon Kelley4b86b652012-02-29 11:45:37 +00001295 {
Simon Kelleyc3a04082014-01-11 22:18:19 +00001296 int len, j;
1297 struct in6_addr *a;
1298
Simon Kelleyc3a04082014-01-11 22:18:19 +00001299 for (a = (struct in6_addr *)opt_cfg->val, len = opt_cfg->len, j = 0;
1300 j < opt_cfg->len; j += IN6ADDRSZ, a++)
1301 if ((IN6_IS_ADDR_ULA_ZERO(a) && IN6_IS_ADDR_UNSPECIFIED(state->ula_addr)) ||
1302 (IN6_IS_ADDR_LINK_LOCAL_ZERO(a) && IN6_IS_ADDR_UNSPECIFIED(state->ll_addr)))
1303 len -= IN6ADDRSZ;
1304
1305 if (len != 0)
1306 {
1307
1308 o = new_opt6(opt_cfg->opt);
1309
1310 for (a = (struct in6_addr *)opt_cfg->val, j = 0; j < opt_cfg->len; j+=IN6ADDRSZ, a++)
1311 {
1312 if (IN6_IS_ADDR_UNSPECIFIED(a))
1313 {
1314 if (!add_local_addrs(state->context))
1315 put_opt6(state->fallback, IN6ADDRSZ);
1316 }
1317 else if (IN6_IS_ADDR_ULA_ZERO(a))
1318 {
1319 if (!IN6_IS_ADDR_UNSPECIFIED(state->ula_addr))
1320 put_opt6(state->ula_addr, IN6ADDRSZ);
1321 }
1322 else if (IN6_IS_ADDR_LINK_LOCAL_ZERO(a))
1323 {
1324 if (!IN6_IS_ADDR_UNSPECIFIED(state->ll_addr))
1325 put_opt6(state->ll_addr, IN6ADDRSZ);
1326 }
1327 else
1328 put_opt6(a, IN6ADDRSZ);
Simon Kelley4b86b652012-02-29 11:45:37 +00001329 }
Simon Kelleyc3a04082014-01-11 22:18:19 +00001330
1331 end_opt6(o);
1332 }
Simon Kelley4b86b652012-02-29 11:45:37 +00001333 }
Simon Kelleyc3a04082014-01-11 22:18:19 +00001334 else
1335 {
1336 o = new_opt6(opt_cfg->opt);
1337 if (opt_cfg->val)
1338 put_opt6(opt_cfg->val, opt_cfg->len);
1339 end_opt6(o);
1340 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001341 }
1342
Simon Kelley871d4562013-07-27 21:32:32 +01001343 if (daemon->port == NAMESERVER_PORT && !done_dns)
Simon Kelley4cb1b322012-02-06 14:30:41 +00001344 {
1345 o = new_opt6(OPTION6_DNS_SERVER);
Simon Kelleyf1af2bb2013-09-24 09:16:28 +01001346 if (!add_local_addrs(state->context))
1347 put_opt6(state->fallback, IN6ADDRSZ);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001348 end_opt6(o);
1349 }
Simon Kelley871d4562013-07-27 21:32:32 +01001350
Simon Kelleyf1af2bb2013-09-24 09:16:28 +01001351 if (state->context && !done_refresh)
Simon Kelley871d4562013-07-27 21:32:32 +01001352 {
1353 struct dhcp_context *c;
1354 unsigned int lease_time = 0xffffffff;
1355
1356 /* Find the smallest lease tie of all contexts,
Josh Soref730c6742017-02-06 16:14:04 +00001357 subject to the RFC-4242 stipulation that this must not
Simon Kelley871d4562013-07-27 21:32:32 +01001358 be less than 600. */
Simon Kelleyf1af2bb2013-09-24 09:16:28 +01001359 for (c = state->context; c; c = c->next)
Simon Kelley871d4562013-07-27 21:32:32 +01001360 if (c->lease_time < lease_time)
1361 {
1362 if (c->lease_time < 600)
1363 lease_time = 600;
1364 else
1365 lease_time = c->lease_time;
1366 }
1367
1368 o = new_opt6(OPTION6_REFRESH_TIME);
1369 put_opt6_long(lease_time);
1370 end_opt6(o);
1371 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001372
1373 /* handle vendor-identifying vendor-encapsulated options,
1374 dhcp-option = vi-encap:13,17,....... */
1375 for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next)
1376 opt_cfg->flags &= ~DHOPT_ENCAP_DONE;
Simon Kelley4b86b652012-02-29 11:45:37 +00001377
Simon Kelley4cb1b322012-02-06 14:30:41 +00001378 if (oro)
1379 for (i = 0; i < opt6_len(oro) - 1; i += 2)
1380 if (opt6_uint(oro, i, 2) == OPTION6_VENDOR_OPTS)
1381 do_encap = 1;
1382
1383 for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next)
1384 {
1385 if (opt_cfg->flags & DHOPT_RFC3925)
1386 {
1387 int found = 0;
1388 struct dhcp_opt *oc;
1389
1390 if (opt_cfg->flags & DHOPT_ENCAP_DONE)
1391 continue;
1392
1393 for (oc = daemon->dhcp_opts6; oc; oc = oc->next)
1394 {
1395 oc->flags &= ~DHOPT_ENCAP_MATCH;
1396
1397 if (!(oc->flags & DHOPT_RFC3925) || opt_cfg->u.encap != oc->u.encap)
1398 continue;
1399
1400 oc->flags |= DHOPT_ENCAP_DONE;
1401 if (match_netid(oc->netid, tagif, 1))
1402 {
1403 /* option requested/forced? */
1404 if (!oro || do_encap || (oc->flags & DHOPT_FORCE))
1405 {
1406 oc->flags |= DHOPT_ENCAP_MATCH;
1407 found = 1;
1408 }
1409 }
1410 }
1411
1412 if (found)
1413 {
1414 o = new_opt6(OPTION6_VENDOR_OPTS);
1415 put_opt6_long(opt_cfg->u.encap);
1416
1417 for (oc = daemon->dhcp_opts6; oc; oc = oc->next)
1418 if (oc->flags & DHOPT_ENCAP_MATCH)
1419 {
1420 o1 = new_opt6(oc->opt);
1421 put_opt6(oc->val, oc->len);
1422 end_opt6(o1);
1423 }
1424 end_opt6(o);
1425 }
1426 }
1427 }
Simon Kelley07933802012-02-14 20:55:25 +00001428
1429
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001430 if (state->hostname)
Simon Kelley4cb1b322012-02-06 14:30:41 +00001431 {
1432 unsigned char *p;
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001433 size_t len = strlen(state->hostname);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001434
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001435 if (state->send_domain)
Roy Marples3f3adae2013-07-25 16:22:46 +01001436 len += strlen(state->send_domain) + 2;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001437
1438 o = new_opt6(OPTION6_FQDN);
Roy Marples3f3adae2013-07-25 16:22:46 +01001439 if ((p = expand(len + 2)))
Simon Kelleyc5ad4e72012-02-24 16:06:20 +00001440 {
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001441 *(p++) = state->fqdn_flags;
Simon Kelley0549c732017-09-25 18:17:11 +01001442 p = do_rfc1035_name(p, state->hostname, NULL);
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001443 if (state->send_domain)
Roy Marples3f3adae2013-07-25 16:22:46 +01001444 {
Simon Kelley0549c732017-09-25 18:17:11 +01001445 p = do_rfc1035_name(p, state->send_domain, NULL);
Roy Marples3f3adae2013-07-25 16:22:46 +01001446 *p = 0;
1447 }
Simon Kelleyc5ad4e72012-02-24 16:06:20 +00001448 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001449 end_opt6(o);
1450 }
1451
1452
1453 /* logging */
1454 if (option_bool(OPT_LOG_OPTS) && oro)
1455 {
1456 char *q = daemon->namebuff;
1457 for (i = 0; i < opt6_len(oro) - 1; i += 2)
1458 {
1459 char *s = option_string(AF_INET6, opt6_uint(oro, i, 2), NULL, 0, NULL, 0);
1460 q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
1461 "%d%s%s%s",
1462 opt6_uint(oro, i, 2),
1463 strlen(s) != 0 ? ":" : "",
1464 s,
1465 (i > opt6_len(oro) - 3) ? "" : ", ");
1466 if ( i > opt6_len(oro) - 3 || (q - daemon->namebuff) > 40)
1467 {
1468 q = daemon->namebuff;
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001469 my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), state->xid, daemon->namebuff);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001470 }
1471 }
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001472 }
Simon Kelley4cb1b322012-02-06 14:30:41 +00001473
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001474 return tagif;
1475}
1476
1477static int add_local_addrs(struct dhcp_context *context)
1478{
1479 int done = 0;
Simon Kelley6c8f21e2012-03-12 15:06:55 +00001480
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001481 for (; context; context = context->current)
1482 if ((context->flags & CONTEXT_USED) && !IN6_IS_ADDR_UNSPECIFIED(&context->local6))
1483 {
1484 /* squash duplicates */
1485 struct dhcp_context *c;
1486 for (c = context->current; c; c = c->current)
1487 if ((c->flags & CONTEXT_USED) &&
1488 IN6_ARE_ADDR_EQUAL(&context->local6, &c->local6))
1489 break;
1490
1491 if (!c)
1492 {
1493 done = 1;
1494 put_opt6(&context->local6, IN6ADDRSZ);
1495 }
1496 }
1497
1498 return done;
Simon Kelley6c8f21e2012-03-12 15:06:55 +00001499}
1500
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001501
1502static void get_context_tag(struct state *state, struct dhcp_context *context)
1503{
1504 /* get tags from context if we've not used it before */
1505 if (context->netid.next == &context->netid && context->netid.net)
1506 {
1507 context->netid.next = state->context_tags;
1508 state->context_tags = &context->netid;
1509 if (!state->hostname_auth)
1510 {
1511 struct dhcp_netid_list *id_list;
1512
1513 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
1514 if ((!id_list->list) || match_netid(id_list->list, &context->netid, 0))
1515 break;
1516 if (id_list)
1517 state->hostname = NULL;
1518 }
1519 }
1520}
Simon Kelleyc6309242013-03-07 20:59:28 +00001521
Simon Kelley3a654c52013-03-06 22:17:48 +00001522static int check_ia(struct state *state, void *opt, void **endp, void **ia_option)
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001523{
1524 state->ia_type = opt6_type(opt);
Simon Kelley3a654c52013-03-06 22:17:48 +00001525 *ia_option = NULL;
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001526
1527 if (state->ia_type != OPTION6_IA_NA && state->ia_type != OPTION6_IA_TA)
Simon Kelley3a654c52013-03-06 22:17:48 +00001528 return 0;
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001529
1530 if (state->ia_type == OPTION6_IA_NA && opt6_len(opt) < 12)
Simon Kelley3a654c52013-03-06 22:17:48 +00001531 return 0;
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001532
1533 if (state->ia_type == OPTION6_IA_TA && opt6_len(opt) < 4)
Simon Kelley3a654c52013-03-06 22:17:48 +00001534 return 0;
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001535
1536 *endp = opt6_ptr(opt, opt6_len(opt));
1537 state->iaid = opt6_uint(opt, 0, 4);
Simon Kelley3a654c52013-03-06 22:17:48 +00001538 *ia_option = opt6_find(opt6_ptr(opt, state->ia_type == OPTION6_IA_NA ? 12 : 4), *endp, OPTION6_IAADDR, 24);
1539
1540 return 1;
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001541}
1542
1543
1544static int build_ia(struct state *state, int *t1cntr)
1545{
1546 int o = new_opt6(state->ia_type);
1547
1548 put_opt6_long(state->iaid);
1549 *t1cntr = 0;
1550
1551 if (state->ia_type == OPTION6_IA_NA)
1552 {
1553 /* save pointer */
1554 *t1cntr = save_counter(-1);
1555 /* so we can fill these in later */
1556 put_opt6_long(0);
1557 put_opt6_long(0);
1558 }
1559
1560 return o;
1561}
1562
1563static void end_ia(int t1cntr, unsigned int min_time, int do_fuzz)
1564{
1565 if (t1cntr != 0)
1566 {
Ville Skyttäfaaf3062018-01-14 17:32:52 +00001567 /* go back and fill in fields in IA_NA option */
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001568 int sav = save_counter(t1cntr);
1569 unsigned int t1, t2, fuzz = 0;
1570
1571 if (do_fuzz)
1572 {
1573 fuzz = rand16();
1574
1575 while (fuzz > (min_time/16))
1576 fuzz = fuzz/2;
1577 }
1578
1579 t1 = (min_time == 0xffffffff) ? 0xffffffff : min_time/2 - fuzz;
1580 t2 = (min_time == 0xffffffff) ? 0xffffffff : ((min_time/8)*7) - fuzz;
1581 put_opt6_long(t1);
1582 put_opt6_long(t2);
1583 save_counter(sav);
1584 }
1585}
1586
Simon Kelleycc4baaa2013-08-05 15:03:44 +01001587static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, void *ia_option,
Simon Kelleyc8f2dd82013-09-13 11:22:55 +01001588 unsigned int *min_time, struct in6_addr *addr, time_t now)
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001589{
Simon Kelleycc4baaa2013-08-05 15:03:44 +01001590 unsigned int valid_time = 0, preferred_time = 0;
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001591 int o = new_opt6(OPTION6_IAADDR);
1592 struct dhcp_lease *lease;
1593
Simon Kelleycc4baaa2013-08-05 15:03:44 +01001594 /* get client requested times */
1595 if (ia_option)
1596 {
1597 preferred_time = opt6_uint(ia_option, 16, 4);
1598 valid_time = opt6_uint(ia_option, 20, 4);
1599 }
1600
1601 calculate_times(context, min_time, &valid_time, &preferred_time, lease_time);
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001602
Simon Kelleyde92b472013-03-15 18:25:10 +00001603 put_opt6(addr, sizeof(*addr));
1604 put_opt6_long(preferred_time);
1605 put_opt6_long(valid_time);
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001606 end_opt6(o);
1607
Simon Kelleyc8f2dd82013-09-13 11:22:55 +01001608 if (state->lease_allocate)
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001609 update_leases(state, context, addr, valid_time, now);
1610
1611 if ((lease = lease6_find_by_addr(addr, 128, 0)))
1612 lease->flags |= LEASE_USED;
1613
1614 /* get tags from context if we've not used it before */
1615 if (context->netid.next == &context->netid && context->netid.net)
1616 {
1617 context->netid.next = state->context_tags;
1618 state->context_tags = &context->netid;
1619
1620 if (!state->hostname_auth)
1621 {
1622 struct dhcp_netid_list *id_list;
1623
1624 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
1625 if ((!id_list->list) || match_netid(id_list->list, &context->netid, 0))
1626 break;
1627 if (id_list)
1628 state->hostname = NULL;
1629 }
1630 }
1631
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001632 log6_quiet(state, state->lease_allocate ? "DHCPREPLY" : "DHCPADVERTISE", addr, state->hostname);
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001633
1634}
Simon Kelleyff59fc82013-03-07 11:00:26 +00001635
Simon Kelleyf1af2bb2013-09-24 09:16:28 +01001636static void mark_context_used(struct state *state, struct in6_addr *addr)
Simon Kelleyff59fc82013-03-07 11:00:26 +00001637{
Simon Kelleyf1af2bb2013-09-24 09:16:28 +01001638 struct dhcp_context *context;
1639
Simon Kelleyff59fc82013-03-07 11:00:26 +00001640 /* Mark that we have an address for this prefix. */
Simon Kelleyf1af2bb2013-09-24 09:16:28 +01001641 for (context = state->context; context; context = context->current)
Simon Kelleyff59fc82013-03-07 11:00:26 +00001642 if (is_same_net6(addr, &context->start6, context->prefix))
1643 context->flags |= CONTEXT_USED;
1644}
Simon Kelleyc6309242013-03-07 20:59:28 +00001645
Simon Kelleyde92b472013-03-15 18:25:10 +00001646static void mark_config_used(struct dhcp_context *context, struct in6_addr *addr)
1647{
1648 for (; context; context = context->current)
1649 if (is_same_net6(addr, &context->start6, context->prefix))
1650 context->flags |= CONTEXT_CONF_USED;
1651}
1652
1653/* make sure address not leased to another CLID/IAID */
1654static int check_address(struct state *state, struct in6_addr *addr)
1655{
1656 struct dhcp_lease *lease;
1657
1658 if (!(lease = lease6_find_by_addr(addr, 128, 0)))
1659 return 1;
1660
1661 if (lease->clid_len != state->clid_len ||
1662 memcmp(lease->clid, state->clid, state->clid_len) != 0 ||
Simon Kelley89500e32013-09-20 16:29:20 +01001663 lease->iaid != state->iaid)
Simon Kelleyde92b472013-03-15 18:25:10 +00001664 return 0;
1665
1666 return 1;
1667}
1668
Simon Kelleycc4baaa2013-08-05 15:03:44 +01001669
1670/* Calculate valid and preferred times to send in leases/renewals.
1671
1672 Inputs are:
1673
1674 *valid_timep, *preferred_timep - requested times from IAADDR options.
1675 context->valid, context->preferred - times associated with subnet address on local interface.
1676 context->flags | CONTEXT_DEPRECATE - "deprecated" flag in dhcp-range.
1677 lease_time - configured time for context for individual client.
1678 *min_time - smallest valid time sent so far.
1679
1680 Outputs are :
1681
1682 *valid_timep, *preferred_timep - times to be send in IAADDR option.
1683 *min_time - smallest valid time sent so far, to calculate T1 and T2.
1684
1685 */
Simon Kelleyde92b472013-03-15 18:25:10 +00001686static void calculate_times(struct dhcp_context *context, unsigned int *min_time, unsigned int *valid_timep,
Simon Kelleycc4baaa2013-08-05 15:03:44 +01001687 unsigned int *preferred_timep, unsigned int lease_time)
Simon Kelleyde92b472013-03-15 18:25:10 +00001688{
Simon Kelleycc4baaa2013-08-05 15:03:44 +01001689 unsigned int req_preferred = *preferred_timep, req_valid = *valid_timep;
1690 unsigned int valid_time = lease_time, preferred_time = lease_time;
1691
1692 /* RFC 3315: "A server ignores the lifetimes set
1693 by the client if the preferred lifetime is greater than the valid
1694 lifetime. */
1695 if (req_preferred <= req_valid)
1696 {
1697 if (req_preferred != 0)
1698 {
1699 /* 0 == "no preference from client" */
1700 if (req_preferred < 120u)
1701 req_preferred = 120u; /* sanity */
1702
1703 if (req_preferred < preferred_time)
1704 preferred_time = req_preferred;
1705 }
1706
1707 if (req_valid != 0)
1708 /* 0 == "no preference from client" */
1709 {
1710 if (req_valid < 120u)
1711 req_valid = 120u; /* sanity */
1712
1713 if (req_valid < valid_time)
1714 valid_time = req_valid;
1715 }
1716 }
Simon Kelleyde92b472013-03-15 18:25:10 +00001717
Simon Kelleycc4baaa2013-08-05 15:03:44 +01001718 /* deprecate (preferred == 0) which configured, or when local address
1719 is deprecated */
1720 if ((context->flags & CONTEXT_DEPRECATE) || context->preferred == 0)
Simon Kelleyde92b472013-03-15 18:25:10 +00001721 preferred_time = 0;
Simon Kelleycc4baaa2013-08-05 15:03:44 +01001722
Simon Kelleyde92b472013-03-15 18:25:10 +00001723 if (preferred_time != 0 && preferred_time < *min_time)
1724 *min_time = preferred_time;
1725
1726 if (valid_time != 0 && valid_time < *min_time)
1727 *min_time = valid_time;
1728
1729 *valid_timep = valid_time;
1730 *preferred_timep = preferred_time;
1731}
1732
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001733static void update_leases(struct state *state, struct dhcp_context *context, struct in6_addr *addr, unsigned int lease_time, time_t now)
1734{
1735 struct dhcp_lease *lease = lease6_find_by_addr(addr, 128, 0);
Vladislav Grishenko408c3682013-09-24 16:18:49 +01001736#ifdef HAVE_SCRIPT
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001737 struct dhcp_netid *tagif = run_tag_if(state->tags);
Vladislav Grishenko408c3682013-09-24 16:18:49 +01001738#endif
1739
1740 (void)context;
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001741
1742 if (!lease)
1743 lease = lease6_allocate(addr, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA);
1744
1745 if (lease)
1746 {
1747 lease_set_expires(lease, lease_time, now);
Simon Kelley89500e32013-09-20 16:29:20 +01001748 lease_set_iaid(lease, state->iaid);
1749 lease_set_hwaddr(lease, state->mac, state->clid, state->mac_len, state->mac_type, state->clid_len, now, 0);
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001750 lease_set_interface(lease, state->interface, now);
1751 if (state->hostname && state->ia_type == OPTION6_IA_NA)
1752 {
1753 char *addr_domain = get_domain6(addr);
1754 if (!state->send_domain)
1755 state->send_domain = addr_domain;
1756 lease_set_hostname(lease, state->hostname, state->hostname_auth, addr_domain, state->domain);
1757 }
1758
1759#ifdef HAVE_SCRIPT
1760 if (daemon->lease_change_command)
1761 {
1762 void *class_opt;
1763 lease->flags |= LEASE_CHANGED;
1764 free(lease->extradata);
1765 lease->extradata = NULL;
1766 lease->extradata_size = lease->extradata_len = 0;
Simon Kelley6f9aaa92013-04-10 10:25:26 +01001767 lease->vendorclass_count = 0;
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001768
1769 if ((class_opt = opt6_find(state->packet_options, state->end, OPTION6_VENDOR_CLASS, 4)))
1770 {
1771 void *enc_opt, *enc_end = opt6_ptr(class_opt, opt6_len(class_opt));
Simon Kelley6f9aaa92013-04-10 10:25:26 +01001772 lease->vendorclass_count++;
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001773 /* send enterprise number first */
1774 sprintf(daemon->dhcp_buff2, "%u", opt6_uint(class_opt, 0, 4));
1775 lease_add_extradata(lease, (unsigned char *)daemon->dhcp_buff2, strlen(daemon->dhcp_buff2), 0);
1776
1777 if (opt6_len(class_opt) >= 6)
1778 for (enc_opt = opt6_ptr(class_opt, 4); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))
1779 {
Simon Kelley6f9aaa92013-04-10 10:25:26 +01001780 lease->vendorclass_count++;
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001781 lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0);
1782 }
1783 }
1784
1785 lease_add_extradata(lease, (unsigned char *)state->client_hostname,
1786 state->client_hostname ? strlen(state->client_hostname) : 0, 0);
1787
1788 /* space-concat tag set */
1789 if (!tagif && !context->netid.net)
1790 lease_add_extradata(lease, NULL, 0, 0);
1791 else
1792 {
1793 if (context->netid.net)
1794 lease_add_extradata(lease, (unsigned char *)context->netid.net, strlen(context->netid.net), tagif ? ' ' : 0);
1795
1796 if (tagif)
1797 {
1798 struct dhcp_netid *n;
1799 for (n = tagif; n; n = n->next)
1800 {
1801 struct dhcp_netid *n1;
1802 /* kill dupes */
1803 for (n1 = n->next; n1; n1 = n1->next)
1804 if (strcmp(n->net, n1->net) == 0)
1805 break;
1806 if (!n1)
1807 lease_add_extradata(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0);
1808 }
1809 }
1810 }
1811
1812 if (state->link_address)
1813 inet_ntop(AF_INET6, state->link_address, daemon->addrbuff, ADDRSTRLEN);
1814
1815 lease_add_extradata(lease, (unsigned char *)daemon->addrbuff, state->link_address ? strlen(daemon->addrbuff) : 0, 0);
1816
1817 if ((class_opt = opt6_find(state->packet_options, state->end, OPTION6_USER_CLASS, 2)))
1818 {
1819 void *enc_opt, *enc_end = opt6_ptr(class_opt, opt6_len(class_opt));
1820 for (enc_opt = opt6_ptr(class_opt, 0); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))
1821 lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0);
1822 }
1823 }
1824#endif
1825
1826 }
1827}
1828
1829
1830
Simon Kelley6c8f21e2012-03-12 15:06:55 +00001831static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_opts)
1832{
1833 void *opt;
1834 char *desc = nest ? "nest" : "sent";
Simon Kelley5cfea3d2012-03-12 17:28:27 +00001835
Simon Kelleyd81b42d2013-09-23 12:26:34 +01001836 if (!option_bool(OPT_LOG_OPTS) || start_opts == end_opts)
Simon Kelley5cfea3d2012-03-12 17:28:27 +00001837 return;
1838
1839 for (opt = start_opts; opt; opt = opt6_next(opt, end_opts))
1840 {
1841 int type = opt6_type(opt);
1842 void *ia_options = NULL;
1843 char *optname;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001844
Simon Kelley5cfea3d2012-03-12 17:28:27 +00001845 if (type == OPTION6_IA_NA)
1846 {
1847 sprintf(daemon->namebuff, "IAID=%u T1=%u T2=%u",
1848 opt6_uint(opt, 0, 4), opt6_uint(opt, 4, 4), opt6_uint(opt, 8, 4));
1849 optname = "ia-na";
1850 ia_options = opt6_ptr(opt, 12);
1851 }
1852 else if (type == OPTION6_IA_TA)
1853 {
1854 sprintf(daemon->namebuff, "IAID=%u", opt6_uint(opt, 0, 4));
1855 optname = "ia-ta";
1856 ia_options = opt6_ptr(opt, 4);
1857 }
1858 else if (type == OPTION6_IAADDR)
1859 {
Simon Kelley97f876b2018-08-21 22:06:36 +01001860 struct in6_addr addr;
1861
1862 /* align */
1863 memcpy(&addr, opt6_ptr(opt, 0), IN6ADDRSZ);
1864 inet_ntop(AF_INET6, &addr, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley5cfea3d2012-03-12 17:28:27 +00001865 sprintf(daemon->namebuff, "%s PL=%u VL=%u",
1866 daemon->addrbuff, opt6_uint(opt, 16, 4), opt6_uint(opt, 20, 4));
1867 optname = "iaaddr";
1868 ia_options = opt6_ptr(opt, 24);
1869 }
1870 else if (type == OPTION6_STATUS_CODE)
1871 {
1872 int len = sprintf(daemon->namebuff, "%u ", opt6_uint(opt, 0, 2));
1873 memcpy(daemon->namebuff + len, opt6_ptr(opt, 2), opt6_len(opt)-2);
1874 daemon->namebuff[len + opt6_len(opt) - 2] = 0;
1875 optname = "status";
1876 }
1877 else
1878 {
1879 /* account for flag byte on FQDN */
1880 int offset = type == OPTION6_FQDN ? 1 : 0;
1881 optname = option_string(AF_INET6, type, opt6_ptr(opt, offset), opt6_len(opt) - offset, daemon->namebuff, MAXDNAME);
1882 }
1883
1884 my_syslog(MS_DHCP | LOG_INFO, "%u %s size:%3d option:%3d %s %s",
1885 xid, desc, opt6_len(opt), type, optname, daemon->namebuff);
1886
1887 if (ia_options)
1888 log6_opts(1, xid, ia_options, opt6_ptr(opt, opt6_len(opt)));
1889 }
Simon Kelley6c8f21e2012-03-12 15:06:55 +00001890}
1891
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001892static void log6_quiet(struct state *state, char *type, struct in6_addr *addr, char *string)
1893{
1894 if (option_bool(OPT_LOG_OPTS) || !option_bool(OPT_QUIET_DHCP6))
1895 log6_packet(state, type, addr, string);
1896}
1897
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001898static void log6_packet(struct state *state, char *type, struct in6_addr *addr, char *string)
Simon Kelley4cb1b322012-02-06 14:30:41 +00001899{
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001900 int clid_len = state->clid_len;
1901
Simon Kelley4cb1b322012-02-06 14:30:41 +00001902 /* avoid buffer overflow */
1903 if (clid_len > 100)
1904 clid_len = 100;
1905
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001906 print_mac(daemon->namebuff, state->clid, clid_len);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001907
1908 if (addr)
1909 {
Simon Kelleybf4e62c2016-07-22 21:37:59 +01001910 inet_ntop(AF_INET6, addr, daemon->dhcp_buff2, DHCP_BUFF_SZ - 1);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001911 strcat(daemon->dhcp_buff2, " ");
1912 }
1913 else
1914 daemon->dhcp_buff2[0] = 0;
1915
1916 if(option_bool(OPT_LOG_OPTS))
Simon Kelley58dc02e2012-02-27 11:49:37 +00001917 my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s %s",
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001918 state->xid,
Simon Kelley4cb1b322012-02-06 14:30:41 +00001919 type,
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001920 state->iface_name,
Simon Kelley4cb1b322012-02-06 14:30:41 +00001921 daemon->dhcp_buff2,
Simon Kelley58dc02e2012-02-27 11:49:37 +00001922 daemon->namebuff,
Simon Kelley4cb1b322012-02-06 14:30:41 +00001923 string ? string : "");
1924 else
Simon Kelley58dc02e2012-02-27 11:49:37 +00001925 my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s %s",
Simon Kelley4cb1b322012-02-06 14:30:41 +00001926 type,
Simon Kelleya6ebfac2013-03-06 20:52:35 +00001927 state->iface_name,
Simon Kelley4cb1b322012-02-06 14:30:41 +00001928 daemon->dhcp_buff2,
Simon Kelley58dc02e2012-02-27 11:49:37 +00001929 daemon->namebuff,
Simon Kelley4cb1b322012-02-06 14:30:41 +00001930 string ? string : "");
1931}
1932
1933static void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsize)
Simon Kelleyc72daea2012-01-05 21:33:27 +00001934{
1935 u16 opt, opt_len;
1936 void *start;
1937
1938 if (!opts)
1939 return NULL;
1940
1941 while (1)
1942 {
1943 if (end - opts < 4)
1944 return NULL;
1945
1946 start = opts;
1947 GETSHORT(opt, opts);
1948 GETSHORT(opt_len, opts);
1949
1950 if (opt_len > (end - opts))
1951 return NULL;
1952
1953 if (opt == search && (opt_len >= minsize))
1954 return start;
1955
1956 opts += opt_len;
1957 }
1958}
1959
Simon Kelley4cb1b322012-02-06 14:30:41 +00001960static void *opt6_next(void *opts, void *end)
Simon Kelleyc72daea2012-01-05 21:33:27 +00001961{
1962 u16 opt_len;
1963
1964 if (end - opts < 4)
1965 return NULL;
1966
1967 opts += 2;
1968 GETSHORT(opt_len, opts);
1969
1970 if (opt_len >= (end - opts))
1971 return NULL;
1972
1973 return opts + opt_len;
1974}
Simon Kelleyc72daea2012-01-05 21:33:27 +00001975
1976static unsigned int opt6_uint(unsigned char *opt, int offset, int size)
1977{
1978 /* this worries about unaligned data and byte order */
1979 unsigned int ret = 0;
1980 int i;
1981 unsigned char *p = opt6_ptr(opt, offset);
1982
1983 for (i = 0; i < size; i++)
1984 ret = (ret << 8) | *p++;
1985
1986 return ret;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001987}
Simon Kelleyc72daea2012-01-05 21:33:27 +00001988
Simon Kelley33702ab2015-12-28 23:17:15 +00001989void relay_upstream6(struct dhcp_relay *relay, ssize_t sz,
1990 struct in6_addr *peer_address, u32 scope_id, time_t now)
Simon Kelleyff7eea22013-09-04 18:01:38 +01001991{
1992 /* ->local is same value for all relays on ->current chain */
1993
Simon Kelleycc921df2019-01-02 22:48:59 +00001994 union all_addr from;
Simon Kelleyff7eea22013-09-04 18:01:38 +01001995 unsigned char *header;
1996 unsigned char *inbuff = daemon->dhcp_packet.iov_base;
1997 int msg_type = *inbuff;
1998 int hopcount;
1999 struct in6_addr multicast;
Simon Kelley8939c952013-09-25 11:49:34 +01002000 unsigned int maclen, mactype;
2001 unsigned char mac[DHCP_CHADDR_MAX];
Simon Kelleyff7eea22013-09-04 18:01:38 +01002002
2003 inet_pton(AF_INET6, ALL_SERVERS, &multicast);
Simon Kelley33702ab2015-12-28 23:17:15 +00002004 get_client_mac(peer_address, scope_id, mac, &maclen, &mactype, now);
Simon Kelley8939c952013-09-25 11:49:34 +01002005
Simon Kelleyff7eea22013-09-04 18:01:38 +01002006 /* source address == relay address */
Simon Kelleycc921df2019-01-02 22:48:59 +00002007 from.addr6 = relay->local.addr6;
Simon Kelleyff7eea22013-09-04 18:01:38 +01002008
2009 /* Get hop count from nested relayed message */
2010 if (msg_type == DHCP6RELAYFORW)
2011 hopcount = *((unsigned char *)inbuff+1) + 1;
2012 else
2013 hopcount = 0;
2014
2015 /* RFC 3315 HOP_COUNT_LIMIT */
2016 if (hopcount > 32)
2017 return;
2018
Simon Kelleyfa785732016-07-22 20:56:01 +01002019 reset_counter();
Simon Kelleyff7eea22013-09-04 18:01:38 +01002020
2021 if ((header = put_opt6(NULL, 34)))
2022 {
2023 int o;
2024
2025 header[0] = DHCP6RELAYFORW;
2026 header[1] = hopcount;
Simon Kelleycc921df2019-01-02 22:48:59 +00002027 memcpy(&header[2], &relay->local.addr6, IN6ADDRSZ);
Simon Kelleyff7eea22013-09-04 18:01:38 +01002028 memcpy(&header[18], peer_address, IN6ADDRSZ);
Simon Kelley89500e32013-09-20 16:29:20 +01002029
2030 /* RFC-6939 */
Simon Kelley8939c952013-09-25 11:49:34 +01002031 if (maclen != 0)
Simon Kelley89500e32013-09-20 16:29:20 +01002032 {
2033 o = new_opt6(OPTION6_CLIENT_MAC);
Simon Kelley8939c952013-09-25 11:49:34 +01002034 put_opt6_short(mactype);
2035 put_opt6(mac, maclen);
Simon Kelley89500e32013-09-20 16:29:20 +01002036 end_opt6(o);
2037 }
Simon Kelleyff7eea22013-09-04 18:01:38 +01002038
2039 o = new_opt6(OPTION6_RELAY_MSG);
2040 put_opt6(inbuff, sz);
2041 end_opt6(o);
2042
2043 for (; relay; relay = relay->current)
2044 {
2045 union mysockaddr to;
2046
2047 to.sa.sa_family = AF_INET6;
Simon Kelleycc921df2019-01-02 22:48:59 +00002048 to.in6.sin6_addr = relay->server.addr6;
Simon Kelleyff7eea22013-09-04 18:01:38 +01002049 to.in6.sin6_port = htons(DHCPV6_SERVER_PORT);
2050 to.in6.sin6_flowinfo = 0;
2051 to.in6.sin6_scope_id = 0;
2052
Simon Kelleycc921df2019-01-02 22:48:59 +00002053 if (IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast))
Simon Kelleyff7eea22013-09-04 18:01:38 +01002054 {
2055 int multicast_iface;
2056 if (!relay->interface || strchr(relay->interface, '*') ||
2057 (multicast_iface = if_nametoindex(relay->interface)) == 0 ||
2058 setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_iface, sizeof(multicast_iface)) == -1)
2059 my_syslog(MS_DHCP | LOG_ERR, _("Cannot multicast to DHCPv6 server without correct interface"));
2060 }
2061
Simon Kelley6b1c4642016-07-22 20:59:16 +01002062 send_from(daemon->dhcp6fd, 0, daemon->outpacket.iov_base, save_counter(-1), &to, &from, 0);
Simon Kelleyff7eea22013-09-04 18:01:38 +01002063
2064 if (option_bool(OPT_LOG_OPTS))
2065 {
2066 inet_ntop(AF_INET6, &relay->local, daemon->addrbuff, ADDRSTRLEN);
2067 inet_ntop(AF_INET6, &relay->server, daemon->namebuff, ADDRSTRLEN);
2068 my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, daemon->namebuff);
2069 }
2070
2071 /* Save this for replies */
2072 relay->iface_index = scope_id;
2073 }
2074 }
2075}
2076
2077unsigned short relay_reply6(struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface)
2078{
2079 struct dhcp_relay *relay;
2080 struct in6_addr link;
2081 unsigned char *inbuff = daemon->dhcp_packet.iov_base;
2082
2083 /* must have at least msg_type+hopcount+link_address+peer_address+minimal size option
2084 which is 1 + 1 + 16 + 16 + 2 + 2 = 38 */
2085
2086 if (sz < 38 || *inbuff != DHCP6RELAYREPL)
2087 return 0;
2088
2089 memcpy(&link, &inbuff[2], IN6ADDRSZ);
2090
2091 for (relay = daemon->relay6; relay; relay = relay->next)
Simon Kelleycc921df2019-01-02 22:48:59 +00002092 if (IN6_ARE_ADDR_EQUAL(&link, &relay->local.addr6) &&
Simon Kelleyff7eea22013-09-04 18:01:38 +01002093 (!relay->interface || wildcard_match(relay->interface, arrival_interface)))
2094 break;
2095
Simon Kelleyfa785732016-07-22 20:56:01 +01002096 reset_counter();
Simon Kelleyff7eea22013-09-04 18:01:38 +01002097
2098 if (relay)
2099 {
2100 void *opt, *opts = inbuff + 34;
2101 void *end = inbuff + sz;
2102 for (opt = opts; opt; opt = opt6_next(opt, end))
2103 if (opt6_type(opt) == OPTION6_RELAY_MSG && opt6_len(opt) > 0)
2104 {
2105 int encap_type = *((unsigned char *)opt6_ptr(opt, 0));
2106 put_opt6(opt6_ptr(opt, 0), opt6_len(opt));
2107 memcpy(&peer->sin6_addr, &inbuff[18], IN6ADDRSZ);
2108 peer->sin6_scope_id = relay->iface_index;
2109 return encap_type == DHCP6RELAYREPL ? DHCPV6_SERVER_PORT : DHCPV6_CLIENT_PORT;
2110 }
2111 }
2112
2113 return 0;
2114}
2115
Simon Kelleyc72daea2012-01-05 21:33:27 +00002116#endif