dhcpv6.c 25.6 KB
Newer Older
K
Kozlov Dmitry 已提交
1 2 3 4 5 6 7 8 9
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <pthread.h>
K
Kozlov Dmitry 已提交
10
#include <endian.h>
K
Kozlov Dmitry 已提交
11 12 13
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
14 15 16
#include <sys/ioctl.h>
#include <linux/route.h>
#include <linux/ipv6_route.h>
K
Kozlov Dmitry 已提交
17 18 19 20 21 22 23

#include "triton.h"
#include "mempool.h"
#include "log.h"
#include "ppp.h"
#include "ipdb.h"
#include "events.h"
24
#include "iputils.h"
K
Kozlov Dmitry 已提交
25 26 27

#include "dhcpv6.h"

28 29
#include "memdebug.h"

K
Kozlov Dmitry 已提交
30 31 32
#define BUF_SIZE 65536
#define MAX_DNS_COUNT 3

33 34 35 36 37
static struct {
	struct dhcpv6_opt_serverid hdr;
	uint64_t u64;
} __packed serverid;

K
Kozlov Dmitry 已提交
38
static int conf_verbose;
K
Kozlov Dmitry 已提交
39 40
static int conf_pref_lifetime = 604800;
static int conf_valid_lifetime = 2592000;
41
static struct dhcpv6_opt_serverid *conf_serverid = &serverid.hdr;
K
Kozlov Dmitry 已提交
42 43
static int conf_route_via_gw = 1;

K
Kozlov Dmitry 已提交
44 45 46 47 48
static struct in6_addr conf_dns[MAX_DNS_COUNT];
static int conf_dns_count;
static void *conf_dnssl;
static int conf_dnssl_size;

49
struct dhcpv6_pd {
50
	struct ap_private pd;
51 52
	struct ap_session *ses;
	struct triton_md_handler_t hnd;
K
Kozlov Dmitry 已提交
53
	struct dhcpv6_opt_clientid *clientid;
54 55
	uint32_t addr_iaid;
	uint32_t dp_iaid;
56
	int dp_active:1;
K
Kozlov Dmitry 已提交
57 58 59 60
};

static void *pd_key;

61 62 63
static int dhcpv6_read(struct triton_md_handler_t *h);

static void ev_ses_started(struct ap_session *ses)
K
Kozlov Dmitry 已提交
64 65 66
{
	struct ipv6_mreq mreq;
	struct dhcpv6_pd *pd;
67
	struct sockaddr_in6 addr;
68
	struct ipv6db_addr_t *a;
69 70
	int sock;
	int f = 1;
K
Kozlov Dmitry 已提交
71

72
	if (!ses->ipv6 || list_empty(&ses->ipv6->addr_list))
K
Kozlov Dmitry 已提交
73
		return;
74

75
	a = list_entry(ses->ipv6->addr_list.next, typeof(*a), entry);
76
	if (a->prefix_len == 0 || IN6_IS_ADDR_UNSPECIFIED(&a->addr))
77 78
		return;

79
	sock = net->socket(AF_INET6, SOCK_DGRAM, 0);
80 81 82 83
	if (!sock) {
		log_ppp_error("dhcpv6: socket: %s\n", strerror(errno));
		return;
	}
84

85
	net->setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &f, sizeof(f));
K
Kozlov Dmitry 已提交
86

87
	if (net->setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, ses->ifname, strlen(ses->ifname))) {
88 89 90 91
		log_ppp_error("ipv6_nd: setsockopt(SO_BINDTODEVICE): %s\n", strerror(errno));
		close(sock);
		return;
	}
92

93 94 95
	memset(&addr, 0, sizeof(addr));
	addr.sin6_family = AF_INET6;
	addr.sin6_port = htons(DHCPV6_SERV_PORT);
96

97
	if (net->bind(sock, (struct sockaddr *)&addr, sizeof(addr))) {
98 99 100 101
		log_ppp_error("dhcpv6: bind: %s\n", strerror(errno));
		close(sock);
		return;
	}
K
Kozlov Dmitry 已提交
102 103

	memset(&mreq, 0, sizeof(mreq));
104
	mreq.ipv6mr_interface = ses->ifindex;
K
Kozlov Dmitry 已提交
105 106 107
	mreq.ipv6mr_multiaddr.s6_addr32[0] = htonl(0xff020000);
	mreq.ipv6mr_multiaddr.s6_addr32[3] = htonl(0x010002);

108
	if (net->setsockopt(sock, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq))) {
K
Kozlov Dmitry 已提交
109
		log_ppp_error("dhcpv6: failed to join to All_DHCP_Relay_Agents_and_Servers\n");
110
		close(sock);
K
Kozlov Dmitry 已提交
111 112
		return;
	}
113

114
	fcntl(sock, F_SETFD, fcntl(sock, F_GETFD) | FD_CLOEXEC);
115
	net->set_nonblocking(sock, 1);
116 117 118

	pd = _malloc(sizeof(*pd));
	memset(pd, 0, sizeof(*pd));
119

120 121 122 123 124 125 126 127 128
	pd->pd.key = &pd_key;
	list_add_tail(&pd->pd.entry, &ses->pd_list);

	pd->ses = ses;

	pd->hnd.fd = sock;
	pd->hnd.read = dhcpv6_read;
	triton_md_register_handler(ses->ctrl->ctx, &pd->hnd);
	triton_md_enable_handler(&pd->hnd, MD_MODE_READ);
K
Kozlov Dmitry 已提交
129 130
}

131
static struct dhcpv6_pd *find_pd(struct ap_session *ses)
K
Kozlov Dmitry 已提交
132
{
133
	struct ap_private *pd;
K
Kozlov Dmitry 已提交
134

135
	list_for_each_entry(pd, &ses->pd_list, entry) {
K
Kozlov Dmitry 已提交
136 137 138 139 140 141 142
		if (pd->key == &pd_key)
			return container_of(pd, struct dhcpv6_pd, pd);
	}

	return NULL;
}

143
static void ev_ses_finished(struct ap_session *ses)
K
Kozlov Dmitry 已提交
144
{
145
	struct dhcpv6_pd *pd = find_pd(ses);
K
Kozlov Dmitry 已提交
146 147 148 149 150 151 152 153

	if (!pd)
		return;

	list_del(&pd->pd.entry);

	if (pd->clientid)
		_free(pd->clientid);
154

155 156 157 158
	if (ses->ipv6_dp) {
		if (pd->dp_active) {
			struct ipv6db_addr_t *p;
			list_for_each_entry(p, &ses->ipv6_dp->prefix_list, entry)
159
				ip6route_del(0, &p->addr, p->prefix_len, NULL, 0, 0);
160 161
		}

162
		ipdb_put_ipv6_prefix(ses, ses->ipv6_dp);
163
	}
164

165
	triton_md_unregister_handler(&pd->hnd, 1);
K
Kozlov Dmitry 已提交
166 167 168 169

	_free(pd);
}

170
static void insert_dp_routes(struct ap_session *ses, struct dhcpv6_pd *pd, struct in6_addr *addr)
171 172 173 174 175 176 177 178
{
	struct ipv6db_addr_t *p;
	struct in6_rtmsg rt6;
	char str1[INET6_ADDRSTRLEN];
	char str2[INET6_ADDRSTRLEN];
	int err;

	memset(&rt6, 0, sizeof(rt6));
179
	rt6.rtmsg_ifindex = ses->ifindex;
K
Kozlov Dmitry 已提交
180
	rt6.rtmsg_flags = RTF_UP;
181

182
	if (conf_route_via_gw && addr && !IN6_IS_ADDR_UNSPECIFIED(addr)) {
183
		rt6.rtmsg_flags |= RTF_GATEWAY;
184
		memcpy(&rt6.rtmsg_gateway, addr, sizeof(rt6.rtmsg_gateway));
185 186
	}

187
	list_for_each_entry(p, &ses->ipv6_dp->prefix_list, entry) {
188 189
		memcpy(&rt6.rtmsg_dst, &p->addr, sizeof(p->addr));
		rt6.rtmsg_dst_len = p->prefix_len;
190 191 192 193 194 195 196 197 198 199 200 201

		if (net->sock6_ioctl(SIOCADDRT, &rt6)) {
			err = errno;
			inet_ntop(AF_INET6, &p->addr, str1, sizeof(str1));
			inet_ntop(AF_INET6, &rt6.rtmsg_gateway, str2, sizeof(str2));
			log_ppp_error("dhcpv6: route add %s/%i%s%s: %s\n", str1, p->prefix_len,
					conf_route_via_gw ? " via " : "", str2, strerror(err));
		} else if (conf_verbose) {
			inet_ntop(AF_INET6, &p->addr, str1, sizeof(str1));
			inet_ntop(AF_INET6, &rt6.rtmsg_gateway, str2, sizeof(str2));
			log_ppp_info2("dhcpv6: route add %s/%i%s%s\n", str1, p->prefix_len,
					conf_route_via_gw ? " via " : "", str2);
202 203 204 205 206 207
		}
	}

	pd->dp_active = 1;
}

K
Kozlov Dmitry 已提交
208 209 210 211 212 213 214 215 216 217 218 219 220 221
static void insert_status(struct dhcpv6_packet *pkt, struct dhcpv6_option *opt, int code)
{
	struct dhcpv6_option *opt1;
	struct dhcpv6_opt_status *status;

	if (opt)
		opt1 = dhcpv6_nested_option_alloc(pkt, opt, D6_OPTION_STATUS_CODE, sizeof(struct dhcpv6_opt_status) - sizeof(struct dhcpv6_opt_hdr));
	else
		opt1 = dhcpv6_option_alloc(pkt, D6_OPTION_STATUS_CODE, sizeof(struct dhcpv6_opt_status) - sizeof(struct dhcpv6_opt_hdr));

	status = (struct dhcpv6_opt_status *)opt1->hdr;
	status->code = htons(code);
}

K
Kozlov Dmitry 已提交
222 223 224 225 226 227 228 229 230
static void insert_oro(struct dhcpv6_packet *reply, struct dhcpv6_option *opt)
{
	struct dhcpv6_option *opt1;
	int i, j;
	uint16_t *ptr;
	struct in6_addr addr, *addr_ptr;

	for (i = ntohs(opt->hdr->len) / 2, ptr = (uint16_t *)opt->hdr->data; i; i--, ptr++) {
		if (ntohs(*ptr) == D6_OPTION_DNS_SERVERS) {
231 232 233 234 235
			if (conf_dns_count) {
				opt1 = dhcpv6_option_alloc(reply, D6_OPTION_DNS_SERVERS, conf_dns_count * sizeof(addr));
				for (j = 0, addr_ptr = (struct in6_addr *)opt1->hdr->data; j < conf_dns_count; j++, addr_ptr++)
					memcpy(addr_ptr, conf_dns + j, sizeof(addr));
			}
K
Kozlov Dmitry 已提交
236
		} else if (ntohs(*ptr) == D6_OPTION_DOMAIN_LIST) {
237 238 239 240
			if (conf_dnssl_size) {
				opt1 = dhcpv6_option_alloc(reply, D6_OPTION_DOMAIN_LIST, conf_dnssl_size);
				memcpy(opt1->hdr->data, conf_dnssl, conf_dnssl_size);
			}
K
Kozlov Dmitry 已提交
241 242 243 244
		}
	}
}

K
Kozlov Dmitry 已提交
245 246 247
static void dhcpv6_send_reply(struct dhcpv6_packet *req, struct dhcpv6_pd *pd, int code)
{
	struct dhcpv6_packet *reply;
K
Kozlov Dmitry 已提交
248
	struct dhcpv6_option *opt, *opt1, *opt2, *opt3;
K
Kozlov Dmitry 已提交
249 250
	struct dhcpv6_opt_ia_na *ia_na;
	struct dhcpv6_opt_ia_addr *ia_addr;
251
	struct dhcpv6_opt_ia_prefix *ia_prefix;
K
Kozlov Dmitry 已提交
252
	struct ipv6db_addr_t *a;
K
Kozlov Dmitry 已提交
253
	struct in6_addr addr;
254
	struct ap_session *ses = req->ses;
K
Kozlov Dmitry 已提交
255
	int f = 0, f1, f2 = 0;
K
Kozlov Dmitry 已提交
256 257 258 259

	reply = dhcpv6_packet_alloc_reply(req, code);
	if (!reply)
		return;
260

K
Kozlov Dmitry 已提交
261
	list_for_each_entry(opt, &req->opt_list, entry) {
262 263

		// IA_NA
264 265 266 267
		if (ntohs(opt->hdr->code) == D6_OPTION_IA_NA) {
			if (req->hdr->type == D6_INFORMATION_REQUEST)
				continue;

K
Kozlov Dmitry 已提交
268 269
			opt1 = dhcpv6_option_alloc(reply, D6_OPTION_IA_NA, sizeof(struct dhcpv6_opt_ia_na) - sizeof(struct dhcpv6_opt_hdr));
			memcpy(opt1->hdr + 1, opt->hdr + 1, ntohs(opt1->hdr->len));
270 271

			ia_na = (struct dhcpv6_opt_ia_na *)opt1->hdr;
272 273
			ia_na->T1 = conf_pref_lifetime == -1 ? -1 : htonl(conf_pref_lifetime / 2);
			ia_na->T2 = conf_pref_lifetime == -1 ? -1 : htonl((conf_pref_lifetime * 4) / 5);
274

K
Kozlov Dmitry 已提交
275
			if (req->hdr->type == D6_RENEW && pd->addr_iaid != ia_na->iaid) {
K
Kozlov Dmitry 已提交
276
				insert_status(reply, opt1, D6_STATUS_NoBinding);
277
			} else if (list_empty(&ses->ipv6->addr_list) || f) {
K
Kozlov Dmitry 已提交
278
				insert_status(reply, opt1, D6_STATUS_NoAddrsAvail);
K
Kozlov Dmitry 已提交
279
			} else {
280

K
Kozlov Dmitry 已提交
281
				if (req->hdr->type == D6_REQUEST || req->rapid_commit)
282
					pd->addr_iaid = ia_na->iaid;
K
Kozlov Dmitry 已提交
283 284 285

				f = 1;

286
				list_for_each_entry(a, &ses->ipv6->addr_list, entry) {
K
Kozlov Dmitry 已提交
287 288 289
					opt2 = dhcpv6_nested_option_alloc(reply, opt1, D6_OPTION_IAADDR, sizeof(*ia_addr) - sizeof(struct dhcpv6_opt_hdr));
					ia_addr = (struct dhcpv6_opt_ia_addr *)opt2->hdr;

290
					build_ip6_addr(a, ses->ipv6->peer_intf_id, &ia_addr->addr);
K
Kozlov Dmitry 已提交
291 292

					ia_addr->pref_lifetime = htonl(conf_pref_lifetime);
293
					ia_addr->valid_lifetime = htonl(conf_valid_lifetime);
294

D
Dmitry Kozlov 已提交
295
					if (!a->installed) {
296 297 298 299 300 301 302 303
						struct in6_addr addr, peer_addr;
						if (a->prefix_len == 128) {
							memcpy(addr.s6_addr, &a->addr, 8);
							memcpy(addr.s6_addr + 8, &ses->ipv6->intf_id, 8);
							memcpy(peer_addr.s6_addr, &a->addr, 8);
							memcpy(peer_addr.s6_addr + 8, &ses->ipv6->peer_intf_id, 8);
							ip6addr_add_peer(ses->ifindex, &addr, &peer_addr);
						} else {
304 305 306
							build_ip6_addr(a, ses->ipv6->intf_id, &addr);
							if (memcmp(&addr, &ia_addr->addr, sizeof(addr)) == 0)
								build_ip6_addr(a, ~ses->ipv6->intf_id, &addr);
307 308 309 310
							ip6addr_add(ses->ifindex, &addr, a->prefix_len);
						}
						a->installed = 1;
					}
K
Kozlov Dmitry 已提交
311 312
				}

313 314 315 316
				if (code == D6_REPLY) {
					list_for_each_entry(opt2, &opt->opt_list, entry) {
						if (ntohs(opt2->hdr->code) == D6_OPTION_IAADDR) {
							ia_addr = (struct dhcpv6_opt_ia_addr *)opt2->hdr;
317

318
							if (IN6_IS_ADDR_UNSPECIFIED(&ia_addr->addr))
K
Kozlov Dmitry 已提交
319
								continue;
320

321 322
							f1 = 0;
							list_for_each_entry(a, &ses->ipv6->addr_list, entry) {
323
								build_ip6_addr(a, ses->ipv6->peer_intf_id, &addr);
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
								if (memcmp(&addr, &ia_addr->addr, sizeof(addr)))
									continue;
								f1 = 1;
								break;
							}

							if (!f1) {
								opt3 = dhcpv6_nested_option_alloc(reply, opt1, D6_OPTION_IAADDR, sizeof(*ia_addr) - sizeof(struct dhcpv6_opt_hdr));
								memcpy(opt3->hdr->data, opt2->hdr->data, sizeof(*ia_addr) - sizeof(struct dhcpv6_opt_hdr));

								ia_addr = (struct dhcpv6_opt_ia_addr *)opt3->hdr;
								ia_addr->pref_lifetime = 0;
								ia_addr->valid_lifetime = 0;

								insert_status(reply, opt3, D6_STATUS_NotOnLink);
							}
K
Kozlov Dmitry 已提交
340 341 342
						}
					}
				}
K
Kozlov Dmitry 已提交
343

K
Kozlov Dmitry 已提交
344
				//insert_status(reply, opt1, D6_STATUS_Success);
K
Kozlov Dmitry 已提交
345
			}
346 347

		// IA_PD
348 349 350 351
		} else if (ntohs(opt->hdr->code) == D6_OPTION_IA_PD) {
			if (req->hdr->type == D6_INFORMATION_REQUEST)
				continue;

352 353
			opt1 = dhcpv6_option_alloc(reply, D6_OPTION_IA_PD, sizeof(struct dhcpv6_opt_ia_na) - sizeof(struct dhcpv6_opt_hdr));
			memcpy(opt1->hdr + 1, opt->hdr + 1, ntohs(opt1->hdr->len));
354

355
			ia_na = (struct dhcpv6_opt_ia_na *)opt1->hdr;
356 357
			ia_na->T1 = conf_pref_lifetime == -1 ? -1 : htonl(conf_pref_lifetime / 2);
			ia_na->T2 = conf_pref_lifetime == -1 ? -1 : htonl((conf_pref_lifetime * 4) / 5);
358

359
			if (!ses->ipv6_dp) {
360
				ses->ipv6_dp = ipdb_get_ipv6_prefix(ses);
361 362 363
				if (ses->ipv6_dp)
					triton_event_fire(EV_FORCE_INTERIM_UPDATE, ses);
			}
364

K
Kozlov Dmitry 已提交
365
			if ((req->hdr->type == D6_RENEW) && pd->dp_iaid != ia_na->iaid) {
K
Kozlov Dmitry 已提交
366
				insert_status(reply, opt1, D6_STATUS_NoBinding);
367
			} else if (!ses->ipv6_dp || list_empty(&ses->ipv6_dp->prefix_list) || f2) {
K
Kozlov Dmitry 已提交
368
				insert_status(reply, opt1, D6_STATUS_NoPrefixAvail);
369 370
			} else {

K
Kozlov Dmitry 已提交
371
				if (req->hdr->type == D6_REQUEST || req->rapid_commit) {
372
					pd->dp_iaid = ia_na->iaid;
373
					if (!pd->dp_active)
374
						insert_dp_routes(ses, pd, &req->addr.sin6_addr);
375
				}
376 377 378

				f2 = 1;

379
				list_for_each_entry(a, &ses->ipv6_dp->prefix_list, entry) {
380 381
					opt2 = dhcpv6_nested_option_alloc(reply, opt1, D6_OPTION_IAPREFIX, sizeof(*ia_prefix) - sizeof(struct dhcpv6_opt_hdr));
					ia_prefix = (struct dhcpv6_opt_ia_prefix *)opt2->hdr;
382

383 384 385
					memcpy(&ia_prefix->prefix, &a->addr, sizeof(a->addr));
					ia_prefix->prefix_len = a->prefix_len;
					ia_prefix->pref_lifetime = htonl(conf_pref_lifetime);
386
					ia_prefix->valid_lifetime = htonl(conf_valid_lifetime);
387 388
				}

389 390 391 392
				if (code == D6_REPLY) {
					list_for_each_entry(opt2, &opt->opt_list, entry) {
						if (ntohs(opt2->hdr->code) == D6_OPTION_IAPREFIX) {
							ia_prefix = (struct dhcpv6_opt_ia_prefix *)opt2->hdr;
393

394
							if (ia_prefix->prefix_len == 0 || IN6_IS_ADDR_UNSPECIFIED(&ia_prefix->prefix))
395 396
								continue;

397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
							f1 = 0;
							list_for_each_entry(a, &ses->ipv6_dp->prefix_list, entry) {
								if (a->prefix_len != ia_prefix->prefix_len)
									continue;
								if (memcmp(&a->addr, &ia_prefix->prefix, sizeof(a->addr)))
									continue;
								f1 = 1;
								break;
							}

							if (!f1) {
								opt3 = dhcpv6_nested_option_alloc(reply, opt1, D6_OPTION_IAPREFIX, sizeof(*ia_prefix) - sizeof(struct dhcpv6_opt_hdr));
								memcpy(opt3->hdr->data, opt2->hdr->data, sizeof(*ia_prefix) - sizeof(struct dhcpv6_opt_hdr));
								ia_prefix = (struct dhcpv6_opt_ia_prefix *)opt3->hdr;
								ia_prefix->pref_lifetime = 0;
								ia_prefix->valid_lifetime = 0;

								insert_status(reply, opt3, D6_STATUS_NotOnLink);
							}
416 417 418
						}
					}
				}
419

K
Kozlov Dmitry 已提交
420
				//insert_status(reply, opt1, D6_STATUS_Success);
421 422 423
		}

		// IA_TA
424 425 426 427
		} else if (ntohs(opt->hdr->code) == D6_OPTION_IA_TA) {
			if (req->hdr->type == D6_INFORMATION_REQUEST)
				continue;

K
Kozlov Dmitry 已提交
428 429
			opt1 = dhcpv6_option_alloc(reply, D6_OPTION_IA_TA, sizeof(struct dhcpv6_opt_ia_ta) - sizeof(struct dhcpv6_opt_hdr));
			memcpy(opt1->hdr + 1, opt->hdr + 1, ntohs(opt1->hdr->len));
K
Kozlov Dmitry 已提交
430 431

			insert_status(reply, opt1, D6_STATUS_NoAddrsAvail);
432 433

		// Option Request
434
		} else if (ntohs(opt->hdr->code) == D6_OPTION_ORO) {
K
Kozlov Dmitry 已提交
435
			insert_oro(reply, opt);
436

437
		} else if (ntohs(opt->hdr->code) == D6_OPTION_RAPID_COMMIT) {
438 439
			if (req->hdr->type == D6_SOLICIT)
				dhcpv6_option_alloc(reply, D6_OPTION_RAPID_COMMIT, 0);
440
		}
K
Kozlov Dmitry 已提交
441
	}
442 443 444 445 446 447 448 449 450 451 452

	opt1 = dhcpv6_option_alloc(reply, D6_OPTION_PREFERENCE, 1);
	*(uint8_t *)opt1->hdr->data = 255;

	//insert_status(reply, NULL, D6_STATUS_Success);

	if (conf_verbose) {
		log_ppp_info2("send ");
		dhcpv6_packet_print(reply, log_ppp_info2);
	}

453
	net->sendto(pd->hnd.fd, reply->hdr, reply->endptr - (void *)reply->hdr, 0, (struct sockaddr *)&req->addr, sizeof(req->addr));
454 455 456 457 458 459 460 461 462 463 464 465 466

	dhcpv6_packet_free(reply);
}

static void dhcpv6_send_reply2(struct dhcpv6_packet *req, struct dhcpv6_pd *pd, int code)
{
	struct dhcpv6_packet *reply;
	struct dhcpv6_option *opt, *opt1, *opt2, *opt3;
	struct dhcpv6_opt_ia_na *ia_na;
	struct dhcpv6_opt_ia_addr *ia_addr;
	struct dhcpv6_opt_ia_prefix *ia_prefix;
	struct ipv6db_addr_t *a;
	struct in6_addr addr;
467
	struct ap_session *ses = req->ses;
468 469 470 471 472
	int f = 0, f1, f2 = 0, f3;

	reply = dhcpv6_packet_alloc_reply(req, code);
	if (!reply)
		return;
473

474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
	list_for_each_entry(opt, &req->opt_list, entry) {

		// IA_NA
		if (ntohs(opt->hdr->code) == D6_OPTION_IA_NA) {
			opt1 = dhcpv6_option_alloc(reply, D6_OPTION_IA_NA, sizeof(struct dhcpv6_opt_ia_na) - sizeof(struct dhcpv6_opt_hdr));
			memcpy(opt1->hdr + 1, opt->hdr + 1, ntohs(opt1->hdr->len));

			ia_na = (struct dhcpv6_opt_ia_na *)opt1->hdr;
			ia_na->T1 = conf_pref_lifetime == -1 ? -1 : htonl(conf_pref_lifetime / 2);
			ia_na->T2 = conf_pref_lifetime == -1 ? -1 : htonl((conf_pref_lifetime * 4) / 5);

			f3 = 0;

			list_for_each_entry(opt2, &opt->opt_list, entry) {
				if (ntohs(opt2->hdr->code) == D6_OPTION_IAADDR) {
					ia_addr = (struct dhcpv6_opt_ia_addr *)opt2->hdr;

					if (IN6_IS_ADDR_UNSPECIFIED(&ia_addr->addr))
						continue;

					f1 = 0;
495

496
					if (!f) {
497
						list_for_each_entry(a, &ses->ipv6->addr_list, entry) {
498
							build_ip6_addr(a, ses->ipv6->peer_intf_id, &addr);
499 500 501 502 503 504 505 506 507 508 509 510 511 512
							if (memcmp(&addr, &ia_addr->addr, sizeof(addr)))
								continue;
							f1 = 1;
							f3 = 1;
							break;
						}
					}

					opt3 = dhcpv6_nested_option_alloc(reply, opt1, D6_OPTION_IAADDR, sizeof(*ia_addr) - sizeof(struct dhcpv6_opt_hdr));
					memcpy(opt3->hdr->data, opt2->hdr->data, sizeof(*ia_addr) - sizeof(struct dhcpv6_opt_hdr));

					ia_addr = (struct dhcpv6_opt_ia_addr *)opt3->hdr;
					if (f1) {
						ia_addr->pref_lifetime = htonl(conf_pref_lifetime);
513
						ia_addr->valid_lifetime = htonl(conf_valid_lifetime);
514 515 516 517
					} else {
						ia_addr->pref_lifetime = 0;
						ia_addr->valid_lifetime = 0;

518
						goto out;
519 520 521 522 523 524 525 526 527 528 529 530 531 532
					}
				}
			}

			if (f3) {
				pd->addr_iaid = ia_na->iaid;
				f = 1;
			}


		// IA_PD
		} else if (ntohs(opt->hdr->code) == D6_OPTION_IA_PD) {
			opt1 = dhcpv6_option_alloc(reply, D6_OPTION_IA_PD, sizeof(struct dhcpv6_opt_ia_na) - sizeof(struct dhcpv6_opt_hdr));
			memcpy(opt1->hdr + 1, opt->hdr + 1, ntohs(opt1->hdr->len));
533

534 535 536
			ia_na = (struct dhcpv6_opt_ia_na *)opt1->hdr;
			ia_na->T1 = conf_pref_lifetime == -1 ? -1 : htonl(conf_pref_lifetime / 2);
			ia_na->T2 = conf_pref_lifetime == -1 ? -1 : htonl((conf_pref_lifetime * 4) / 5);
537

538 539 540 541 542
			if (!ses->ipv6_dp) {
				ses->ipv6_dp = ipdb_get_ipv6_prefix(ses);
				if (ses->ipv6_dp)
					triton_event_fire(EV_FORCE_INTERIM_UPDATE, ses);
			}
543

544 545 546
			if (!ses->ipv6_dp)
				goto out;

547 548 549 550 551 552 553 554 555 556 557 558
			f3 = 0;

			list_for_each_entry(opt2, &opt->opt_list, entry) {
				if (ntohs(opt2->hdr->code) == D6_OPTION_IAPREFIX) {
					ia_prefix = (struct dhcpv6_opt_ia_prefix *)opt2->hdr;

					if (ia_prefix->prefix_len == 0 || IN6_IS_ADDR_UNSPECIFIED(&ia_prefix->prefix))
						continue;

					f1 = 0;

					if (!f2) {
559
						list_for_each_entry(a, &ses->ipv6_dp->prefix_list, entry) {
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
							if (a->prefix_len != ia_prefix->prefix_len)
								continue;
							if (memcmp(&a->addr, &ia_prefix->prefix, sizeof(a->addr)))
								continue;
							f1 = 1;
							f3 = 1;
							break;
						}
					}

					opt3 = dhcpv6_nested_option_alloc(reply, opt1, D6_OPTION_IAPREFIX, sizeof(*ia_prefix) - sizeof(struct dhcpv6_opt_hdr));
					memcpy(opt3->hdr->data, opt2->hdr->data, sizeof(*ia_prefix) - sizeof(struct dhcpv6_opt_hdr));
					ia_prefix = (struct dhcpv6_opt_ia_prefix *)opt3->hdr;

					if (f1) {
						ia_prefix->pref_lifetime = htonl(conf_pref_lifetime);
576
						ia_prefix->valid_lifetime = htonl(conf_valid_lifetime);
577 578 579 580
					} else {
						ia_prefix->pref_lifetime = 0;
						ia_prefix->valid_lifetime = 0;

581
						goto out;
582 583 584 585 586 587 588 589 590
					}
				}
			}

			if (f3) {
				pd->dp_iaid = ia_na->iaid;
				f2 = 1;
			}
		// Option Request
591
		} else if (ntohs(opt->hdr->code) == D6_OPTION_ORO)
592 593 594
			insert_oro(reply, opt);
	}

K
Kozlov Dmitry 已提交
595 596 597 598
	opt1 = dhcpv6_option_alloc(reply, D6_OPTION_PREFERENCE, 1);
	*(uint8_t *)opt1->hdr->data = 255;

	//insert_status(reply, NULL, D6_STATUS_Success);
K
Kozlov Dmitry 已提交
599 600 601 602 603 604

	if (conf_verbose) {
		log_ppp_info2("send ");
		dhcpv6_packet_print(reply, log_ppp_info2);
	}

605
	net->sendto(pd->hnd.fd, reply->hdr, reply->endptr - (void *)reply->hdr, 0, (struct sockaddr *)&req->addr, sizeof(req->addr));
K
Kozlov Dmitry 已提交
606

607
out:
K
Kozlov Dmitry 已提交
608 609 610
	dhcpv6_packet_free(reply);
}

611

K
Kozlov Dmitry 已提交
612 613
static void dhcpv6_recv_solicit(struct dhcpv6_packet *req)
{
614
	struct dhcpv6_pd *pd = req->pd;
K
Kozlov Dmitry 已提交
615 616 617 618 619 620 621 622 623 624 625

	if (!req->clientid) {
		log_ppp_error("dhcpv6: no Client-ID option\n");
		return;
	}

	if (req->serverid) {
		log_ppp_error("dhcpv6: unexpected Server-ID option\n");
		return;
	}

626
	req->serverid = conf_serverid;
K
Kozlov Dmitry 已提交
627

K
Kozlov Dmitry 已提交
628 629 630 631 632 633 634 635
	if (req->rapid_commit) {
		if (!pd->clientid) {
			pd->clientid = _malloc(sizeof(struct dhcpv6_opt_hdr) + ntohs(req->clientid->hdr.len));
			memcpy(pd->clientid, req->clientid, sizeof(struct dhcpv6_opt_hdr) + ntohs(req->clientid->hdr.len));
		} else if (pd->clientid->hdr.len != req->clientid->hdr.len || memcmp(pd->clientid, req->clientid, sizeof(struct dhcpv6_opt_hdr) + ntohs(req->clientid->hdr.len))) {
			log_ppp_error("dhcpv6: unmatched Client-ID option\n");
			return;
		}
K
Kozlov Dmitry 已提交
636 637
	}

K
Kozlov Dmitry 已提交
638
	dhcpv6_send_reply(req, pd, req->rapid_commit ? D6_REPLY : D6_ADVERTISE);
K
Kozlov Dmitry 已提交
639 640 641 642
}

static void dhcpv6_recv_request(struct dhcpv6_packet *req)
{
643
	struct dhcpv6_pd *pd = req->pd;
K
Kozlov Dmitry 已提交
644 645 646 647 648 649 650 651 652 653 654

	if (!req->clientid) {
		log_ppp_error("dhcpv6: no Client-ID option\n");
		return;
	}

	if (!req->serverid) {
		log_ppp_error("dhcpv6: no Server-ID option\n");
		return;
	}

K
Kozlov Dmitry 已提交
655 656 657 658 659 660
	if (!pd->clientid) {
		pd->clientid = _malloc(sizeof(struct dhcpv6_opt_hdr) + ntohs(req->clientid->hdr.len));
		memcpy(pd->clientid, req->clientid, sizeof(struct dhcpv6_opt_hdr) + ntohs(req->clientid->hdr.len));
	} else if (pd->clientid->hdr.len != req->clientid->hdr.len || memcmp(pd->clientid, req->clientid, sizeof(struct dhcpv6_opt_hdr) + ntohs(req->clientid->hdr.len))) {
		log_ppp_error("dhcpv6: unmatched Client-ID option\n");
		return;
K
Kozlov Dmitry 已提交
661
	}
662

K
Kozlov Dmitry 已提交
663 664 665
	dhcpv6_send_reply(req, pd, D6_REPLY);
}

666
static void dhcpv6_recv_renew(struct dhcpv6_packet *req)
K
Kozlov Dmitry 已提交
667
{
668
	struct dhcpv6_pd *pd = req->pd;
K
Kozlov Dmitry 已提交
669

670 671 672 673 674 675 676 677 678 679
	if (!req->clientid) {
		log_ppp_error("dhcpv6: no Client-ID option\n");
		return;
	}

	if (!req->serverid) {
		log_ppp_error("dhcpv6: no Server-ID option\n");
		return;
	}

680 681
	if (req->serverid->hdr.len != conf_serverid->hdr.len ||
		memcmp(req->serverid, conf_serverid, ntohs(conf_serverid->hdr.len) + sizeof(struct dhcpv6_opt_hdr))) {
K
Kozlov Dmitry 已提交
682 683 684 685
		log_ppp_error("dhcpv6: unmatched Server-ID option\n");
		return;
	}

686
	if (!pd->clientid) {
K
Kozlov Dmitry 已提交
687 688 689
		log_ppp_error("dhcpv6: no Request was received\n");
		return;
	}
690

K
Kozlov Dmitry 已提交
691 692 693
	if (req->clientid->hdr.len != pd->clientid->hdr.len ||
		memcmp(req->clientid, pd->clientid, ntohs(pd->clientid->hdr.len) + sizeof(struct dhcpv6_opt_hdr))) {
		log_ppp_error("dhcpv6: unmatched Client-ID option\n");
694 695
		return;
	}
696

697
	dhcpv6_send_reply(req, pd, D6_REPLY);
K
Kozlov Dmitry 已提交
698 699
}

700 701
static void dhcpv6_recv_information_request(struct dhcpv6_packet *req)
{
702
	struct dhcpv6_pd *pd = req->pd;
703

704 705 706 707 708
	if (req->rapid_commit) {
		log_ppp_error("dhcpv6: unexpected Rapid-Commit option\n");
		return;
	}

709
	req->serverid = conf_serverid;
710 711 712 713

	dhcpv6_send_reply(req, pd, D6_REPLY);
}

K
Kozlov Dmitry 已提交
714
static void dhcpv6_recv_rebind(struct dhcpv6_packet *req)
K
Kozlov Dmitry 已提交
715
{
716
	struct dhcpv6_pd *pd = req->pd;
717 718 719 720 721 722 723 724 725 726 727

	if (!req->clientid) {
		log_ppp_error("dhcpv6: no Client-ID option\n");
		return;
	}

	if (req->serverid) {
		log_ppp_error("dhcpv6: unexcpected Server-ID option\n");
		return;
	}

728 729 730
	if (!pd->clientid)
		return;
	else if (pd->clientid->hdr.len != req->clientid->hdr.len || memcmp(pd->clientid, req->clientid, sizeof(struct dhcpv6_opt_hdr) + ntohs(req->clientid->hdr.len))) {
731 732 733
		log_ppp_error("dhcpv6: unmatched Client-ID option\n");
		return;
	}
734

735
	req->serverid = conf_serverid;
736 737

	dhcpv6_send_reply2(req, pd, D6_REPLY);
K
Kozlov Dmitry 已提交
738 739 740 741
}

static void dhcpv6_recv_release(struct dhcpv6_packet *pkt)
{
K
Kozlov Dmitry 已提交
742
	// don't answer
K
Kozlov Dmitry 已提交
743 744 745 746
}

static void dhcpv6_recv_decline(struct dhcpv6_packet *pkt)
{
K
Kozlov Dmitry 已提交
747
	// don't answer
K
Kozlov Dmitry 已提交
748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775
}

static void dhcpv6_recv_packet(struct dhcpv6_packet *pkt)
{
	if (conf_verbose) {
		log_ppp_info2("recv ");
		dhcpv6_packet_print(pkt, log_ppp_info2);
	}

	switch (pkt->hdr->type) {
		case D6_SOLICIT:
			dhcpv6_recv_solicit(pkt);
			break;
		case D6_REQUEST:
			dhcpv6_recv_request(pkt);
			break;
		case D6_RENEW:
			dhcpv6_recv_renew(pkt);
			break;
		case D6_REBIND:
			dhcpv6_recv_rebind(pkt);
			break;
		case D6_RELEASE:
			dhcpv6_recv_release(pkt);
			break;
		case D6_DECLINE:
			dhcpv6_recv_decline(pkt);
			break;
776 777 778
		case D6_INFORMATION_REQUEST:
			dhcpv6_recv_information_request(pkt);
			break;
K
Kozlov Dmitry 已提交
779 780 781 782 783 784 785
	}

	dhcpv6_packet_free(pkt);
}

static int dhcpv6_read(struct triton_md_handler_t *h)
{
786 787
	struct dhcpv6_pd *pd = container_of(h, typeof(*pd), hnd);
	struct ap_session *ses = pd->ses;
K
Kozlov Dmitry 已提交
788 789 790 791
	int n;
	struct sockaddr_in6 addr;
	socklen_t len = sizeof(addr);
	struct dhcpv6_packet *pkt;
792
	uint8_t *buf = _malloc(BUF_SIZE);
K
Kozlov Dmitry 已提交
793 794

	while (1) {
795
		n = net->recvfrom(h->fd, buf, BUF_SIZE, 0, (struct sockaddr *)&addr, &len);
K
Kozlov Dmitry 已提交
796 797
		if (n == -1) {
			if (errno == EAGAIN)
798
				break;
K
Kozlov Dmitry 已提交
799
			log_error("dhcpv6: read: %s\n", strerror(errno));
800
			continue;
K
Kozlov Dmitry 已提交
801 802 803 804 805 806 807 808 809 810 811 812 813
		}

		if (!IN6_IS_ADDR_LINKLOCAL(&addr.sin6_addr))
			continue;

		if (addr.sin6_port != ntohs(DHCPV6_CLIENT_PORT))
			continue;

		pkt = dhcpv6_packet_parse(buf, n);
		if (!pkt || !pkt->clientid) {
			continue;
		}

814 815 816
		pkt->ses = ses;
		pkt->pd = pd;
		pkt->addr = addr;
K
Kozlov Dmitry 已提交
817

818
		dhcpv6_recv_packet(pkt);
K
Kozlov Dmitry 已提交
819 820
	}

821
	_free(buf);
K
Kozlov Dmitry 已提交
822

823
	return 0;
K
Kozlov Dmitry 已提交
824 825 826 827 828 829 830 831 832 833 834 835
}

static void add_dnssl(const char *val)
{
	int n = strlen(val);
	const char *ptr;
	uint8_t *buf;

	if (val[n - 1] == '.')
		n++;
	else
		n += 2;
836

K
Kozlov Dmitry 已提交
837 838 839 840
	if (n > 255) {
		log_error("dnsv6: dnssl '%s' is too long\n", val);
		return;
	}
841

K
Kozlov Dmitry 已提交
842 843 844 845
	if (!conf_dnssl)
		conf_dnssl = _malloc(n);
	else
		conf_dnssl = _realloc(conf_dnssl, conf_dnssl_size + n);
846

K
Kozlov Dmitry 已提交
847
	buf = conf_dnssl + conf_dnssl_size;
848

K
Kozlov Dmitry 已提交
849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865
	while (1) {
		ptr = strchr(val, '.');
		if (!ptr)
			ptr = strchr(val, 0);
		if (ptr - val > 63) {
			log_error("dnsv6: dnssl '%s' is invalid\n", val);
			return;
		}
		*buf = ptr - val;
		memcpy(buf + 1, val, ptr - val);
		buf += 1 + (ptr - val);
		val = ptr + 1;
		if (!*ptr || !*val) {
				*buf = 0;
				break;
		}
	}
866

K
Kozlov Dmitry 已提交
867 868 869 870 871
	conf_dnssl_size += n;
}

static void load_dns(void)
{
K
Kozlov Dmitry 已提交
872
	struct conf_sect_t *s = conf_get_section("ipv6-dns");
K
Kozlov Dmitry 已提交
873
	struct conf_option_t *opt;
874

K
Kozlov Dmitry 已提交
875 876
	if (!s)
		return;
877

K
Kozlov Dmitry 已提交
878
	conf_dns_count = 0;
879

K
Kozlov Dmitry 已提交
880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895
	if (conf_dnssl)
		_free(conf_dnssl);
	conf_dnssl = NULL;
	conf_dnssl_size = 0;

	list_for_each_entry(opt, &s->items, entry) {
		if (!strcmp(opt->name, "dnssl")) {
			add_dnssl(opt->val);
			continue;
		}

		if (!strcmp(opt->name, "dns") || !opt->val) {
			if (conf_dns_count == MAX_DNS_COUNT)
				continue;

			if (inet_pton(AF_INET6, opt->val ? opt->val : opt->name, &conf_dns[conf_dns_count]) == 0) {
K
Kozlov Dmitry 已提交
896
				log_error("dnsv6: failed to parse '%s'\n", opt->name);
K
Kozlov Dmitry 已提交
897 898 899 900 901 902 903
				continue;
			}
			conf_dns_count++;
		}
	}
}

K
Kozlov Dmitry 已提交
904 905 906 907 908
static uint64_t parse_serverid(const char *opt)
{
	union {
		uint64_t u64;
		uint16_t u16[4];
909
	} __packed u;
K
Kozlov Dmitry 已提交
910 911 912 913 914 915

	int n[4];
	int i;

	if (sscanf(opt, "%x:%x:%x:%x", &n[0], &n[1], &n[2], &n[3]) != 4)
		goto err;
916

K
Kozlov Dmitry 已提交
917 918 919 920 921 922 923 924 925 926 927 928 929
	for (i = 0; i < 4; i++) {
		if (n[i] < 0 || n[i] > 0xffff)
			goto err;
		u.u16[i] = htons(n[i]);
	}

	return u.u64;

err:
	log_error("dhcpv6: failed to parse server-id '%s'\n", opt);
	return 0;
}

K
Kozlov Dmitry 已提交
930 931 932
static void load_config(void)
{
	const char *opt;
K
Kozlov Dmitry 已提交
933
	uint64_t id;
K
Kozlov Dmitry 已提交
934

K
Kozlov Dmitry 已提交
935
	opt = conf_get_opt("ipv6-dhcp", "verbose");
K
Kozlov Dmitry 已提交
936 937
	if (opt)
		conf_verbose = atoi(opt);
938

K
Kozlov Dmitry 已提交
939 940 941
	opt = conf_get_opt("ipv6-dhcp", "pref-lifetime");
	if (opt)
		conf_pref_lifetime = atoi(opt);
942

K
Kozlov Dmitry 已提交
943 944 945
	opt = conf_get_opt("ipv6-dhcp", "valid-lifetime");
	if (opt)
		conf_valid_lifetime = atoi(opt);
946

K
Kozlov Dmitry 已提交
947 948 949
	opt = conf_get_opt("ipv6-dhcp", "route-via-gw");
	if (opt)
		conf_route_via_gw = atoi(opt);
950

K
Kozlov Dmitry 已提交
951 952 953 954 955 956
	opt = conf_get_opt("ipv6-dhcp", "server-id");
	if (opt)
		id = parse_serverid(opt);
	else
		id = htobe64(1);

957 958 959 960
	conf_serverid->hdr.code = htons(D6_OPTION_SERVERID);
	conf_serverid->hdr.len = htons(12);
	conf_serverid->duid.type = htons(DUID_LL);
	conf_serverid->duid.u.ll.htype = htons(27);
K
Kozlov Dmitry 已提交
961
	//conf_serverid.duid.u.llt.time = htonl(t - t0);
962
	memcpy(conf_serverid->duid.u.ll.addr, &id, sizeof(id));
963

K
Kozlov Dmitry 已提交
964 965 966 967 968
	load_dns();
}

static void init(void)
{
969 970 971
	if (!triton_module_loaded("ipv6_nd"))
		log_warn("dhcpv6: ipv6_nd module is not loaded, you probably get misconfigured network environment\n");

K
Kozlov Dmitry 已提交
972 973 974
	load_config();

	triton_event_register_handler(EV_CONFIG_RELOAD, (triton_event_func)load_config);
975 976
	triton_event_register_handler(EV_SES_STARTED, (triton_event_func)ev_ses_started);
	triton_event_register_handler(EV_SES_FINISHED, (triton_event_func)ev_ses_finished);
K
Kozlov Dmitry 已提交
977 978 979
}

DEFINE_INIT(10, init);