--- embedaddon/dnsmasq/src/rfc3315.c 2014/06/15 16:31:38 1.1.1.2 +++ embedaddon/dnsmasq/src/rfc3315.c 2016/11/02 09:57:01 1.1.1.3 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2016 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 @@ -130,7 +130,7 @@ static int dhcp6_maybe_relay(struct state *state, void 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); + get_client_mac(client_addr, state->interface, state->mac, &state->mac_len, &state->mac_type, now); else { struct dhcp_context *c; @@ -313,8 +313,8 @@ static int dhcp6_no_relay(struct state *state, int msg else if (msg_type != DHCP6IREQ) return 0; - /* server-id must match except for SOLICIT and CONFIRM messages */ - if (msg_type != DHCP6SOLICIT && msg_type != DHCP6CONFIRM && msg_type != DHCP6IREQ && + /* 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)) @@ -328,6 +328,7 @@ static int dhcp6_no_relay(struct state *state, int msg (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"); @@ -690,6 +691,8 @@ static int dhcp6_no_relay(struct state *state, int msg #endif 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)) { @@ -780,6 +783,27 @@ static int dhcp6_no_relay(struct state *state, int msg 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); } @@ -805,7 +829,16 @@ static int dhcp6_no_relay(struct state *state, int msg put_opt6_short(DHCP6NOADDRS); put_opt6_string(_("no addresses available")); end_opt6(o1); - log6_packet(state, "DHCPADVERTISE", NULL, _("no addresses available")); + + /* 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; @@ -861,7 +894,7 @@ static int dhcp6_no_relay(struct state *state, int msg { /* Static range, not configured. */ o1 = new_opt6(OPTION6_STATUS_CODE); - put_opt6_short(DHCP6UNSPEC); + put_opt6_short(DHCP6NOADDRS); put_opt6_string(_("address unavailable")); end_opt6(o1); } @@ -1014,9 +1047,9 @@ static int dhcp6_no_relay(struct state *state, int msg { preferred_time = valid_time = 0; message = _("address invalid"); - } + } - if (message) + if (message && (message != state->hostname)) log6_packet(state, "DHCPREPLY", req_addr, message); else log6_quiet(state, "DHCPREPLY", req_addr, message); @@ -1039,6 +1072,8 @@ static int dhcp6_no_relay(struct state *state, int msg case DHCP6CONFIRM: { + int good_addr = 0; + /* set reply message type */ *outmsgtypep = DHCP6REPLY; @@ -1054,7 +1089,7 @@ static int dhcp6_no_relay(struct state *state, int msg { struct in6_addr *req_addr = opt6_ptr(ia_option, 0); - if (!address6_available(state->context, req_addr, tagif, 1)) + if (!address6_valid(state->context, req_addr, tagif, 1)) { o1 = new_opt6(OPTION6_STATUS_CODE); put_opt6_short(DHCP6NOTONLINK); @@ -1063,9 +1098,14 @@ static int dhcp6_no_relay(struct state *state, int msg 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 ); @@ -1232,6 +1272,12 @@ static int dhcp6_no_relay(struct state *state, int msg } } + + /* We must anwser with 'success' in global section anyway */ + o1 = new_opt6(OPTION6_STATUS_CODE); + put_opt6_short(DHCP6SUCCESS); + put_opt6_string(_("success")); + end_opt6(o1); break; } @@ -1274,15 +1320,15 @@ static struct dhcp_netid *add_options(struct state *st 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; - if (opt_cfg->opt == OPTION6_DNS_SERVER) - done_dns = 1; - 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)) || @@ -2008,7 +2054,8 @@ static unsigned int opt6_uint(unsigned char *opt, int return ret; } -void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, u32 scope_id) +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 */ @@ -2022,7 +2069,7 @@ void relay_upstream6(struct dhcp_relay *relay, ssize_t unsigned char mac[DHCP_CHADDR_MAX]; inet_pton(AF_INET6, ALL_SERVERS, &multicast); - get_client_mac(peer_address, scope_id, mac, &maclen, &mactype); + get_client_mac(peer_address, scope_id, mac, &maclen, &mactype, now); /* source address == relay address */ from.addr.addr6 = relay->local.addr.addr6;