diff --git a/0141-Fix-memory-leak-in-helper.c.patch b/0141-Fix-memory-leak-in-helper.c.patch deleted file mode 100644 index 88b72f69f7af42378ec6bbbe200b504668666276..0000000000000000000000000000000000000000 --- a/0141-Fix-memory-leak-in-helper.c.patch +++ /dev/null @@ -1,46 +0,0 @@ -From 69bc94779c2f035a9fffdb5327a54c3aeca73ed5 Mon Sep 17 00:00:00 2001 -From: Simon Kelley -Date: Wed, 14 Aug 2019 20:44:50 +0100 -Subject: [PATCH 141/156] Fix memory leak in helper.c - -Thanks to Xu Mingjie for spotting this. ---- - src/helper.c | 12 +++++++++--- - 1 file changed, 9 insertions(+), 3 deletions(-) - -diff --git a/src/helper.c b/src/helper.c -index 33ba120..c392eec 100644 ---- a/src/helper.c -+++ b/src/helper.c -@@ -80,7 +80,8 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) - pid_t pid; - int i, pipefd[2]; - struct sigaction sigact; -- -+ unsigned char *alloc_buff = NULL; -+ - /* create the pipe through which the main program sends us commands, - then fork our process. */ - if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1) -@@ -186,11 +187,16 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) - struct script_data data; - char *p, *action_str, *hostname = NULL, *domain = NULL; - unsigned char *buf = (unsigned char *)daemon->namebuff; -- unsigned char *end, *extradata, *alloc_buff = NULL; -+ unsigned char *end, *extradata; - int is6, err = 0; - int pipeout[2]; - -- free(alloc_buff); -+ /* Free rarely-allocated memory from previous iteration. */ -+ if (alloc_buff) -+ { -+ free(alloc_buff); -+ alloc_buff = NULL; -+ } - - /* we read zero bytes when pipe closed: this is our signal to exit */ - if (!read_write(pipefd[0], (unsigned char *)&data, sizeof(data), 1)) --- -1.8.3.1 - diff --git a/bugfix-allow-binding-mac-with-ipv6.patch b/bugfix-allow-binding-mac-with-ipv6.patch index 3a1dad2f1a948526de064dedc26629b948dc23e4..77b862fd6142dbbc092a6fcad157a6f9b7a4fe38 100644 --- a/bugfix-allow-binding-mac-with-ipv6.patch +++ b/bugfix-allow-binding-mac-with-ipv6.patch @@ -1,23 +1,20 @@ -From 7d8a6199730a2fd0cd2a7cfa4fdb73b8399f110a Mon Sep 17 00:00:00 2001 -From: Shufeng Cao -Date: Tue, 27 Nov 2018 15:03:59 +0800 -Subject: [PATCH 1/2] allow binding mac address with ipv6 +From 5db7b109af8f6967335806b50d628611be7b9cfd Mon Sep 17 00:00:00 2001 +From: xiaoweiwei +Date: Tue, 28 Jul 2020 15:22:54 +0800 +Subject: [PATCH] bugfix allow binding mac with ipv6 -This change introduces a new option --bind-mac-with-ip6, when this -option is enabled, a client of same mac address will always get the -bound ipv6 address, even when it's duid has been changed. --- src/dnsmasq.c | 1 + - src/dnsmasq.h | 4 +++- + src/dnsmasq.h | 2 ++ src/option.c | 3 +++ - src/rfc3315.c | 34 +++++++++++++++++++++++++++++++++- - 4 files changed, 40 insertions(+), 2 deletions(-) + src/rfc3315.c | 32 +++++++++++++++++++++++++++++++- + 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/dnsmasq.c b/src/dnsmasq.c -index 9f6c020..32a4d22 100644 +index bfad87f..b25e0c9 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c -@@ -243,6 +243,7 @@ int main (int argc, char **argv) +@@ -261,6 +261,7 @@ int main (int argc, char **argv) if (daemon->dhcp6) { daemon->doing_ra = option_bool(OPT_RA); @@ -26,20 +23,18 @@ index 9f6c020..32a4d22 100644 for (context = daemon->dhcp6; context; context = context->next) { diff --git a/src/dnsmasq.h b/src/dnsmasq.h -index 6773b69..8b31d42 100644 +index 4220798..b9054b9 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h -@@ -250,7 +250,8 @@ struct event_desc { - #define OPT_MAC_B64 54 - #define OPT_MAC_HEX 55 - #define OPT_TFTP_APREF_MAC 56 --#define OPT_LAST 57 -+#define OPT_BIND_MAC_IP6 57 -+#define OPT_LAST 58 +@@ -270,6 +270,7 @@ struct event_desc { + #define OPT_SINGLE_PORT 60 + #define OPT_LEASE_RENEW 61 + #define OPT_LAST 62 ++#define OPT_BIND_MAC_IP6 63 - /* extra flags for my_syslog, we use a couple of facilities since they are known - not to occupy the same bits as priorities, no matter how syslog.h is set up. */ -@@ -1000,6 +1001,7 @@ extern struct daemon { + #define OPTION_BITS (sizeof(unsigned int)*8) + #define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) ) +@@ -1051,6 +1052,7 @@ extern struct daemon { int override; int enable_pxe; int doing_ra, doing_dhcp6; @@ -48,60 +43,60 @@ index 6773b69..8b31d42 100644 struct dhcp_netid_list *force_broadcast, *bootp_dynamic; struct hostsfile *dhcp_hosts_file, *dhcp_opts_file, *dynamic_dirs; diff --git a/src/option.c b/src/option.c -index d358d99..919e5a3 100644 +index dbe5f90..f8391d0 100644 --- a/src/option.c +++ b/src/option.c -@@ -160,6 +160,7 @@ struct myoption { - #define LOPT_DHCPTTL 348 - #define LOPT_TFTP_MTU 349 - #define LOPT_REPLY_DELAY 350 -+#define LOPT_BIND_MAC_IP6 351 +@@ -167,6 +167,7 @@ struct myoption { + #define LOPT_IGNORE_CLID 358 + #define LOPT_SINGLE_PORT 359 + #define LOPT_SCRIPT_TIME 360 ++#define LOPT_BIND_MAC_IP6 361 #ifdef HAVE_GETOPT_LONG static const struct option opts[] = -@@ -325,6 +326,7 @@ static const struct myoption opts[] = - { "script-arp", 0, 0, LOPT_SCRIPT_ARP }, - { "dhcp-ttl", 1, 0 , LOPT_DHCPTTL }, - { "dhcp-reply-delay", 1, 0, LOPT_REPLY_DELAY }, -+ { "bind-mac-with-ip6", 0, 0 , LOPT_BIND_MAC_IP6 }, +@@ -339,6 +340,7 @@ static const struct myoption opts[] = + { "dumpfile", 1, 0, LOPT_DUMPFILE }, + { "dumpmask", 1, 0, LOPT_DUMPMASK }, + { "dhcp-ignore-clid", 0, 0, LOPT_IGNORE_CLID }, ++ { "bind-mac-with-ip6",0, 0, LOPT_BIND_MAC_IP6}, { NULL, 0, 0, 0 } }; -@@ -497,6 +499,7 @@ static struct { - { LOPT_IGNORE_ADDR, ARG_DUP, "", gettext_noop("Ignore DNS responses containing ipaddr."), NULL }, - { LOPT_DHCPTTL, ARG_ONE, "", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL }, - { LOPT_REPLY_DELAY, ARG_ONE, "", gettext_noop("Delay DHCP replies for at least number of seconds."), NULL }, +@@ -518,6 +520,7 @@ static struct { + { LOPT_DUMPFILE, ARG_ONE, "", gettext_noop("Path to debug packet dump file"), NULL }, + { LOPT_DUMPMASK, ARG_ONE, "", gettext_noop("Mask which packets to dump"), NULL }, + { LOPT_SCRIPT_TIME, OPT_LEASE_RENEW, NULL, gettext_noop("Call dhcp-script when lease expiry changes."), NULL }, + { LOPT_BIND_MAC_IP6, OPT_BIND_MAC_IP6, NULL, gettext_noop("Bind mac with ipv6 address. This is an experimental feature and it conflicts with rfc3315."), NULL }, { 0, 0, NULL, NULL, NULL } }; diff --git a/src/rfc3315.c b/src/rfc3315.c -index 21fcd9b..defd966 100644 +index b3f0a0a..5781809 100644 --- a/src/rfc3315.c +++ b/src/rfc3315.c -@@ -55,6 +55,7 @@ static struct prefix_class *prefix_class_from_context(struct dhcp_context *conte +@@ -49,6 +49,7 @@ static void end_ia(int t1cntr, unsigned int min_time, int do_fuzz); static void mark_context_used(struct state *state, struct in6_addr *addr); static void mark_config_used(struct dhcp_context *context, struct in6_addr *addr); static int check_address(struct state *state, struct in6_addr *addr); +static int check_and_try_preempte_address(struct state *state, struct in6_addr *addr, time_t now, struct dhcp_config *config); + static int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr, struct state *state, time_t now); + static struct addrlist *config_implies(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr); static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, void *ia_option, - unsigned int *min_time, struct in6_addr *addr, time_t now); - static void update_leases(struct state *state, struct dhcp_context *context, struct in6_addr *addr, unsigned int lease_time, time_t now); -@@ -746,7 +747,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ +@@ -703,7 +704,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + for (c = state->context; c; c = c->current) if (!(c->flags & CONTEXT_CONF_USED) && match_netid(c->filter, solicit_tags, plain_range) && - config_valid(config, c, &addr) && -- check_address(state, &addr)) -+ check_and_try_preempte_address(state, &addr, now, config)) +- config_valid(config, c, &addr, state, now)) ++ config_valid(config, c, &addr, state, now) && ++ check_and_try_preempte_address(state, &addr, now, config)) { mark_config_used(state->context, &addr); if (have_config(config, CONFIG_TIME)) -@@ -1744,6 +1745,37 @@ static int check_address(struct state *state, struct in6_addr *addr) +@@ -1684,6 +1686,34 @@ static int check_address(struct state *state, struct in6_addr *addr) return 1; } -+static int check_and_try_preempte_address(struct state *state, struct in6_addr *addr, time_t now, struct dhcp_config *config) -+{ ++static int check_and_try_preempte_address(struct state *state, struct in6_addr *addr, time_t now, struct dhcp_config *config){ + struct dhcp_lease *lease; + + if (!(lease = lease6_find_by_addr(addr, 128, 0))) @@ -109,8 +104,7 @@ index 21fcd9b..defd966 100644 + return 1; + } + -+ -+ if(daemon->bind_mac_with_ip6) { ++ if (daemon->bind_mac_with_ip6) { + // break rfc3315 here + // bind mac address with a lease + if ((state->mac) && !(config->flags & CONFIG_CLID) && @@ -124,16 +118,15 @@ index 21fcd9b..defd966 100644 + if (lease->clid_len != state->clid_len || + memcmp(lease->clid, state->clid, state->clid_len) != 0 || + lease->iaid != state->iaid) -+ { -+ return 0; -+ } ++ { ++ return 0; ++ } + + return 1; +} -+ - - /* Calculate valid and preferred times to send in leases/renewals. + /* return true of *addr could have been generated from config. */ + static struct addrlist *config_implies(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr) -- -2.19.1 +1.8.3.1 diff --git a/bugfix-deal-with-CONFRIM-when-binding-mac-with-ipv6.patch b/bugfix-deal-with-CONFRIM-when-binding-mac-with-ipv6.patch index 761abbfa8d3592ad8ce6c0953deae16b92240a34..12d85b8c1aef3bfb54a732284c3ed1c60ce32335 100644 --- a/bugfix-deal-with-CONFRIM-when-binding-mac-with-ipv6.patch +++ b/bugfix-deal-with-CONFRIM-when-binding-mac-with-ipv6.patch @@ -1,66 +1,2384 @@ -From c4a283365bdd56e4552e7205fbfba17ca298fc3a Mon Sep 17 00:00:00 2001 -From: Shufeng Cao -Date: Fri, 14 Dec 2018 20:02:31 +0800 -Subject: [PATCH 2/2] binding mac with ipv6: dealing with invalid CONFIRM - package +From 76cc93d2aa547b1247d69b7f20050c1483b891e4 Mon Sep 17 00:00:00 2001 +From: xiaoweiwei +Date: Tue, 28 Jul 2020 16:20:13 +0800 +Subject: [PATCH] bugfix deal with CONFRIM when binding mac with ipv6 --- - src/rfc3315.c | 30 ++++++++++++++++++++++++++++++ - 1 file changed, 30 insertions(+) + ...l-with-CONFRIM-when-binding-mac-with-ipv6.patch | 66 + + src/rfc3315.c | 31 +- + src/rfc3315.c.orig | 2236 ++++++++++++++++++++ + 3 files changed, 2332 insertions(+), 1 deletion(-) + create mode 100644 bugfix-deal-with-CONFRIM-when-binding-mac-with-ipv6.patch + create mode 100644 src/rfc3315.c.orig +diff --git a/bugfix-deal-with-CONFRIM-when-binding-mac-with-ipv6.patch b/bugfix-deal-with-CONFRIM-when-binding-mac-with-ipv6.patch +new file mode 100644 +index 0000000..d474de0 +--- /dev/null ++++ b/bugfix-deal-with-CONFRIM-when-binding-mac-with-ipv6.patch +@@ -0,0 +1,66 @@ ++From 9735f27181d266379c96b1bbf66901b6c83673e7 Mon Sep 17 00:00:00 2001 ++From: xiaoweiwei ++Date: Tue, 28 Jul 2020 11:26:22 +0800 ++Subject: [PATCH] bugfix deal with CONFRIM when binding mac with ipv6 ++ ++--- ++ src/rfc3315.c | 31 ++++++++++++++++++++++++++++++- ++ 1 file changed, 30 insertions(+), 1 deletion(-) ++ ++diff --git a/src/rfc3315.c b/src/rfc3315.c ++index bdf330a..345355c 100644 ++--- a/src/rfc3315.c +++++ b/src/rfc3315.c ++@@ -1030,12 +1030,32 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ ++ ++ case DHCP6CONFIRM: ++ { ++- int good_addr = 0; +++ int good_addr = 0; +++ int find_bind = 0; +++ struct dhcp_config *find_config = NULL; ++ ++ /* set reply message type */ ++ *outmsgtypep = DHCP6REPLY; ++ ++ log6_quiet(state, "DHCPCONFIRM", NULL, NULL); +++ +++ if(daemon->bind_mac_with_ip6) { +++ if(state->mac) { +++ for (find_config = daemon->dhcp_conf; find_config; find_config = find_config->next) +++ if (config_has_mac(find_config, state->mac, state->mac_len, state->mac_type) && have_config(find_config, CONFIG_ADDR6)) { +++ find_bind = 1; +++ break; +++ } +++ } +++ /* requires all mac has binding ipv6 address. */ +++ if (find_bind == 0) { +++ o1 = new_opt6(OPTION6_STATUS_CODE); +++ put_opt6_short(DHCP6NOTONLINK); +++ put_opt6_string(_("confirm failed, no binding found")); +++ end_opt6(o1); +++ return 1; +++ } +++ } ++ ++ for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end)) ++ { ++@@ -1059,6 +1079,15 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ ++ log6_quiet(state, "DHCPREPLY", &req_addr, _("confirm failed")); ++ return 1; ++ } +++ if (daemon->bind_mac_with_ip6) { +++ if (!is_same_net6(req_addr, &find_config->addr6, 128)) { +++ o1 = new_opt6(OPTION6_STATUS_CODE); +++ put_opt6_short(DHCP6NOTONLINK); +++ put_opt6_string(_("confirm failed, not binding to this address")); +++ end_opt6(o1); +++ return 1; +++ } +++ } ++ ++ good_addr = 1; ++ log6_quiet(state, "DHCPREPLY", &req_addr, state->hostname); ++-- ++1.8.3.1 ++ diff --git a/src/rfc3315.c b/src/rfc3315.c -index defd966..f8ba7e4 100644 +index 5781809..c6032d9 100644 --- a/src/rfc3315.c +++ b/src/rfc3315.c -@@ -1087,11 +1087,31 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ +@@ -1032,12 +1032,32 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + case DHCP6CONFIRM: { - int good_addr = 0; -+ int find_bind = 0; -+ struct dhcp_config *find_config = NULL; +- int good_addr = 0; ++ int good_addr = 0; ++ int find_bind = 0; ++ struct dhcp_config *find_config = NULL; /* set reply message type */ *outmsgtypep = DHCP6REPLY; log6_quiet(state, "DHCPCONFIRM", NULL, NULL); + -+ if(daemon->bind_mac_with_ip6) { -+ if(state->mac) { -+ for (find_config = daemon->dhcp_conf; find_config; find_config = find_config->next) -+ if (config_has_mac(find_config, state->mac, state->mac_len, state->mac_type) && have_config(find_config, CONFIG_ADDR6)) { -+ find_bind = 1; -+ break; -+ } -+ } -+ /* requires all mac has binding ipv6 address. */ -+ if (find_bind == 0) { -+ o1 = new_opt6(OPTION6_STATUS_CODE); -+ put_opt6_short(DHCP6NOTONLINK); -+ put_opt6_string(_("confirm failed, no binding found")); -+ end_opt6(o1); -+ return 1; -+ } -+ } ++ if(daemon->bind_mac_with_ip6) { ++ if(state->mac) { ++ for (find_config = daemon->dhcp_conf; find_config; find_config = find_config->next) ++ if (config_has_mac(find_config, state->mac, state->mac_len, state->mac_type) && have_config(find_config, CONFIG_ADDR6)) { ++ find_bind = 1; ++ break; ++ } ++ } ++ /* requires all mac has binding ipv6 address. */ ++ if (find_bind == 0) { ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6NOTONLINK); ++ put_opt6_string(_("confirm failed, no binding found")); ++ end_opt6(o1); ++ return 1; ++ } ++ } for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end)) { -@@ -1112,6 +1132,16 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ +@@ -1061,6 +1081,15 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + log6_quiet(state, "DHCPREPLY", &req_addr, _("confirm failed")); return 1; } ++ if (daemon->bind_mac_with_ip6) { ++ if (!is_same_net6(&req_addr, &find_config->addr6, 128)) { ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6NOTONLINK); ++ put_opt6_string(_("confirm failed, not binding to this address")); ++ end_opt6(o1); ++ return 1; ++ } ++ } -+ if(daemon->bind_mac_with_ip6) { -+ if (!is_same_net6(req_addr, &find_config->addr6, 128)) { + good_addr = 1; + log6_quiet(state, "DHCPREPLY", &req_addr, state->hostname); +diff --git a/src/rfc3315.c.orig b/src/rfc3315.c.orig +new file mode 100644 +index 0000000..5781809 +--- /dev/null ++++ b/src/rfc3315.c.orig +@@ -0,0 +1,2236 @@ ++/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; version 2 dated June, 1991, or ++ (at your option) version 3 dated 29 June, 2007. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++ ++#include "dnsmasq.h" ++ ++#ifdef HAVE_DHCP6 ++ ++struct state { ++ unsigned char *clid; ++ int clid_len, ia_type, interface, hostname_auth, lease_allocate; ++ char *client_hostname, *hostname, *domain, *send_domain; ++ struct dhcp_context *context; ++ struct in6_addr *link_address, *fallback, *ll_addr, *ula_addr; ++ unsigned int xid, fqdn_flags, iaid; ++ char *iface_name; ++ void *packet_options, *end; ++ struct dhcp_netid *tags, *context_tags; ++ unsigned char mac[DHCP_CHADDR_MAX]; ++ unsigned int mac_len, mac_type; ++}; ++ ++static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz, ++ struct in6_addr *client_addr, int is_unicast, time_t now); ++static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_t sz, int is_unicast, time_t now); ++static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_opts); ++static void log6_packet(struct state *state, char *type, struct in6_addr *addr, char *string); ++static void log6_quiet(struct state *state, char *type, struct in6_addr *addr, char *string); ++static void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsize); ++static void *opt6_next(void *opts, void *end); ++static unsigned int opt6_uint(unsigned char *opt, int offset, int size); ++static void get_context_tag(struct state *state, struct dhcp_context *context); ++static int check_ia(struct state *state, void *opt, void **endp, void **ia_option); ++static int build_ia(struct state *state, int *t1cntr); ++static void end_ia(int t1cntr, unsigned int min_time, int do_fuzz); ++static void mark_context_used(struct state *state, struct in6_addr *addr); ++static void mark_config_used(struct dhcp_context *context, struct in6_addr *addr); ++static int check_address(struct state *state, struct in6_addr *addr); ++static int check_and_try_preempte_address(struct state *state, struct in6_addr *addr, time_t now, struct dhcp_config *config); ++static int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr, struct state *state, time_t now); ++static struct addrlist *config_implies(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr); ++static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, void *ia_option, ++ unsigned int *min_time, struct in6_addr *addr, time_t now); ++static void update_leases(struct state *state, struct dhcp_context *context, struct in6_addr *addr, unsigned int lease_time, time_t now); ++static int add_local_addrs(struct dhcp_context *context); ++static struct dhcp_netid *add_options(struct state *state, int do_refresh); ++static void calculate_times(struct dhcp_context *context, unsigned int *min_time, unsigned int *valid_timep, ++ unsigned int *preferred_timep, unsigned int lease_time); ++ ++#define opt6_len(opt) ((int)(opt6_uint(opt, -2, 2))) ++#define opt6_type(opt) (opt6_uint(opt, -4, 2)) ++#define opt6_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[4+(i)])) ++ ++#define opt6_user_vendor_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[2+(i)])) ++#define opt6_user_vendor_len(opt) ((int)(opt6_uint(opt, -4, 2))) ++#define opt6_user_vendor_next(opt, end) (opt6_next(((void *) opt) - 2, end)) ++ ++ ++unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name, ++ struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr, ++ size_t sz, struct in6_addr *client_addr, time_t now) ++{ ++ struct dhcp_vendor *vendor; ++ int msg_type; ++ struct state state; ++ ++ if (sz <= 4) ++ return 0; ++ ++ msg_type = *((unsigned char *)daemon->dhcp_packet.iov_base); ++ ++ /* Mark these so we only match each at most once, to avoid tangled linked lists */ ++ for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next) ++ vendor->netid.next = &vendor->netid; ++ ++ reset_counter(); ++ state.context = context; ++ state.interface = interface; ++ state.iface_name = iface_name; ++ state.fallback = fallback; ++ state.ll_addr = ll_addr; ++ state.ula_addr = ula_addr; ++ state.mac_len = 0; ++ state.tags = NULL; ++ state.link_address = NULL; ++ ++ if (dhcp6_maybe_relay(&state, daemon->dhcp_packet.iov_base, sz, client_addr, ++ IN6_IS_ADDR_MULTICAST(client_addr), now)) ++ return msg_type == DHCP6RELAYFORW ? DHCPV6_SERVER_PORT : DHCPV6_CLIENT_PORT; ++ ++ return 0; ++} ++ ++/* This cost me blood to write, it will probably cost you blood to understand - srk. */ ++static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz, ++ struct in6_addr *client_addr, int is_unicast, time_t now) ++{ ++ void *end = inbuff + sz; ++ void *opts = inbuff + 34; ++ int msg_type = *((unsigned char *)inbuff); ++ unsigned char *outmsgtypep; ++ void *opt; ++ struct dhcp_vendor *vendor; ++ ++ /* if not an encapsulated relayed message, just do the stuff */ ++ if (msg_type != DHCP6RELAYFORW) ++ { ++ /* if link_address != NULL if points to the link address field of the ++ innermost nested RELAYFORW message, which is where we find the ++ address of the network on which we can allocate an address. ++ Recalculate the available contexts using that information. ++ ++ link_address == NULL means there's no relay in use, so we try and find the client's ++ MAC address from the local ND cache. */ ++ ++ if (!state->link_address) ++ get_client_mac(client_addr, state->interface, state->mac, &state->mac_len, &state->mac_type, now); ++ else ++ { ++ struct dhcp_context *c; ++ struct shared_network *share = NULL; ++ state->context = NULL; ++ ++ if (!IN6_IS_ADDR_LOOPBACK(state->link_address) && ++ !IN6_IS_ADDR_LINKLOCAL(state->link_address) && ++ !IN6_IS_ADDR_MULTICAST(state->link_address)) ++ for (c = daemon->dhcp6; c; c = c->next) ++ { ++ for (share = daemon->shared_networks; share; share = share->next) ++ { ++ if (share->shared_addr.s_addr != 0) ++ continue; ++ ++ if (share->if_index != 0 || ++ !IN6_ARE_ADDR_EQUAL(state->link_address, &share->match_addr6)) ++ continue; ++ ++ if ((c->flags & CONTEXT_DHCP) && ++ !(c->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) && ++ is_same_net6(&share->shared_addr6, &c->start6, c->prefix) && ++ is_same_net6(&share->shared_addr6, &c->end6, c->prefix)) ++ break; ++ } ++ ++ if (share || ++ ((c->flags & CONTEXT_DHCP) && ++ !(c->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) && ++ is_same_net6(state->link_address, &c->start6, c->prefix) && ++ is_same_net6(state->link_address, &c->end6, c->prefix))) ++ { ++ c->preferred = c->valid = 0xffffffff; ++ c->current = state->context; ++ state->context = c; ++ } ++ } ++ ++ if (!state->context) ++ { ++ inet_ntop(AF_INET6, state->link_address, daemon->addrbuff, ADDRSTRLEN); ++ my_syslog(MS_DHCP | LOG_WARNING, ++ _("no address range available for DHCPv6 request from relay at %s"), ++ daemon->addrbuff); ++ return 0; ++ } ++ } ++ ++ if (!state->context) ++ { ++ my_syslog(MS_DHCP | LOG_WARNING, ++ _("no address range available for DHCPv6 request via %s"), state->iface_name); ++ return 0; ++ } ++ ++ return dhcp6_no_relay(state, msg_type, inbuff, sz, is_unicast, now); ++ } ++ ++ /* must have at least msg_type+hopcount+link_address+peer_address+minimal size option ++ which is 1 + 1 + 16 + 16 + 2 + 2 = 38 */ ++ if (sz < 38) ++ return 0; ++ ++ /* copy header stuff into reply message and set type to reply */ ++ if (!(outmsgtypep = put_opt6(inbuff, 34))) ++ return 0; ++ *outmsgtypep = DHCP6RELAYREPL; ++ ++ /* look for relay options and set tags if found. */ ++ for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next) ++ { ++ int mopt; ++ ++ if (vendor->match_type == MATCH_SUBSCRIBER) ++ mopt = OPTION6_SUBSCRIBER_ID; ++ else if (vendor->match_type == MATCH_REMOTE) ++ mopt = OPTION6_REMOTE_ID; ++ else ++ continue; ++ ++ if ((opt = opt6_find(opts, end, mopt, 1)) && ++ vendor->len == opt6_len(opt) && ++ memcmp(vendor->data, opt6_ptr(opt, 0), vendor->len) == 0 && ++ vendor->netid.next != &vendor->netid) ++ { ++ vendor->netid.next = state->tags; ++ state->tags = &vendor->netid; ++ break; ++ } ++ } ++ ++ /* RFC-6939 */ ++ if ((opt = opt6_find(opts, end, OPTION6_CLIENT_MAC, 3))) ++ { ++ if (opt6_len(opt) - 2 > DHCP_CHADDR_MAX) { ++ return 0; ++ } ++ state->mac_type = opt6_uint(opt, 0, 2); ++ state->mac_len = opt6_len(opt) - 2; ++ memcpy(&state->mac[0], opt6_ptr(opt, 2), state->mac_len); ++ } ++ ++ for (opt = opts; opt; opt = opt6_next(opt, end)) ++ { ++ if (opt6_ptr(opt, 0) + opt6_len(opt) > end) ++ return 0; ++ ++ /* Don't copy MAC address into reply. */ ++ if (opt6_type(opt) != OPTION6_CLIENT_MAC) ++ { ++ int o = new_opt6(opt6_type(opt)); ++ if (opt6_type(opt) == OPTION6_RELAY_MSG) ++ { ++ struct in6_addr align; ++ /* the packet data is unaligned, copy to aligned storage */ ++ memcpy(&align, inbuff + 2, IN6ADDRSZ); ++ state->link_address = &align; ++ /* zero is_unicast since that is now known to refer to the ++ relayed packet, not the original sent by the client */ ++ if (!dhcp6_maybe_relay(state, opt6_ptr(opt, 0), opt6_len(opt), client_addr, 0, now)) ++ return 0; ++ } ++ else ++ put_opt6(opt6_ptr(opt, 0), opt6_len(opt)); ++ end_opt6(o); ++ } ++ } ++ ++ return 1; ++} ++ ++static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_t sz, int is_unicast, time_t now) ++{ ++ void *opt; ++ int i, o, o1, start_opts; ++ struct dhcp_opt *opt_cfg; ++ struct dhcp_netid *tagif; ++ struct dhcp_config *config = NULL; ++ struct dhcp_netid known_id, iface_id, v6_id; ++ unsigned char *outmsgtypep; ++ struct dhcp_vendor *vendor; ++ struct dhcp_context *context_tmp; ++ struct dhcp_mac *mac_opt; ++ unsigned int ignore = 0; ++ ++ state->packet_options = inbuff + 4; ++ state->end = inbuff + sz; ++ state->clid = NULL; ++ state->clid_len = 0; ++ state->lease_allocate = 0; ++ state->context_tags = NULL; ++ state->domain = NULL; ++ state->send_domain = NULL; ++ state->hostname_auth = 0; ++ state->hostname = NULL; ++ state->client_hostname = NULL; ++ state->fqdn_flags = 0x01; /* default to send if we receive no FQDN option */ ++ ++ /* set tag with name == interface */ ++ iface_id.net = state->iface_name; ++ iface_id.next = state->tags; ++ state->tags = &iface_id; ++ ++ /* set tag "dhcpv6" */ ++ v6_id.net = "dhcpv6"; ++ v6_id.next = state->tags; ++ state->tags = &v6_id; ++ ++ /* copy over transaction-id, and save pointer to message type */ ++ if (!(outmsgtypep = put_opt6(inbuff, 4))) ++ return 0; ++ start_opts = save_counter(-1); ++ state->xid = outmsgtypep[3] | outmsgtypep[2] << 8 | outmsgtypep[1] << 16; ++ ++ /* We're going to be linking tags from all context we use. ++ mark them as unused so we don't link one twice and break the list */ ++ for (context_tmp = state->context; context_tmp; context_tmp = context_tmp->current) ++ { ++ context_tmp->netid.next = &context_tmp->netid; ++ ++ if (option_bool(OPT_LOG_OPTS)) ++ { ++ inet_ntop(AF_INET6, &context_tmp->start6, daemon->dhcp_buff, ADDRSTRLEN); ++ inet_ntop(AF_INET6, &context_tmp->end6, daemon->dhcp_buff2, ADDRSTRLEN); ++ if (context_tmp->flags & (CONTEXT_STATIC)) ++ my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCPv6 subnet: %s/%d"), ++ state->xid, daemon->dhcp_buff, context_tmp->prefix); ++ else ++ my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"), ++ state->xid, daemon->dhcp_buff, daemon->dhcp_buff2); ++ } ++ } ++ ++ if ((opt = opt6_find(state->packet_options, state->end, OPTION6_CLIENT_ID, 1))) ++ { ++ state->clid = opt6_ptr(opt, 0); ++ state->clid_len = opt6_len(opt); ++ o = new_opt6(OPTION6_CLIENT_ID); ++ put_opt6(state->clid, state->clid_len); ++ end_opt6(o); ++ } ++ else if (msg_type != DHCP6IREQ) ++ return 0; ++ ++ /* server-id must match except for SOLICIT, CONFIRM and REBIND messages */ ++ if (msg_type != DHCP6SOLICIT && msg_type != DHCP6CONFIRM && msg_type != DHCP6IREQ && msg_type != DHCP6REBIND && ++ (!(opt = opt6_find(state->packet_options, state->end, OPTION6_SERVER_ID, 1)) || ++ opt6_len(opt) != daemon->duid_len || ++ memcmp(opt6_ptr(opt, 0), daemon->duid, daemon->duid_len) != 0)) ++ return 0; ++ ++ o = new_opt6(OPTION6_SERVER_ID); ++ put_opt6(daemon->duid, daemon->duid_len); ++ end_opt6(o); ++ ++ if (is_unicast && ++ (msg_type == DHCP6REQUEST || msg_type == DHCP6RENEW || msg_type == DHCP6RELEASE || msg_type == DHCP6DECLINE)) ++ ++ { ++ *outmsgtypep = DHCP6REPLY; ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6USEMULTI); ++ put_opt6_string("Use multicast"); ++ end_opt6(o1); ++ return 1; ++ } ++ ++ /* match vendor and user class options */ ++ for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next) ++ { ++ int mopt; ++ ++ if (vendor->match_type == MATCH_VENDOR) ++ mopt = OPTION6_VENDOR_CLASS; ++ else if (vendor->match_type == MATCH_USER) ++ mopt = OPTION6_USER_CLASS; ++ else ++ continue; ++ ++ if ((opt = opt6_find(state->packet_options, state->end, mopt, 2))) ++ { ++ void *enc_opt, *enc_end = opt6_ptr(opt, opt6_len(opt)); ++ int offset = 0; ++ ++ if (mopt == OPTION6_VENDOR_CLASS) ++ { ++ if (opt6_len(opt) < 4) ++ continue; ++ ++ if (vendor->enterprise != opt6_uint(opt, 0, 4)) ++ continue; ++ ++ offset = 4; ++ } ++ ++ /* Note that format if user/vendor classes is different to DHCP options - no option types. */ ++ for (enc_opt = opt6_ptr(opt, offset); enc_opt; enc_opt = opt6_user_vendor_next(enc_opt, enc_end)) ++ for (i = 0; i <= (opt6_user_vendor_len(enc_opt) - vendor->len); i++) ++ if (memcmp(vendor->data, opt6_user_vendor_ptr(enc_opt, i), vendor->len) == 0) ++ { ++ vendor->netid.next = state->tags; ++ state->tags = &vendor->netid; ++ break; ++ } ++ } ++ } ++ ++ if (option_bool(OPT_LOG_OPTS) && (opt = opt6_find(state->packet_options, state->end, OPTION6_VENDOR_CLASS, 4))) ++ my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %u"), state->xid, opt6_uint(opt, 0, 4)); ++ ++ /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match. ++ Otherwise assume the option is an array, and look for a matching element. ++ If no data given, existence of the option is enough. This code handles ++ V-I opts too. */ ++ for (opt_cfg = daemon->dhcp_match6; opt_cfg; opt_cfg = opt_cfg->next) ++ { ++ int match = 0; ++ ++ if (opt_cfg->flags & DHOPT_RFC3925) ++ { ++ for (opt = opt6_find(state->packet_options, state->end, OPTION6_VENDOR_OPTS, 4); ++ opt; ++ opt = opt6_find(opt6_next(opt, state->end), state->end, OPTION6_VENDOR_OPTS, 4)) ++ { ++ void *vopt; ++ void *vend = opt6_ptr(opt, opt6_len(opt)); ++ ++ for (vopt = opt6_find(opt6_ptr(opt, 4), vend, opt_cfg->opt, 0); ++ vopt; ++ vopt = opt6_find(opt6_next(vopt, vend), vend, opt_cfg->opt, 0)) ++ if ((match = match_bytes(opt_cfg, opt6_ptr(vopt, 0), opt6_len(vopt)))) ++ break; ++ } ++ if (match) ++ break; ++ } ++ else ++ { ++ if (!(opt = opt6_find(state->packet_options, state->end, opt_cfg->opt, 1))) ++ continue; ++ ++ match = match_bytes(opt_cfg, opt6_ptr(opt, 0), opt6_len(opt)); ++ } ++ ++ if (match) ++ { ++ opt_cfg->netid->next = state->tags; ++ state->tags = opt_cfg->netid; ++ } ++ } ++ ++ if (state->mac_len != 0) ++ { ++ if (option_bool(OPT_LOG_OPTS)) ++ { ++ print_mac(daemon->dhcp_buff, state->mac, state->mac_len); ++ my_syslog(MS_DHCP | LOG_INFO, _("%u client MAC address: %s"), state->xid, daemon->dhcp_buff); ++ } ++ ++ for (mac_opt = daemon->dhcp_macs; mac_opt; mac_opt = mac_opt->next) ++ if ((unsigned)mac_opt->hwaddr_len == state->mac_len && ++ ((unsigned)mac_opt->hwaddr_type == state->mac_type || mac_opt->hwaddr_type == 0) && ++ memcmp_masked(mac_opt->hwaddr, state->mac, state->mac_len, mac_opt->mask)) ++ { ++ mac_opt->netid.next = state->tags; ++ state->tags = &mac_opt->netid; ++ } ++ } ++ ++ if ((opt = opt6_find(state->packet_options, state->end, OPTION6_FQDN, 1))) ++ { ++ /* RFC4704 refers */ ++ int len = opt6_len(opt) - 1; ++ ++ state->fqdn_flags = opt6_uint(opt, 0, 1); ++ ++ /* Always force update, since the client has no way to do it itself. */ ++ if (!option_bool(OPT_FQDN_UPDATE) && !(state->fqdn_flags & 0x01)) ++ state->fqdn_flags |= 0x03; ++ ++ state->fqdn_flags &= ~0x04; ++ ++ if (len != 0 && len < 255) ++ { ++ unsigned char *pp, *op = opt6_ptr(opt, 1); ++ char *pq = daemon->dhcp_buff; ++ ++ pp = op; ++ while (*op != 0 && ((op + (*op)) - pp) < len) ++ { ++ memcpy(pq, op+1, *op); ++ pq += *op; ++ op += (*op)+1; ++ *(pq++) = '.'; ++ } ++ ++ if (pq != daemon->dhcp_buff) ++ pq--; ++ *pq = 0; ++ ++ if (legal_hostname(daemon->dhcp_buff)) ++ { ++ struct dhcp_match_name *m; ++ size_t nl = strlen(daemon->dhcp_buff); ++ ++ state->client_hostname = daemon->dhcp_buff; ++ ++ if (option_bool(OPT_LOG_OPTS)) ++ my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), state->xid, state->client_hostname); ++ ++ for (m = daemon->dhcp_name_match; m; m = m->next) ++ { ++ size_t ml = strlen(m->name); ++ char save = 0; ++ ++ if (nl < ml) ++ continue; ++ if (nl > ml) ++ { ++ save = state->client_hostname[ml]; ++ state->client_hostname[ml] = 0; ++ } ++ ++ if (hostname_isequal(state->client_hostname, m->name) && ++ (save == 0 || m->wildcard)) ++ { ++ m->netid->next = state->tags; ++ state->tags = m->netid; ++ } ++ ++ if (save != 0) ++ state->client_hostname[ml] = save; ++ } ++ } ++ } ++ } ++ ++ if (state->clid && ++ (config = find_config(daemon->dhcp_conf, state->context, state->clid, state->clid_len, ++ state->mac, state->mac_len, state->mac_type, NULL, run_tag_if(state->tags))) && ++ have_config(config, CONFIG_NAME)) ++ { ++ state->hostname = config->hostname; ++ state->domain = config->domain; ++ state->hostname_auth = 1; ++ } ++ else if (state->client_hostname) ++ { ++ state->domain = strip_hostname(state->client_hostname); ++ ++ if (strlen(state->client_hostname) != 0) ++ { ++ state->hostname = state->client_hostname; ++ ++ if (!config) ++ { ++ /* Search again now we have a hostname. ++ Only accept configs without CLID here, (it won't match) ++ to avoid impersonation by name. */ ++ struct dhcp_config *new = find_config(daemon->dhcp_conf, state->context, NULL, 0, NULL, 0, 0, state->hostname, run_tag_if(state->tags)); ++ if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr) ++ config = new; ++ } ++ } ++ } ++ ++ if (config) ++ { ++ struct dhcp_netid_list *list; ++ ++ for (list = config->netid; list; list = list->next) ++ { ++ list->list->next = state->tags; ++ state->tags = list->list; ++ } ++ ++ /* set "known" tag for known hosts */ ++ known_id.net = "known"; ++ known_id.next = state->tags; ++ state->tags = &known_id; ++ ++ if (have_config(config, CONFIG_DISABLE)) ++ ignore = 1; ++ } ++ else if (state->clid && ++ find_config(daemon->dhcp_conf, NULL, state->clid, state->clid_len, ++ state->mac, state->mac_len, state->mac_type, NULL, run_tag_if(state->tags))) ++ { ++ known_id.net = "known-othernet"; ++ known_id.next = state->tags; ++ state->tags = &known_id; ++ } ++ ++ tagif = run_tag_if(state->tags); ++ ++ /* if all the netids in the ignore list are present, ignore this client */ ++ if (daemon->dhcp_ignore) ++ { ++ struct dhcp_netid_list *id_list; ++ ++ for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next) ++ if (match_netid(id_list->list, tagif, 0)) ++ ignore = 1; ++ } ++ ++ /* if all the netids in the ignore_name list are present, ignore client-supplied name */ ++ if (!state->hostname_auth) ++ { ++ struct dhcp_netid_list *id_list; ++ ++ for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next) ++ if ((!id_list->list) || match_netid(id_list->list, tagif, 0)) ++ break; ++ if (id_list) ++ state->hostname = NULL; ++ } ++ ++ ++ switch (msg_type) ++ { ++ default: ++ return 0; ++ ++ ++ case DHCP6SOLICIT: ++ { ++ int address_assigned = 0; ++ /* tags without all prefix-class tags */ ++ struct dhcp_netid *solicit_tags; ++ struct dhcp_context *c; ++ ++ *outmsgtypep = DHCP6ADVERTISE; ++ ++ if (opt6_find(state->packet_options, state->end, OPTION6_RAPID_COMMIT, 0)) ++ { ++ *outmsgtypep = DHCP6REPLY; ++ state->lease_allocate = 1; ++ o = new_opt6(OPTION6_RAPID_COMMIT); ++ end_opt6(o); ++ } ++ ++ log6_quiet(state, "DHCPSOLICIT", NULL, ignore ? _("ignored") : NULL); ++ ++ request_no_address: ++ solicit_tags = tagif; ++ ++ if (ignore) ++ return 0; ++ ++ /* reset USED bits in leases */ ++ lease6_reset(); ++ ++ /* Can use configured address max once per prefix */ ++ for (c = state->context; c; c = c->current) ++ c->flags &= ~CONTEXT_CONF_USED; ++ ++ for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end)) ++ { ++ void *ia_option, *ia_end; ++ unsigned int min_time = 0xffffffff; ++ int t1cntr; ++ int ia_counter; ++ /* set unless we're sending a particular prefix-class, when we ++ want only dhcp-ranges with the correct tags set and not those without any tags. */ ++ int plain_range = 1; ++ u32 lease_time; ++ struct dhcp_lease *ltmp; ++ struct in6_addr req_addr, addr; ++ ++ if (!check_ia(state, opt, &ia_end, &ia_option)) ++ continue; ++ ++ /* reset USED bits in contexts - one address per prefix per IAID */ ++ for (c = state->context; c; c = c->current) ++ c->flags &= ~CONTEXT_USED; ++ ++ o = build_ia(state, &t1cntr); ++ if (address_assigned) ++ address_assigned = 2; ++ ++ for (ia_counter = 0; ia_option; ia_counter++, ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) ++ { ++ /* worry about alignment here. */ ++ memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); ++ ++ if ((c = address6_valid(state->context, &req_addr, solicit_tags, plain_range))) ++ { ++ lease_time = c->lease_time; ++ /* If the client asks for an address on the same network as a configured address, ++ offer the configured address instead, to make moving to newly-configured ++ addresses automatic. */ ++ if (!(c->flags & CONTEXT_CONF_USED) && config_valid(config, c, &addr, state, now)) ++ { ++ req_addr = addr; ++ mark_config_used(c, &addr); ++ if (have_config(config, CONFIG_TIME)) ++ lease_time = config->lease_time; ++ } ++ else if (!(c = address6_available(state->context, &req_addr, solicit_tags, plain_range))) ++ continue; /* not an address we're allowed */ ++ else if (!check_address(state, &req_addr)) ++ continue; /* address leased elsewhere */ ++ ++ /* add address to output packet */ ++ add_address(state, c, lease_time, ia_option, &min_time, &req_addr, now); ++ mark_context_used(state, &req_addr); ++ get_context_tag(state, c); ++ address_assigned = 1; ++ } ++ } ++ ++ /* Suggest configured address(es) */ ++ for (c = state->context; c; c = c->current) ++ if (!(c->flags & CONTEXT_CONF_USED) && ++ match_netid(c->filter, solicit_tags, plain_range) && ++ config_valid(config, c, &addr, state, now) && ++ check_and_try_preempte_address(state, &addr, now, config)) ++ { ++ mark_config_used(state->context, &addr); ++ if (have_config(config, CONFIG_TIME)) ++ lease_time = config->lease_time; ++ else ++ lease_time = c->lease_time; ++ ++ /* add address to output packet */ ++ add_address(state, c, lease_time, NULL, &min_time, &addr, now); ++ mark_context_used(state, &addr); ++ get_context_tag(state, c); ++ address_assigned = 1; ++ } ++ ++ /* return addresses for existing leases */ ++ ltmp = NULL; ++ while ((ltmp = lease6_find_by_client(ltmp, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, state->clid, state->clid_len, state->iaid))) ++ { ++ req_addr = ltmp->addr6; ++ if ((c = address6_available(state->context, &req_addr, solicit_tags, plain_range))) ++ { ++ add_address(state, c, c->lease_time, NULL, &min_time, &req_addr, now); ++ mark_context_used(state, &req_addr); ++ get_context_tag(state, c); ++ address_assigned = 1; ++ } ++ } ++ ++ /* Return addresses for all valid contexts which don't yet have one */ ++ while ((c = address6_allocate(state->context, state->clid, state->clid_len, state->ia_type == OPTION6_IA_TA, ++ state->iaid, ia_counter, solicit_tags, plain_range, &addr))) ++ { ++ add_address(state, c, c->lease_time, NULL, &min_time, &addr, now); ++ mark_context_used(state, &addr); ++ get_context_tag(state, c); ++ address_assigned = 1; ++ } ++ ++ if (address_assigned != 1) ++ { ++ /* If the server will not assign any addresses to any IAs in a ++ subsequent Request from the client, the server MUST send an Advertise ++ message to the client that doesn't include any IA options. */ ++ if (!state->lease_allocate) ++ { ++ save_counter(o); ++ continue; ++ } ++ ++ /* If the server cannot assign any addresses to an IA in the message ++ from the client, the server MUST include the IA in the Reply message ++ with no addresses in the IA and a Status Code option in the IA ++ containing status code NoAddrsAvail. */ ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6NOADDRS); ++ put_opt6_string(_("address unavailable")); ++ end_opt6(o1); ++ } ++ ++ end_ia(t1cntr, min_time, 0); ++ end_opt6(o); ++ } ++ ++ if (address_assigned) ++ { ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6SUCCESS); ++ put_opt6_string(_("success")); ++ end_opt6(o1); ++ ++ /* If --dhcp-authoritative is set, we can tell client not to wait for ++ other possible servers */ ++ o = new_opt6(OPTION6_PREFERENCE); ++ put_opt6_char(option_bool(OPT_AUTHORITATIVE) ? 255 : 0); ++ end_opt6(o); ++ tagif = add_options(state, 0); ++ } ++ else ++ { ++ /* no address, return error */ ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6NOADDRS); ++ put_opt6_string(_("no addresses available")); ++ end_opt6(o1); ++ ++ /* Some clients will ask repeatedly when we're not giving ++ out addresses because we're in stateless mode. Avoid spamming ++ the log in that case. */ ++ for (c = state->context; c; c = c->current) ++ if (!(c->flags & CONTEXT_RA_STATELESS)) ++ { ++ log6_packet(state, state->lease_allocate ? "DHCPREPLY" : "DHCPADVERTISE", NULL, _("no addresses available")); ++ break; ++ } ++ } ++ ++ break; ++ } ++ ++ case DHCP6REQUEST: ++ { ++ int address_assigned = 0; ++ int start = save_counter(-1); ++ ++ /* set reply message type */ ++ *outmsgtypep = DHCP6REPLY; ++ state->lease_allocate = 1; ++ ++ log6_quiet(state, "DHCPREQUEST", NULL, ignore ? _("ignored") : NULL); ++ ++ if (ignore) ++ return 0; ++ ++ for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end)) ++ { ++ void *ia_option, *ia_end; ++ unsigned int min_time = 0xffffffff; ++ int t1cntr; ++ ++ if (!check_ia(state, opt, &ia_end, &ia_option)) ++ continue; ++ ++ if (!ia_option) ++ { ++ /* If we get a request with an IA_*A without addresses, treat it exactly like ++ a SOLICT with rapid commit set. */ ++ save_counter(start); ++ goto request_no_address; ++ } ++ ++ o = build_ia(state, &t1cntr); ++ ++ for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) ++ { ++ struct in6_addr req_addr; ++ struct dhcp_context *dynamic, *c; ++ unsigned int lease_time; ++ int config_ok = 0; ++ ++ /* align. */ ++ memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); ++ ++ if ((c = address6_valid(state->context, &req_addr, tagif, 1))) ++ config_ok = (config_implies(config, c, &req_addr) != NULL); ++ ++ if ((dynamic = address6_available(state->context, &req_addr, tagif, 1)) || c) ++ { ++ if (!dynamic && !config_ok) ++ { ++ /* Static range, not configured. */ ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6NOADDRS); ++ put_opt6_string(_("address unavailable")); ++ end_opt6(o1); ++ } ++ else if (!check_address(state, &req_addr)) ++ { ++ /* Address leased to another DUID/IAID */ ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6UNSPEC); ++ put_opt6_string(_("address in use")); ++ end_opt6(o1); ++ } ++ else ++ { ++ if (!dynamic) ++ dynamic = c; ++ ++ lease_time = dynamic->lease_time; ++ ++ if (config_ok && have_config(config, CONFIG_TIME)) ++ lease_time = config->lease_time; ++ ++ add_address(state, dynamic, lease_time, ia_option, &min_time, &req_addr, now); ++ get_context_tag(state, dynamic); ++ address_assigned = 1; ++ } ++ } ++ else ++ { ++ /* requested address not on the correct link */ ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6NOTONLINK); ++ put_opt6_string(_("not on link")); ++ end_opt6(o1); ++ } ++ } ++ ++ end_ia(t1cntr, min_time, 0); ++ end_opt6(o); ++ } ++ ++ if (address_assigned) ++ { ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6SUCCESS); ++ put_opt6_string(_("success")); ++ end_opt6(o1); ++ } ++ else ++ { ++ /* no address, return error */ ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6NOADDRS); ++ put_opt6_string(_("no addresses available")); ++ end_opt6(o1); ++ log6_packet(state, "DHCPREPLY", NULL, _("no addresses available")); ++ } ++ ++ tagif = add_options(state, 0); ++ break; ++ } ++ ++ ++ case DHCP6RENEW: ++ { ++ /* set reply message type */ ++ *outmsgtypep = DHCP6REPLY; ++ ++ log6_quiet(state, "DHCPRENEW", NULL, NULL); ++ ++ for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end)) ++ { ++ void *ia_option, *ia_end; ++ unsigned int min_time = 0xffffffff; ++ int t1cntr, iacntr; ++ ++ if (!check_ia(state, opt, &ia_end, &ia_option)) ++ continue; ++ ++ o = build_ia(state, &t1cntr); ++ iacntr = save_counter(-1); ++ ++ for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) ++ { ++ struct dhcp_lease *lease = NULL; ++ struct in6_addr req_addr; ++ unsigned int preferred_time = opt6_uint(ia_option, 16, 4); ++ unsigned int valid_time = opt6_uint(ia_option, 20, 4); ++ char *message = NULL; ++ struct dhcp_context *this_context; ++ ++ memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); ++ ++ if (!(lease = lease6_find(state->clid, state->clid_len, ++ state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, ++ state->iaid, &req_addr))) ++ { ++ /* If the server cannot find a client entry for the IA the server ++ returns the IA containing no addresses with a Status Code option set ++ to NoBinding in the Reply message. */ ++ save_counter(iacntr); ++ t1cntr = 0; ++ ++ log6_packet(state, "DHCPREPLY", &req_addr, _("lease not found")); ++ ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6NOBINDING); ++ put_opt6_string(_("no binding found")); ++ end_opt6(o1); ++ ++ preferred_time = valid_time = 0; ++ break; ++ } ++ ++ ++ if ((this_context = address6_available(state->context, &req_addr, tagif, 1)) || ++ (this_context = address6_valid(state->context, &req_addr, tagif, 1))) ++ { ++ unsigned int lease_time; ++ ++ get_context_tag(state, this_context); ++ ++ if (config_implies(config, this_context, &req_addr) && have_config(config, CONFIG_TIME)) ++ lease_time = config->lease_time; ++ else ++ lease_time = this_context->lease_time; ++ ++ calculate_times(this_context, &min_time, &valid_time, &preferred_time, lease_time); ++ ++ lease_set_expires(lease, valid_time, now); ++ /* Update MAC record in case it's new information. */ ++ if (state->mac_len != 0) ++ lease_set_hwaddr(lease, state->mac, state->clid, state->mac_len, state->mac_type, state->clid_len, now, 0); ++ if (state->ia_type == OPTION6_IA_NA && state->hostname) ++ { ++ char *addr_domain = get_domain6(&req_addr); ++ if (!state->send_domain) ++ state->send_domain = addr_domain; ++ lease_set_hostname(lease, state->hostname, state->hostname_auth, addr_domain, state->domain); ++ message = state->hostname; ++ } ++ ++ ++ if (preferred_time == 0) ++ message = _("deprecated"); ++ } ++ else ++ { ++ preferred_time = valid_time = 0; ++ message = _("address invalid"); ++ } ++ ++ if (message && (message != state->hostname)) ++ log6_packet(state, "DHCPREPLY", &req_addr, message); ++ else ++ log6_quiet(state, "DHCPREPLY", &req_addr, message); ++ ++ o1 = new_opt6(OPTION6_IAADDR); ++ put_opt6(&req_addr, sizeof(req_addr)); ++ put_opt6_long(preferred_time); ++ put_opt6_long(valid_time); ++ end_opt6(o1); ++ } ++ ++ end_ia(t1cntr, min_time, 1); ++ end_opt6(o); ++ } ++ ++ tagif = add_options(state, 0); ++ break; ++ ++ } ++ ++ case DHCP6CONFIRM: ++ { ++ int good_addr = 0; ++ ++ /* set reply message type */ ++ *outmsgtypep = DHCP6REPLY; ++ ++ log6_quiet(state, "DHCPCONFIRM", NULL, NULL); ++ ++ for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end)) ++ { ++ void *ia_option, *ia_end; ++ ++ for (check_ia(state, opt, &ia_end, &ia_option); ++ ia_option; ++ ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) ++ { ++ struct in6_addr req_addr; ++ ++ /* alignment */ ++ memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); ++ ++ if (!address6_valid(state->context, &req_addr, tagif, 1)) ++ { + o1 = new_opt6(OPTION6_STATUS_CODE); + put_opt6_short(DHCP6NOTONLINK); -+ put_opt6_string(_("confirm failed, not binding to this address")); ++ put_opt6_string(_("confirm failed")); + end_opt6(o1); ++ log6_quiet(state, "DHCPREPLY", &req_addr, _("confirm failed")); + return 1; + } ++ ++ good_addr = 1; ++ log6_quiet(state, "DHCPREPLY", &req_addr, state->hostname); ++ } ++ } ++ ++ /* No addresses, no reply: RFC 3315 18.2.2 */ ++ if (!good_addr) ++ return 0; ++ ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6SUCCESS ); ++ put_opt6_string(_("all addresses still on link")); ++ end_opt6(o1); ++ break; ++ } ++ ++ case DHCP6IREQ: ++ { ++ /* We can't discriminate contexts based on address, as we don't know it. ++ If there is only one possible context, we can use its tags */ ++ if (state->context && state->context->netid.net && !state->context->current) ++ { ++ state->context->netid.next = NULL; ++ state->context_tags = &state->context->netid; ++ } ++ ++ /* Similarly, we can't determine domain from address, but if the FQDN is ++ given in --dhcp-host, we can use that, and failing that we can use the ++ unqualified configured domain, if any. */ ++ if (state->hostname_auth) ++ state->send_domain = state->domain; ++ else ++ state->send_domain = get_domain6(NULL); ++ ++ log6_quiet(state, "DHCPINFORMATION-REQUEST", NULL, ignore ? _("ignored") : state->hostname); ++ if (ignore) ++ return 0; ++ *outmsgtypep = DHCP6REPLY; ++ tagif = add_options(state, 1); ++ break; ++ } ++ ++ ++ case DHCP6RELEASE: ++ { ++ /* set reply message type */ ++ *outmsgtypep = DHCP6REPLY; ++ ++ log6_quiet(state, "DHCPRELEASE", NULL, NULL); ++ ++ for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end)) ++ { ++ void *ia_option, *ia_end; ++ int made_ia = 0; ++ ++ for (check_ia(state, opt, &ia_end, &ia_option); ++ ia_option; ++ ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) ++ { ++ struct dhcp_lease *lease; ++ struct in6_addr addr; ++ ++ /* align */ ++ memcpy(&addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); ++ if ((lease = lease6_find(state->clid, state->clid_len, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, ++ state->iaid, &addr))) ++ lease_prune(lease, now); ++ else ++ { ++ if (!made_ia) ++ { ++ o = new_opt6(state->ia_type); ++ put_opt6_long(state->iaid); ++ if (state->ia_type == OPTION6_IA_NA) ++ { ++ put_opt6_long(0); ++ put_opt6_long(0); ++ } ++ made_ia = 1; ++ } ++ ++ o1 = new_opt6(OPTION6_IAADDR); ++ put_opt6(&addr, IN6ADDRSZ); ++ put_opt6_long(0); ++ put_opt6_long(0); ++ end_opt6(o1); ++ } ++ } ++ ++ if (made_ia) ++ { ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6NOBINDING); ++ put_opt6_string(_("no binding found")); ++ end_opt6(o1); ++ ++ end_opt6(o); ++ } ++ } ++ ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6SUCCESS); ++ put_opt6_string(_("release received")); ++ end_opt6(o1); ++ ++ break; ++ } ++ ++ case DHCP6DECLINE: ++ { ++ /* set reply message type */ ++ *outmsgtypep = DHCP6REPLY; ++ ++ log6_quiet(state, "DHCPDECLINE", NULL, NULL); ++ ++ for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end)) ++ { ++ void *ia_option, *ia_end; ++ int made_ia = 0; ++ ++ for (check_ia(state, opt, &ia_end, &ia_option); ++ ia_option; ++ ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) ++ { ++ struct dhcp_lease *lease; ++ struct in6_addr addr; ++ struct addrlist *addr_list; ++ ++ /* align */ ++ memcpy(&addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); ++ ++ if ((addr_list = config_implies(config, state->context, &addr))) ++ { ++ prettyprint_time(daemon->dhcp_buff3, DECLINE_BACKOFF); ++ inet_ntop(AF_INET6, &addr, daemon->addrbuff, ADDRSTRLEN); ++ my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"), ++ daemon->addrbuff, daemon->dhcp_buff3); ++ addr_list->flags |= ADDRLIST_DECLINED; ++ addr_list->decline_time = now; ++ } ++ else ++ /* make sure this host gets a different address next time. */ ++ for (context_tmp = state->context; context_tmp; context_tmp = context_tmp->current) ++ context_tmp->addr_epoch++; ++ ++ if ((lease = lease6_find(state->clid, state->clid_len, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, ++ state->iaid, &addr))) ++ lease_prune(lease, now); ++ else ++ { ++ if (!made_ia) ++ { ++ o = new_opt6(state->ia_type); ++ put_opt6_long(state->iaid); ++ if (state->ia_type == OPTION6_IA_NA) ++ { ++ put_opt6_long(0); ++ put_opt6_long(0); ++ } ++ made_ia = 1; ++ } ++ ++ o1 = new_opt6(OPTION6_IAADDR); ++ put_opt6(&addr, IN6ADDRSZ); ++ put_opt6_long(0); ++ put_opt6_long(0); ++ end_opt6(o1); ++ } ++ } ++ ++ if (made_ia) ++ { ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6NOBINDING); ++ put_opt6_string(_("no binding found")); ++ end_opt6(o1); ++ ++ end_opt6(o); ++ } ++ ++ } ++ ++ /* We must answer with 'success' in global section anyway */ ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6SUCCESS); ++ put_opt6_string(_("success")); ++ end_opt6(o1); ++ break; ++ } ++ ++ } ++ ++ log_tags(tagif, state->xid); ++ log6_opts(0, state->xid, daemon->outpacket.iov_base + start_opts, daemon->outpacket.iov_base + save_counter(-1)); ++ ++ return 1; ++ ++} ++ ++static struct dhcp_netid *add_options(struct state *state, int do_refresh) ++{ ++ void *oro; ++ /* filter options based on tags, those we want get DHOPT_TAGOK bit set */ ++ struct dhcp_netid *tagif = option_filter(state->tags, state->context_tags, daemon->dhcp_opts6); ++ struct dhcp_opt *opt_cfg; ++ int done_dns = 0, done_refresh = !do_refresh, do_encap = 0; ++ int i, o, o1; ++ ++ oro = opt6_find(state->packet_options, state->end, OPTION6_ORO, 0); ++ ++ for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next) ++ { ++ /* netids match and not encapsulated? */ ++ if (!(opt_cfg->flags & DHOPT_TAGOK)) ++ continue; ++ ++ if (!(opt_cfg->flags & DHOPT_FORCE) && oro) ++ { ++ for (i = 0; i < opt6_len(oro) - 1; i += 2) ++ if (opt6_uint(oro, i, 2) == (unsigned)opt_cfg->opt) ++ break; ++ ++ /* option not requested */ ++ if (i >= opt6_len(oro) - 1) ++ continue; ++ } ++ ++ if (opt_cfg->opt == OPTION6_REFRESH_TIME) ++ done_refresh = 1; ++ ++ if (opt_cfg->opt == OPTION6_DNS_SERVER) ++ done_dns = 1; ++ ++ if (opt_cfg->flags & DHOPT_ADDR6) ++ { ++ int len, j; ++ struct in6_addr *a; ++ ++ for (a = (struct in6_addr *)opt_cfg->val, len = opt_cfg->len, j = 0; ++ j < opt_cfg->len; j += IN6ADDRSZ, a++) ++ if ((IN6_IS_ADDR_ULA_ZERO(a) && IN6_IS_ADDR_UNSPECIFIED(state->ula_addr)) || ++ (IN6_IS_ADDR_LINK_LOCAL_ZERO(a) && IN6_IS_ADDR_UNSPECIFIED(state->ll_addr))) ++ len -= IN6ADDRSZ; ++ ++ if (len != 0) ++ { ++ ++ o = new_opt6(opt_cfg->opt); ++ ++ for (a = (struct in6_addr *)opt_cfg->val, j = 0; j < opt_cfg->len; j+=IN6ADDRSZ, a++) ++ { ++ struct in6_addr *p = NULL; ++ ++ if (IN6_IS_ADDR_UNSPECIFIED(a)) ++ { ++ if (!add_local_addrs(state->context)) ++ p = state->fallback; ++ } ++ else if (IN6_IS_ADDR_ULA_ZERO(a)) ++ { ++ if (!IN6_IS_ADDR_UNSPECIFIED(state->ula_addr)) ++ p = state->ula_addr; ++ } ++ else if (IN6_IS_ADDR_LINK_LOCAL_ZERO(a)) ++ { ++ if (!IN6_IS_ADDR_UNSPECIFIED(state->ll_addr)) ++ p = state->ll_addr; ++ } ++ else ++ p = a; ++ ++ if (!p) ++ continue; ++ else if (opt_cfg->opt == OPTION6_NTP_SERVER) ++ { ++ if (IN6_IS_ADDR_MULTICAST(p)) ++ o1 = new_opt6(NTP_SUBOPTION_MC_ADDR); ++ else ++ o1 = new_opt6(NTP_SUBOPTION_SRV_ADDR); ++ put_opt6(p, IN6ADDRSZ); ++ end_opt6(o1); ++ } ++ else ++ put_opt6(p, IN6ADDRSZ); + } + - good_addr = 1; - log6_quiet(state, "DHCPREPLY", req_addr, state->hostname); - } ++ end_opt6(o); ++ } ++ } ++ else ++ { ++ o = new_opt6(opt_cfg->opt); ++ if (opt_cfg->val) ++ put_opt6(opt_cfg->val, opt_cfg->len); ++ end_opt6(o); ++ } ++ } ++ ++ if (daemon->port == NAMESERVER_PORT && !done_dns) ++ { ++ o = new_opt6(OPTION6_DNS_SERVER); ++ if (!add_local_addrs(state->context)) ++ put_opt6(state->fallback, IN6ADDRSZ); ++ end_opt6(o); ++ } ++ ++ if (state->context && !done_refresh) ++ { ++ struct dhcp_context *c; ++ unsigned int lease_time = 0xffffffff; ++ ++ /* Find the smallest lease tie of all contexts, ++ subject to the RFC-4242 stipulation that this must not ++ be less than 600. */ ++ for (c = state->context; c; c = c->next) ++ if (c->lease_time < lease_time) ++ { ++ if (c->lease_time < 600) ++ lease_time = 600; ++ else ++ lease_time = c->lease_time; ++ } ++ ++ o = new_opt6(OPTION6_REFRESH_TIME); ++ put_opt6_long(lease_time); ++ end_opt6(o); ++ } ++ ++ /* handle vendor-identifying vendor-encapsulated options, ++ dhcp-option = vi-encap:13,17,....... */ ++ for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next) ++ opt_cfg->flags &= ~DHOPT_ENCAP_DONE; ++ ++ if (oro) ++ for (i = 0; i < opt6_len(oro) - 1; i += 2) ++ if (opt6_uint(oro, i, 2) == OPTION6_VENDOR_OPTS) ++ do_encap = 1; ++ ++ for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next) ++ { ++ if (opt_cfg->flags & DHOPT_RFC3925) ++ { ++ int found = 0; ++ struct dhcp_opt *oc; ++ ++ if (opt_cfg->flags & DHOPT_ENCAP_DONE) ++ continue; ++ ++ for (oc = daemon->dhcp_opts6; oc; oc = oc->next) ++ { ++ oc->flags &= ~DHOPT_ENCAP_MATCH; ++ ++ if (!(oc->flags & DHOPT_RFC3925) || opt_cfg->u.encap != oc->u.encap) ++ continue; ++ ++ oc->flags |= DHOPT_ENCAP_DONE; ++ if (match_netid(oc->netid, tagif, 1)) ++ { ++ /* option requested/forced? */ ++ if (!oro || do_encap || (oc->flags & DHOPT_FORCE)) ++ { ++ oc->flags |= DHOPT_ENCAP_MATCH; ++ found = 1; ++ } ++ } ++ } ++ ++ if (found) ++ { ++ o = new_opt6(OPTION6_VENDOR_OPTS); ++ put_opt6_long(opt_cfg->u.encap); ++ ++ for (oc = daemon->dhcp_opts6; oc; oc = oc->next) ++ if (oc->flags & DHOPT_ENCAP_MATCH) ++ { ++ o1 = new_opt6(oc->opt); ++ put_opt6(oc->val, oc->len); ++ end_opt6(o1); ++ } ++ end_opt6(o); ++ } ++ } ++ } ++ ++ ++ if (state->hostname) ++ { ++ unsigned char *p; ++ size_t len = strlen(state->hostname); ++ ++ if (state->send_domain) ++ len += strlen(state->send_domain) + 2; ++ ++ o = new_opt6(OPTION6_FQDN); ++ if ((p = expand(len + 2))) ++ { ++ *(p++) = state->fqdn_flags; ++ p = do_rfc1035_name(p, state->hostname, NULL); ++ if (state->send_domain) ++ { ++ p = do_rfc1035_name(p, state->send_domain, NULL); ++ *p = 0; ++ } ++ } ++ end_opt6(o); ++ } ++ ++ ++ /* logging */ ++ if (option_bool(OPT_LOG_OPTS) && oro) ++ { ++ char *q = daemon->namebuff; ++ for (i = 0; i < opt6_len(oro) - 1; i += 2) ++ { ++ char *s = option_string(AF_INET6, opt6_uint(oro, i, 2), NULL, 0, NULL, 0); ++ q += snprintf(q, MAXDNAME - (q - daemon->namebuff), ++ "%d%s%s%s", ++ opt6_uint(oro, i, 2), ++ strlen(s) != 0 ? ":" : "", ++ s, ++ (i > opt6_len(oro) - 3) ? "" : ", "); ++ if ( i > opt6_len(oro) - 3 || (q - daemon->namebuff) > 40) ++ { ++ q = daemon->namebuff; ++ my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), state->xid, daemon->namebuff); ++ } ++ } ++ } ++ ++ return tagif; ++} ++ ++static int add_local_addrs(struct dhcp_context *context) ++{ ++ int done = 0; ++ ++ for (; context; context = context->current) ++ if ((context->flags & CONTEXT_USED) && !IN6_IS_ADDR_UNSPECIFIED(&context->local6)) ++ { ++ /* squash duplicates */ ++ struct dhcp_context *c; ++ for (c = context->current; c; c = c->current) ++ if ((c->flags & CONTEXT_USED) && ++ IN6_ARE_ADDR_EQUAL(&context->local6, &c->local6)) ++ break; ++ ++ if (!c) ++ { ++ done = 1; ++ put_opt6(&context->local6, IN6ADDRSZ); ++ } ++ } ++ ++ return done; ++} ++ ++ ++static void get_context_tag(struct state *state, struct dhcp_context *context) ++{ ++ /* get tags from context if we've not used it before */ ++ if (context->netid.next == &context->netid && context->netid.net) ++ { ++ context->netid.next = state->context_tags; ++ state->context_tags = &context->netid; ++ if (!state->hostname_auth) ++ { ++ struct dhcp_netid_list *id_list; ++ ++ for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next) ++ if ((!id_list->list) || match_netid(id_list->list, &context->netid, 0)) ++ break; ++ if (id_list) ++ state->hostname = NULL; ++ } ++ } ++} ++ ++static int check_ia(struct state *state, void *opt, void **endp, void **ia_option) ++{ ++ state->ia_type = opt6_type(opt); ++ *ia_option = NULL; ++ ++ if (state->ia_type != OPTION6_IA_NA && state->ia_type != OPTION6_IA_TA) ++ return 0; ++ ++ if (state->ia_type == OPTION6_IA_NA && opt6_len(opt) < 12) ++ return 0; ++ ++ if (state->ia_type == OPTION6_IA_TA && opt6_len(opt) < 4) ++ return 0; ++ ++ *endp = opt6_ptr(opt, opt6_len(opt)); ++ state->iaid = opt6_uint(opt, 0, 4); ++ *ia_option = opt6_find(opt6_ptr(opt, state->ia_type == OPTION6_IA_NA ? 12 : 4), *endp, OPTION6_IAADDR, 24); ++ ++ return 1; ++} ++ ++ ++static int build_ia(struct state *state, int *t1cntr) ++{ ++ int o = new_opt6(state->ia_type); ++ ++ put_opt6_long(state->iaid); ++ *t1cntr = 0; ++ ++ if (state->ia_type == OPTION6_IA_NA) ++ { ++ /* save pointer */ ++ *t1cntr = save_counter(-1); ++ /* so we can fill these in later */ ++ put_opt6_long(0); ++ put_opt6_long(0); ++ } ++ ++ return o; ++} ++ ++static void end_ia(int t1cntr, unsigned int min_time, int do_fuzz) ++{ ++ if (t1cntr != 0) ++ { ++ /* go back and fill in fields in IA_NA option */ ++ int sav = save_counter(t1cntr); ++ unsigned int t1, t2, fuzz = 0; ++ ++ if (do_fuzz) ++ { ++ fuzz = rand16(); ++ ++ while (fuzz > (min_time/16)) ++ fuzz = fuzz/2; ++ } ++ ++ t1 = (min_time == 0xffffffff) ? 0xffffffff : min_time/2 - fuzz; ++ t2 = (min_time == 0xffffffff) ? 0xffffffff : ((min_time/8)*7) - fuzz; ++ put_opt6_long(t1); ++ put_opt6_long(t2); ++ save_counter(sav); ++ } ++} ++ ++static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, void *ia_option, ++ unsigned int *min_time, struct in6_addr *addr, time_t now) ++{ ++ unsigned int valid_time = 0, preferred_time = 0; ++ int o = new_opt6(OPTION6_IAADDR); ++ struct dhcp_lease *lease; ++ ++ /* get client requested times */ ++ if (ia_option) ++ { ++ preferred_time = opt6_uint(ia_option, 16, 4); ++ valid_time = opt6_uint(ia_option, 20, 4); ++ } ++ ++ calculate_times(context, min_time, &valid_time, &preferred_time, lease_time); ++ ++ put_opt6(addr, sizeof(*addr)); ++ put_opt6_long(preferred_time); ++ put_opt6_long(valid_time); ++ end_opt6(o); ++ ++ if (state->lease_allocate) ++ update_leases(state, context, addr, valid_time, now); ++ ++ if ((lease = lease6_find_by_addr(addr, 128, 0))) ++ lease->flags |= LEASE_USED; ++ ++ /* get tags from context if we've not used it before */ ++ if (context->netid.next == &context->netid && context->netid.net) ++ { ++ context->netid.next = state->context_tags; ++ state->context_tags = &context->netid; ++ ++ if (!state->hostname_auth) ++ { ++ struct dhcp_netid_list *id_list; ++ ++ for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next) ++ if ((!id_list->list) || match_netid(id_list->list, &context->netid, 0)) ++ break; ++ if (id_list) ++ state->hostname = NULL; ++ } ++ } ++ ++ log6_quiet(state, state->lease_allocate ? "DHCPREPLY" : "DHCPADVERTISE", addr, state->hostname); ++ ++} ++ ++static void mark_context_used(struct state *state, struct in6_addr *addr) ++{ ++ struct dhcp_context *context; ++ ++ /* Mark that we have an address for this prefix. */ ++ for (context = state->context; context; context = context->current) ++ if (is_same_net6(addr, &context->start6, context->prefix)) ++ context->flags |= CONTEXT_USED; ++} ++ ++static void mark_config_used(struct dhcp_context *context, struct in6_addr *addr) ++{ ++ for (; context; context = context->current) ++ if (is_same_net6(addr, &context->start6, context->prefix)) ++ context->flags |= CONTEXT_CONF_USED; ++} ++ ++/* make sure address not leased to another CLID/IAID */ ++static int check_address(struct state *state, struct in6_addr *addr) ++{ ++ struct dhcp_lease *lease; ++ ++ if (!(lease = lease6_find_by_addr(addr, 128, 0))) ++ return 1; ++ ++ if (lease->clid_len != state->clid_len || ++ memcmp(lease->clid, state->clid, state->clid_len) != 0 || ++ lease->iaid != state->iaid) ++ return 0; ++ ++ return 1; ++} ++ ++static int check_and_try_preempte_address(struct state *state, struct in6_addr *addr, time_t now, struct dhcp_config *config){ ++ struct dhcp_lease *lease; ++ ++ if (!(lease = lease6_find_by_addr(addr, 128, 0))) ++ { ++ return 1; ++ } ++ ++ if (daemon->bind_mac_with_ip6) { ++ // break rfc3315 here ++ // bind mac address with a lease ++ if ((state->mac) && !(config->flags & CONFIG_CLID) && ++ config_has_mac(config, state->mac, state->mac_len, state->mac_type)) { ++ lease_prune(lease, now); ++ return 1; ++ } ++ } ++ ++ // what rfc3315 do ++ if (lease->clid_len != state->clid_len || ++ memcmp(lease->clid, state->clid, state->clid_len) != 0 || ++ lease->iaid != state->iaid) ++ { ++ return 0; ++ } ++ ++ return 1; ++} ++ ++/* return true of *addr could have been generated from config. */ ++static struct addrlist *config_implies(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr) ++{ ++ int prefix; ++ struct in6_addr wild_addr; ++ struct addrlist *addr_list; ++ ++ if (!config || !(config->flags & CONFIG_ADDR6)) ++ return NULL; ++ ++ for (addr_list = config->addr6; addr_list; addr_list = addr_list->next) ++ { ++ prefix = (addr_list->flags & ADDRLIST_PREFIX) ? addr_list->prefixlen : 128; ++ wild_addr = addr_list->addr.addr6; ++ ++ if ((addr_list->flags & ADDRLIST_WILDCARD) && context->prefix == 64) ++ { ++ wild_addr = context->start6; ++ setaddr6part(&wild_addr, addr6part(&addr_list->addr.addr6)); ++ } ++ else if (!is_same_net6(&context->start6, addr, context->prefix)) ++ continue; ++ ++ if (is_same_net6(&wild_addr, addr, prefix)) ++ return addr_list; ++ } ++ ++ return NULL; ++} ++ ++static int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr, struct state *state, time_t now) ++{ ++ u64 addrpart, i, addresses; ++ struct addrlist *addr_list; ++ ++ if (!config || !(config->flags & CONFIG_ADDR6)) ++ return 0; ++ ++ for (addr_list = config->addr6; addr_list; addr_list = addr_list->next) ++ if (!(addr_list->flags & ADDRLIST_DECLINED) || ++ difftime(now, addr_list->decline_time) >= (float)DECLINE_BACKOFF) ++ { ++ addrpart = addr6part(&addr_list->addr.addr6); ++ addresses = 1; ++ ++ if (addr_list->flags & ADDRLIST_PREFIX) ++ addresses = (u64)1<<(128-addr_list->prefixlen); ++ ++ if ((addr_list->flags & ADDRLIST_WILDCARD)) ++ { ++ if (context->prefix != 64) ++ continue; ++ ++ *addr = context->start6; ++ } ++ else if (is_same_net6(&context->start6, &addr_list->addr.addr6, context->prefix)) ++ *addr = addr_list->addr.addr6; ++ else ++ continue; ++ ++ for (i = 0 ; i < addresses; i++) ++ { ++ setaddr6part(addr, addrpart+i); ++ ++ if (check_address(state, addr)) ++ return 1; ++ } ++ } ++ ++ return 0; ++} ++ ++/* Calculate valid and preferred times to send in leases/renewals. ++ ++ Inputs are: ++ ++ *valid_timep, *preferred_timep - requested times from IAADDR options. ++ context->valid, context->preferred - times associated with subnet address on local interface. ++ context->flags | CONTEXT_DEPRECATE - "deprecated" flag in dhcp-range. ++ lease_time - configured time for context for individual client. ++ *min_time - smallest valid time sent so far. ++ ++ Outputs are : ++ ++ *valid_timep, *preferred_timep - times to be send in IAADDR option. ++ *min_time - smallest valid time sent so far, to calculate T1 and T2. ++ ++ */ ++static void calculate_times(struct dhcp_context *context, unsigned int *min_time, unsigned int *valid_timep, ++ unsigned int *preferred_timep, unsigned int lease_time) ++{ ++ unsigned int req_preferred = *preferred_timep, req_valid = *valid_timep; ++ unsigned int valid_time = lease_time, preferred_time = lease_time; ++ ++ /* RFC 3315: "A server ignores the lifetimes set ++ by the client if the preferred lifetime is greater than the valid ++ lifetime. */ ++ if (req_preferred <= req_valid) ++ { ++ if (req_preferred != 0) ++ { ++ /* 0 == "no preference from client" */ ++ if (req_preferred < 120u) ++ req_preferred = 120u; /* sanity */ ++ ++ if (req_preferred < preferred_time) ++ preferred_time = req_preferred; ++ } ++ ++ if (req_valid != 0) ++ /* 0 == "no preference from client" */ ++ { ++ if (req_valid < 120u) ++ req_valid = 120u; /* sanity */ ++ ++ if (req_valid < valid_time) ++ valid_time = req_valid; ++ } ++ } ++ ++ /* deprecate (preferred == 0) which configured, or when local address ++ is deprecated */ ++ if ((context->flags & CONTEXT_DEPRECATE) || context->preferred == 0) ++ preferred_time = 0; ++ ++ if (preferred_time != 0 && preferred_time < *min_time) ++ *min_time = preferred_time; ++ ++ if (valid_time != 0 && valid_time < *min_time) ++ *min_time = valid_time; ++ ++ *valid_timep = valid_time; ++ *preferred_timep = preferred_time; ++} ++ ++static void update_leases(struct state *state, struct dhcp_context *context, struct in6_addr *addr, unsigned int lease_time, time_t now) ++{ ++ struct dhcp_lease *lease = lease6_find_by_addr(addr, 128, 0); ++#ifdef HAVE_SCRIPT ++ struct dhcp_netid *tagif = run_tag_if(state->tags); ++#endif ++ ++ (void)context; ++ ++ if (!lease) ++ lease = lease6_allocate(addr, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA); ++ ++ if (lease) ++ { ++ lease_set_expires(lease, lease_time, now); ++ lease_set_iaid(lease, state->iaid); ++ lease_set_hwaddr(lease, state->mac, state->clid, state->mac_len, state->mac_type, state->clid_len, now, 0); ++ lease_set_interface(lease, state->interface, now); ++ if (state->hostname && state->ia_type == OPTION6_IA_NA) ++ { ++ char *addr_domain = get_domain6(addr); ++ if (!state->send_domain) ++ state->send_domain = addr_domain; ++ lease_set_hostname(lease, state->hostname, state->hostname_auth, addr_domain, state->domain); ++ } ++ ++#ifdef HAVE_SCRIPT ++ if (daemon->lease_change_command) ++ { ++ void *class_opt; ++ lease->flags |= LEASE_CHANGED; ++ free(lease->extradata); ++ lease->extradata = NULL; ++ lease->extradata_size = lease->extradata_len = 0; ++ lease->vendorclass_count = 0; ++ ++ if ((class_opt = opt6_find(state->packet_options, state->end, OPTION6_VENDOR_CLASS, 4))) ++ { ++ void *enc_opt, *enc_end = opt6_ptr(class_opt, opt6_len(class_opt)); ++ lease->vendorclass_count++; ++ /* send enterprise number first */ ++ sprintf(daemon->dhcp_buff2, "%u", opt6_uint(class_opt, 0, 4)); ++ lease_add_extradata(lease, (unsigned char *)daemon->dhcp_buff2, strlen(daemon->dhcp_buff2), 0); ++ ++ if (opt6_len(class_opt) >= 6) ++ for (enc_opt = opt6_ptr(class_opt, 4); enc_opt; enc_opt = opt6_next(enc_opt, enc_end)) ++ { ++ lease->vendorclass_count++; ++ lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0); ++ } ++ } ++ ++ lease_add_extradata(lease, (unsigned char *)state->client_hostname, ++ state->client_hostname ? strlen(state->client_hostname) : 0, 0); ++ ++ /* space-concat tag set */ ++ if (!tagif && !context->netid.net) ++ lease_add_extradata(lease, NULL, 0, 0); ++ else ++ { ++ if (context->netid.net) ++ lease_add_extradata(lease, (unsigned char *)context->netid.net, strlen(context->netid.net), tagif ? ' ' : 0); ++ ++ if (tagif) ++ { ++ struct dhcp_netid *n; ++ for (n = tagif; n; n = n->next) ++ { ++ struct dhcp_netid *n1; ++ /* kill dupes */ ++ for (n1 = n->next; n1; n1 = n1->next) ++ if (strcmp(n->net, n1->net) == 0) ++ break; ++ if (!n1) ++ lease_add_extradata(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0); ++ } ++ } ++ } ++ ++ if (state->link_address) ++ inet_ntop(AF_INET6, state->link_address, daemon->addrbuff, ADDRSTRLEN); ++ ++ lease_add_extradata(lease, (unsigned char *)daemon->addrbuff, state->link_address ? strlen(daemon->addrbuff) : 0, 0); ++ ++ if ((class_opt = opt6_find(state->packet_options, state->end, OPTION6_USER_CLASS, 2))) ++ { ++ void *enc_opt, *enc_end = opt6_ptr(class_opt, opt6_len(class_opt)); ++ for (enc_opt = opt6_ptr(class_opt, 0); enc_opt; enc_opt = opt6_next(enc_opt, enc_end)) ++ lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0); ++ } ++ } ++#endif ++ ++ } ++} ++ ++ ++ ++static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_opts) ++{ ++ void *opt; ++ char *desc = nest ? "nest" : "sent"; ++ ++ if (!option_bool(OPT_LOG_OPTS) || start_opts == end_opts) ++ return; ++ ++ for (opt = start_opts; opt; opt = opt6_next(opt, end_opts)) ++ { ++ int type = opt6_type(opt); ++ void *ia_options = NULL; ++ char *optname; ++ ++ if (type == OPTION6_IA_NA) ++ { ++ sprintf(daemon->namebuff, "IAID=%u T1=%u T2=%u", ++ opt6_uint(opt, 0, 4), opt6_uint(opt, 4, 4), opt6_uint(opt, 8, 4)); ++ optname = "ia-na"; ++ ia_options = opt6_ptr(opt, 12); ++ } ++ else if (type == OPTION6_IA_TA) ++ { ++ sprintf(daemon->namebuff, "IAID=%u", opt6_uint(opt, 0, 4)); ++ optname = "ia-ta"; ++ ia_options = opt6_ptr(opt, 4); ++ } ++ else if (type == OPTION6_IAADDR) ++ { ++ struct in6_addr addr; ++ ++ /* align */ ++ memcpy(&addr, opt6_ptr(opt, 0), IN6ADDRSZ); ++ inet_ntop(AF_INET6, &addr, daemon->addrbuff, ADDRSTRLEN); ++ sprintf(daemon->namebuff, "%s PL=%u VL=%u", ++ daemon->addrbuff, opt6_uint(opt, 16, 4), opt6_uint(opt, 20, 4)); ++ optname = "iaaddr"; ++ ia_options = opt6_ptr(opt, 24); ++ } ++ else if (type == OPTION6_STATUS_CODE) ++ { ++ int len = sprintf(daemon->namebuff, "%u ", opt6_uint(opt, 0, 2)); ++ memcpy(daemon->namebuff + len, opt6_ptr(opt, 2), opt6_len(opt)-2); ++ daemon->namebuff[len + opt6_len(opt) - 2] = 0; ++ optname = "status"; ++ } ++ else ++ { ++ /* account for flag byte on FQDN */ ++ int offset = type == OPTION6_FQDN ? 1 : 0; ++ optname = option_string(AF_INET6, type, opt6_ptr(opt, offset), opt6_len(opt) - offset, daemon->namebuff, MAXDNAME); ++ } ++ ++ my_syslog(MS_DHCP | LOG_INFO, "%u %s size:%3d option:%3d %s %s", ++ xid, desc, opt6_len(opt), type, optname, daemon->namebuff); ++ ++ if (ia_options) ++ log6_opts(1, xid, ia_options, opt6_ptr(opt, opt6_len(opt))); ++ } ++} ++ ++static void log6_quiet(struct state *state, char *type, struct in6_addr *addr, char *string) ++{ ++ if (option_bool(OPT_LOG_OPTS) || !option_bool(OPT_QUIET_DHCP6)) ++ log6_packet(state, type, addr, string); ++} ++ ++static void log6_packet(struct state *state, char *type, struct in6_addr *addr, char *string) ++{ ++ int clid_len = state->clid_len; ++ ++ /* avoid buffer overflow */ ++ if (clid_len > 100) ++ clid_len = 100; ++ ++ print_mac(daemon->namebuff, state->clid, clid_len); ++ ++ if (addr) ++ { ++ inet_ntop(AF_INET6, addr, daemon->dhcp_buff2, DHCP_BUFF_SZ - 1); ++ strcat(daemon->dhcp_buff2, " "); ++ } ++ else ++ daemon->dhcp_buff2[0] = 0; ++ ++ if(option_bool(OPT_LOG_OPTS)) ++ my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s %s", ++ state->xid, ++ type, ++ state->iface_name, ++ daemon->dhcp_buff2, ++ daemon->namebuff, ++ string ? string : ""); ++ else ++ my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s %s", ++ type, ++ state->iface_name, ++ daemon->dhcp_buff2, ++ daemon->namebuff, ++ string ? string : ""); ++} ++ ++static void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsize) ++{ ++ u16 opt, opt_len; ++ void *start; ++ ++ if (!opts) ++ return NULL; ++ ++ while (1) ++ { ++ if (end - opts < 4) ++ return NULL; ++ ++ start = opts; ++ GETSHORT(opt, opts); ++ GETSHORT(opt_len, opts); ++ ++ if (opt_len > (end - opts)) ++ return NULL; ++ ++ if (opt == search && (opt_len >= minsize)) ++ return start; ++ ++ opts += opt_len; ++ } ++} ++ ++static void *opt6_next(void *opts, void *end) ++{ ++ u16 opt_len; ++ ++ if (end - opts < 4) ++ return NULL; ++ ++ opts += 2; ++ GETSHORT(opt_len, opts); ++ ++ if (opt_len >= (end - opts)) ++ return NULL; ++ ++ return opts + opt_len; ++} ++ ++static unsigned int opt6_uint(unsigned char *opt, int offset, int size) ++{ ++ /* this worries about unaligned data and byte order */ ++ unsigned int ret = 0; ++ int i; ++ unsigned char *p = opt6_ptr(opt, offset); ++ ++ for (i = 0; i < size; i++) ++ ret = (ret << 8) | *p++; ++ ++ return ret; ++} ++ ++void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, ++ struct in6_addr *peer_address, u32 scope_id, time_t now) ++{ ++ /* ->local is same value for all relays on ->current chain */ ++ ++ union all_addr from; ++ unsigned char *header; ++ unsigned char *inbuff = daemon->dhcp_packet.iov_base; ++ int msg_type = *inbuff; ++ int hopcount; ++ struct in6_addr multicast; ++ unsigned int maclen, mactype; ++ unsigned char mac[DHCP_CHADDR_MAX]; ++ ++ inet_pton(AF_INET6, ALL_SERVERS, &multicast); ++ get_client_mac(peer_address, scope_id, mac, &maclen, &mactype, now); ++ ++ /* source address == relay address */ ++ from.addr6 = relay->local.addr6; ++ ++ /* Get hop count from nested relayed message */ ++ if (msg_type == DHCP6RELAYFORW) ++ hopcount = *((unsigned char *)inbuff+1) + 1; ++ else ++ hopcount = 0; ++ ++ /* RFC 3315 HOP_COUNT_LIMIT */ ++ if (hopcount > 32) ++ return; ++ ++ reset_counter(); ++ ++ if ((header = put_opt6(NULL, 34))) ++ { ++ int o; ++ ++ header[0] = DHCP6RELAYFORW; ++ header[1] = hopcount; ++ memcpy(&header[2], &relay->local.addr6, IN6ADDRSZ); ++ memcpy(&header[18], peer_address, IN6ADDRSZ); ++ ++ /* RFC-6939 */ ++ if (maclen != 0) ++ { ++ o = new_opt6(OPTION6_CLIENT_MAC); ++ put_opt6_short(mactype); ++ put_opt6(mac, maclen); ++ end_opt6(o); ++ } ++ ++ o = new_opt6(OPTION6_RELAY_MSG); ++ put_opt6(inbuff, sz); ++ end_opt6(o); ++ ++ for (; relay; relay = relay->current) ++ { ++ union mysockaddr to; ++ ++ to.sa.sa_family = AF_INET6; ++ to.in6.sin6_addr = relay->server.addr6; ++ to.in6.sin6_port = htons(DHCPV6_SERVER_PORT); ++ to.in6.sin6_flowinfo = 0; ++ to.in6.sin6_scope_id = 0; ++ ++ if (IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast)) ++ { ++ int multicast_iface; ++ if (!relay->interface || strchr(relay->interface, '*') || ++ (multicast_iface = if_nametoindex(relay->interface)) == 0 || ++ setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_iface, sizeof(multicast_iface)) == -1) ++ my_syslog(MS_DHCP | LOG_ERR, _("Cannot multicast to DHCPv6 server without correct interface")); ++ } ++ ++ send_from(daemon->dhcp6fd, 0, daemon->outpacket.iov_base, save_counter(-1), &to, &from, 0); ++ ++ if (option_bool(OPT_LOG_OPTS)) ++ { ++ inet_ntop(AF_INET6, &relay->local, daemon->addrbuff, ADDRSTRLEN); ++ inet_ntop(AF_INET6, &relay->server, daemon->namebuff, ADDRSTRLEN); ++ my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, daemon->namebuff); ++ } ++ ++ /* Save this for replies */ ++ relay->iface_index = scope_id; ++ } ++ } ++} ++ ++unsigned short relay_reply6(struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface) ++{ ++ struct dhcp_relay *relay; ++ struct in6_addr link; ++ unsigned char *inbuff = daemon->dhcp_packet.iov_base; ++ ++ /* must have at least msg_type+hopcount+link_address+peer_address+minimal size option ++ which is 1 + 1 + 16 + 16 + 2 + 2 = 38 */ ++ ++ if (sz < 38 || *inbuff != DHCP6RELAYREPL) ++ return 0; ++ ++ memcpy(&link, &inbuff[2], IN6ADDRSZ); ++ ++ for (relay = daemon->relay6; relay; relay = relay->next) ++ if (IN6_ARE_ADDR_EQUAL(&link, &relay->local.addr6) && ++ (!relay->interface || wildcard_match(relay->interface, arrival_interface))) ++ break; ++ ++ reset_counter(); ++ ++ if (relay) ++ { ++ void *opt, *opts = inbuff + 34; ++ void *end = inbuff + sz; ++ for (opt = opts; opt; opt = opt6_next(opt, end)) ++ if (opt6_type(opt) == OPTION6_RELAY_MSG && opt6_len(opt) > 0) ++ { ++ int encap_type = *((unsigned char *)opt6_ptr(opt, 0)); ++ put_opt6(opt6_ptr(opt, 0), opt6_len(opt)); ++ memcpy(&peer->sin6_addr, &inbuff[18], IN6ADDRSZ); ++ peer->sin6_scope_id = relay->iface_index; ++ return encap_type == DHCP6RELAYREPL ? DHCPV6_SERVER_PORT : DHCPV6_CLIENT_PORT; ++ } ++ } ++ ++ return 0; ++} ++ ++#endif -- -2.19.1 +1.8.3.1 diff --git a/dnsmasq-2.77-underflow.patch b/dnsmasq-2.77-underflow.patch index 2e900bfba04777b4da9b60171990e4317f17156f..743f059ef29958a432294b55678a688344229c50 100644 --- a/dnsmasq-2.77-underflow.patch +++ b/dnsmasq-2.77-underflow.patch @@ -1,63 +1,26 @@ -From c82a594d95431e8615126621397ea595eb037a6b Mon Sep 17 00:00:00 2001 -From: Doran Moppert -Date: Tue, 26 Sep 2017 14:48:20 +0930 -Subject: [PATCH] google patch hand-applied +From 4d3ef152a5d0a3cf053d41ea950823bfcc0ea5c2 Mon Sep 17 00:00:00 2001 +From: xiaoweiwei +Date: Tue, 28 Jul 2020 10:32:50 +0800 +Subject: [PATCH] underflow --- - src/edns0.c | 10 +++++----- - src/forward.c | 4 ++++ - src/rfc1035.c | 2 ++ - 3 files changed, 11 insertions(+), 5 deletions(-) + src/rfc1035.c | 3 +++ + 1 file changed, 3 insertions(+) -diff --git a/src/edns0.c b/src/edns0.c -index af33877..ba6ff0c 100644 ---- a/src/edns0.c -+++ b/src/edns0.c -@@ -212,11 +212,11 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l - /* Copy back any options */ - if (buff) - { -- if (p + rdlen > limit) -- { -- free(buff); -- return plen; /* Too big */ -- } -+ if (p + rdlen > limit) -+ { -+ free(buff); -+ return plen; /* Too big */ -+ } - memcpy(p, buff, rdlen); - free(buff); - p += rdlen; -diff --git a/src/forward.c b/src/forward.c -index cdd11d3..3078f64 100644 ---- a/src/forward.c -+++ b/src/forward.c -@@ -1438,6 +1438,10 @@ void receive_query(struct listener *listen, time_t now) - udp_size = PACKETSZ; /* Sanity check - can't reduce below default. RFC 6891 6.2.3 */ - } - -+ // Make sure the udp size is not smaller than the incoming message so that we -+ // do not underflow -+ if (udp_size < n) udp_size = n; -+ - #ifdef HAVE_AUTH - if (auth_dns) - { diff --git a/src/rfc1035.c b/src/rfc1035.c -index b078b59..777911b 100644 +index fefe63d..d3cce6f 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c -@@ -1281,6 +1281,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, - int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1; - struct mx_srv_record *rec; +@@ -1340,6 +1340,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, size_t len; -+ // Make sure we do not underflow here too. -+ if (qlen > (limit - ((char *)header))) return 0; + int rd_bit = (header->hb3 & HB3_RD); ++ // Make sure we do not underflow here too ++ if (qlen > (limit - ((char *)header))) return 0; ++ + /* never answer queries with RD unset, to avoid cache snooping. */ if (ntohs(header->ancount) != 0 || ntohs(header->nscount) != 0 || -- -2.14.3 +1.8.3.1 diff --git a/dnsmasq-2.78-fips.patch b/dnsmasq-2.78-fips.patch index 011433c76f57c533b1f3627113a5125c96f5ee8d..6454b95b8b53c77e1963856585d06fe89ded5d0a 100644 --- a/dnsmasq-2.78-fips.patch +++ b/dnsmasq-2.78-fips.patch @@ -1,37 +1,26 @@ -From 89f57e39b69f92beacb6bad9c68d61f9c4fb0e77 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= -Date: Fri, 2 Mar 2018 13:17:04 +0100 -Subject: [PATCH] Print warning on FIPS machine with dnssec enabled. Dnsmasq - has no proper FIPS 140-2 compliant implementation. +From 8c8ca24806d5ebfe5018279ec84538a17014a918 Mon Sep 17 00:00:00 2001 +From: xiaoweiwei +Date: Tue, 28 Jul 2020 10:57:56 +0800 +Subject: [PATCH] fips --- - src/dnsmasq.c | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) + src/dnsmasq.c | 3 +++ + 1 file changed, 3 insertions(+) diff --git a/src/dnsmasq.c b/src/dnsmasq.c -index ce44809..9f6c020 100644 +index 2306c48..bfad87f 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c -@@ -187,6 +187,7 @@ int main (int argc, char **argv) - - if (daemon->cachesize < CACHESIZ) - die(_("cannot reduce cache size from default when DNSSEC enabled"), NULL, EC_BADCONF); -+ - #else - die(_("DNSSEC not available: set HAVE_DNSSEC in src/config.h"), NULL, EC_BADCONF); - #endif -@@ -769,7 +770,10 @@ int main (int argc, char **argv) - } - - my_syslog(LOG_INFO, _("DNSSEC validation enabled")); -- +@@ -877,6 +877,9 @@ int main (int argc, char **argv) + my_syslog(LOG_INFO, _("DNSSEC validation enabled but all unsigned answers are trusted")); + else + my_syslog(LOG_INFO, _("DNSSEC validation enabled")); + + if (access("/etc/system-fips", F_OK) == 0) + my_syslog(LOG_WARNING, _("DNSSEC support is not FIPS 140-2 compliant")); -+ + daemon->dnssec_no_time_check = option_bool(OPT_DNSSEC_TIME); if (option_bool(OPT_DNSSEC_TIME) && !daemon->back_to_the_future) - my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until receipt of SIGINT")); -- -2.14.4 +1.8.3.1 diff --git a/dnsmasq-2.79.tar.xz b/dnsmasq-2.79.tar.xz deleted file mode 100644 index d2baa873556eb9853a36cbbe49667a7d73378078..0000000000000000000000000000000000000000 Binary files a/dnsmasq-2.79.tar.xz and /dev/null differ diff --git a/dnsmasq-2.80-dnssec.patch b/dnsmasq-2.80-dnssec.patch deleted file mode 100644 index a34f46eccdefff71b7c6ec9daea237c2beb62701..0000000000000000000000000000000000000000 --- a/dnsmasq-2.80-dnssec.patch +++ /dev/null @@ -1,73 +0,0 @@ -From a997ca0da044719a0ce8a232d14da8b30022592b Mon Sep 17 00:00:00 2001 -From: Simon Kelley -Date: Fri, 29 Jun 2018 14:39:41 +0100 -Subject: [PATCH] Fix sometimes missing DNSSEC RRs when DNSSEC validation not - enabled. - -Dnsmasq does pass on the do-bit, and return DNSSEC RRs, irrespective -of of having DNSSEC validation compiled in or enabled. - -The thing to understand here is that the cache does not store all the -DNSSEC RRs, and dnsmasq doesn't have the (very complex) logic required -to determine the set of DNSSEC RRs required in an answer. Therefore if -the client wants the DNSSEC RRs, the query can not be answered from -the cache. When DNSSEC validation is enabled, any query with the -do-bit set is never answered from the cache, unless the domain is -known not to be signed: the query is always forwarded. This ensures -that the DNSEC RRs are included. - -The same thing should be true when DNSSEC validation is not enabled, -but there's a bug in the logic. - -line 1666 of src/rfc1035.c looks like this - - if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !do_bit || !(crecp->flags & F_DNSSECOK)) - -{ ...answer from cache ... } - -So local stuff (hosts, DHCP, ) get answered. If the do_bit is not set -then the query is answered, and if the domain is known not to be -signed, the query is answered. - -Unfortunately, if DNSSEC validation is not turned on then the -F_DNSSECOK bit is not valid, and it's always zero, so the question -always gets answered from the cache, even when the do-bit is set. - -This code should look like that at line 1468, dealing with PTR queries - - if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || - !do_bit || - (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK))) - -where the F_DNSSECOK bit is only used when validation is enabled. ---- - src/rfc1035.c | 6 ++++-- - 1 file changed, 4 insertions(+), 2 deletions(-) - -diff --git a/src/rfc1035.c b/src/rfc1035.c -index ebb1f36..580f5ef 100644 ---- a/src/rfc1035.c -+++ b/src/rfc1035.c -@@ -1663,7 +1663,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, - } - - /* If the client asked for DNSSEC don't use cached data. */ -- if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !do_bit || !(crecp->flags & F_DNSSECOK)) -+ if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || -+ !do_bit || -+ (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK))) - do - { - /* don't answer wildcard queries with data not from /etc/hosts -@@ -1747,7 +1749,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, - { - if ((crecp = cache_find_by_name(NULL, name, now, F_CNAME | (dryrun ? F_NO_RR : 0))) && - (qtype == T_CNAME || (crecp->flags & F_CONFIG)) && -- ((crecp->flags & F_CONFIG) || !do_bit || !(crecp->flags & F_DNSSECOK))) -+ ((crecp->flags & F_CONFIG) || !do_bit || (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)))) - { - if (!(crecp->flags & F_DNSSECOK)) - sec_data = 0; --- -2.14.4 - diff --git a/dnsmasq-2.82.tar.xz b/dnsmasq-2.82.tar.xz new file mode 100644 index 0000000000000000000000000000000000000000..7b6a4ca7a55dadf0f04e73165b330d685abc302e Binary files /dev/null and b/dnsmasq-2.82.tar.xz differ diff --git a/dnsmasq.spec b/dnsmasq.spec index 1dd6481fb8093d216580903577749825b182149d..ea450ee3e868847b92ac11a2c6aff459ec700001 100644 --- a/dnsmasq.spec +++ b/dnsmasq.spec @@ -1,6 +1,6 @@ Name: dnsmasq -Version: 2.79 -Release: 11 +Version: 2.82 +Release: 1 Summary: Dnsmasq provides network infrastructure for small networks License: GPLv2 or GPLv3 URL: http://www.thekelleys.org.uk/dnsmasq/ @@ -8,12 +8,9 @@ Source0: http://www.thekelleys.org.uk/dnsmasq/%{name}-%{version}.tar.xz Source1: dnsmasq.service Source2: dnsmasq-systemd-sysusers.conf -#patches from the opensource fedora/redhat repository Patch0001: dnsmasq-2.77-underflow.patch Patch0002: dnsmasq-2.78-fips.patch -Patch0003: dnsmasq-2.80-dnssec.patch -Patch6000: 0141-Fix-memory-leak-in-helper.c.patch Patch9000: bugfix-allow-binding-mac-with-ipv6.patch Patch9001: bugfix-deal-with-CONFRIM-when-binding-mac-with-ipv6.patch @@ -109,6 +106,12 @@ install -Dpm644 %{SOURCE2} $RPM_BUILD_ROOT%{_sysusersdir}/dnsmasq.conf %{_mandir}/man8/dnsmasq* %changelog +* Tue Jul 28 2020 xiaoweiwei - 2.82-1 +- Type:upgrade +- Id:NA +- SUG:NA +- DESC:upgrade to 2.82 + * Sat Dec 21 2019 openEuler Buildteam - 2.79-11 - Type:bugfix - Id:NA