server/dhcpleasequery.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2011-2013 by Internet Systems Consortium, Inc. ("ISC")
00003  * Copyright (C) 2006-2007,2009 by Internet Systems Consortium, Inc. ("ISC")
00004  *
00005  * Permission to use, copy, modify, and distribute this software for any
00006  * purpose with or without fee is hereby granted, provided that the above
00007  * copyright notice and this permission notice appear in all copies.
00008  *
00009  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
00010  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
00011  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
00012  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
00013  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
00014  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
00015  * PERFORMANCE OF THIS SOFTWARE.
00016  */
00017 
00018 #include "dhcpd.h"
00019 
00020 /*
00021  * TODO: RFC4388 specifies that the server SHOULD return the same
00022  *       options it would for a DHCREQUEST message, if no Parameter
00023  *       Request List option (option 55) is passed. We do not do that.
00024  *
00025  * TODO: RFC4388 specifies the creation of a "non-sensitive options"
00026  *       configuration list, and that these SHOULD be returned. We
00027  *       have no such list.
00028  *
00029  * TODO: RFC4388 says the server SHOULD use RFC3118, "Authentication
00030  *       for DHCP Messages".
00031  *
00032  * TODO: RFC4388 specifies that you SHOULD insure that you cannot be
00033  *       DoS'ed by DHCPLEASEQUERY message.
00034  */
00035 
00036 /* 
00037  * If you query by hardware address or by client ID, then you may have
00038  * more than one IP address for your query argument. We need to do two
00039  * things:
00040  *
00041  *   1. Find the most recent lease.
00042  *   2. Find all additional IP addresses for the query argument.
00043  *
00044  * We do this by looking through all of the leases associated with a
00045  * given hardware address or client ID. We use the cltt (client last
00046  * transaction time) of the lease, which only has a resolution of one
00047  * second, so we might not actually give the very latest IP.
00048  */
00049 
00050 static struct lease*
00051 next_hw(const struct lease *lease) {
00052         /* INSIST(lease != NULL); */
00053         return lease->n_hw;
00054 }
00055 
00056 static struct lease*
00057 next_uid(const struct lease *lease) {
00058         /* INSIST(lease != NULL); */
00059         return lease->n_uid;
00060 }
00061 
00062 void
00063 get_newest_lease(struct lease **retval,
00064                  struct lease *lease,
00065                  struct lease *(*next)(const struct lease *)) {
00066 
00067         struct lease *p;
00068         struct lease *newest;
00069 
00070         /* INSIST(newest != NULL); */
00071         /* INSIST(next != NULL); */
00072 
00073         *retval = NULL;
00074 
00075         if (lease == NULL) {
00076                 return;
00077         }
00078 
00079         newest = lease;
00080         for (p=next(lease); p != NULL; p=next(p)) {
00081                 if (newest->binding_state == FTS_ACTIVE) {
00082                         if ((p->binding_state == FTS_ACTIVE) && 
00083                         (p->cltt > newest->cltt)) {
00084                                 newest = p;
00085                         }
00086                 } else {
00087                         if (p->ends > newest->ends) {
00088                                 newest = p;
00089                         }
00090                 }
00091         }
00092 
00093         lease_reference(retval, newest, MDL);
00094 }
00095 
00096 static int
00097 get_associated_ips(const struct lease *lease,
00098                    struct lease *(*next)(const struct lease *), 
00099                    const struct lease *newest,
00100                    u_int32_t *associated_ips,
00101                    unsigned int associated_ips_size) {
00102 
00103         const struct lease *p;
00104         int cnt;
00105 
00106         /* INSIST(next != NULL); */
00107         /* INSIST(associated_ips != NULL); */
00108 
00109         if (lease == NULL) {
00110                 return 0;
00111         }
00112 
00113         cnt = 0;
00114         for (p=lease; p != NULL; p=next(p)) {
00115                 if ((p->binding_state == FTS_ACTIVE) && (p != newest)) {
00116                         if (cnt < associated_ips_size) {
00117                                 memcpy(&associated_ips[cnt],
00118                                        p->ip_addr.iabuf,
00119                                        sizeof(associated_ips[cnt]));
00120                         }
00121                         cnt++;
00122                 }
00123         }
00124         return cnt;
00125 }
00126 
00127 
00128 void 
00129 dhcpleasequery(struct packet *packet, int ms_nulltp) {
00130         char msgbuf[256];
00131         char dbg_info[128];
00132         struct iaddr cip;
00133         struct iaddr gip;
00134         struct data_string uid;
00135         struct hardware h;
00136         struct lease *tmp_lease;
00137         struct lease *lease;
00138         int want_associated_ip;
00139         int assoc_ip_cnt;
00140         u_int32_t assoc_ips[40];  /* XXXSK: arbitrary maximum number of IPs */
00141         const int nassoc_ips = sizeof(assoc_ips) / sizeof(assoc_ips[0]);
00142 
00143         unsigned char dhcpMsgType;
00144         const char *dhcp_msg_type_name;
00145         struct subnet *subnet;
00146         struct group *relay_group;
00147         struct option_state *options;
00148         struct option_cache *oc;
00149         int allow_leasequery;
00150         int ignorep;
00151         u_int32_t lease_duration;
00152         u_int32_t time_renewal;
00153         u_int32_t time_rebinding;
00154         u_int32_t time_expiry;
00155         u_int32_t client_last_transaction_time;
00156         struct sockaddr_in to;
00157         struct in_addr siaddr;
00158         struct data_string prl;
00159         struct data_string *prl_ptr;
00160 
00161         int i;
00162         struct interface_info *interface;
00163 
00164         /* INSIST(packet != NULL); */
00165 
00166         /*
00167          * Prepare log information.
00168          */
00169         snprintf(msgbuf, sizeof(msgbuf), 
00170                 "DHCPLEASEQUERY from %s", inet_ntoa(packet->raw->giaddr));
00171 
00172         /* 
00173          * We can't reply if there is no giaddr field.
00174          */
00175         if (!packet->raw->giaddr.s_addr) {
00176                 log_info("%s: missing giaddr, ciaddr is %s, no reply sent", 
00177                          msgbuf, inet_ntoa(packet->raw->ciaddr));
00178                 return;
00179         }
00180 
00181         /* 
00182          * Initially we use the 'giaddr' subnet options scope to determine if
00183          * the giaddr-identified relay agent is permitted to perform a
00184          * leasequery.  The subnet is not required, and may be omitted, in
00185          * which case we are essentially interrogating the root options class
00186          * to find a globally permit.
00187          */
00188         gip.len = sizeof(packet->raw->giaddr);
00189         memcpy(gip.iabuf, &packet->raw->giaddr, sizeof(packet->raw->giaddr));
00190 
00191         subnet = NULL;
00192         find_subnet(&subnet, gip, MDL);
00193         if (subnet != NULL)
00194                 relay_group = subnet->group;
00195         else
00196                 relay_group = root_group;
00197 
00198         subnet_dereference(&subnet, MDL);
00199 
00200         options = NULL;
00201         if (!option_state_allocate(&options, MDL)) {
00202                 log_error("No memory for option state.");
00203                 log_info("%s: out of memory, no reply sent", msgbuf);
00204                 return;
00205         }
00206 
00207         execute_statements_in_scope(NULL, packet, NULL, NULL, packet->options,
00208                                     options, &global_scope, relay_group,
00209                                     NULL, NULL);
00210 
00211         for (i=packet->class_count-1; i>=0; i--) {
00212                 execute_statements_in_scope(NULL, packet, NULL, NULL,
00213                                             packet->options, options,
00214                                             &global_scope,
00215                                             packet->classes[i]->group,
00216                                             relay_group, NULL);
00217         }
00218 
00219         /* 
00220          * Because LEASEQUERY has some privacy concerns, default to deny.
00221          */
00222         allow_leasequery = 0;
00223 
00224         /*
00225          * See if we are authorized to do LEASEQUERY.
00226          */
00227         oc = lookup_option(&server_universe, options, SV_LEASEQUERY);
00228         if (oc != NULL) {
00229                 allow_leasequery = evaluate_boolean_option_cache(&ignorep,
00230                                          packet, NULL, NULL, packet->options,
00231                                          options, &global_scope, oc, MDL);
00232         }
00233 
00234         if (!allow_leasequery) {
00235                 log_info("%s: LEASEQUERY not allowed, query ignored", msgbuf);
00236                 option_state_dereference(&options, MDL);
00237                 return;
00238         }
00239 
00240 
00241         /* 
00242          * Copy out the client IP address.
00243          */
00244         cip.len = sizeof(packet->raw->ciaddr);
00245         memcpy(cip.iabuf, &packet->raw->ciaddr, sizeof(packet->raw->ciaddr));
00246 
00247         /* 
00248          * If the client IP address is valid (not all zero), then we 
00249          * are looking for information about that IP address.
00250          */
00251         assoc_ip_cnt = 0;
00252         lease = tmp_lease = NULL;
00253         if (memcmp(cip.iabuf, "\0\0\0", 4)) {
00254 
00255                 want_associated_ip = 0;
00256 
00257                 snprintf(dbg_info, sizeof(dbg_info), "IP %s", piaddr(cip));
00258                 find_lease_by_ip_addr(&lease, cip, MDL);
00259 
00260 
00261         } else {
00262 
00263                 want_associated_ip = 1;
00264 
00265                 /*
00266                  * If the client IP address is all zero, then we will
00267                  * either look up by the client identifier (if we have
00268                  * one), or by the MAC address.
00269                  */
00270 
00271                 memset(&uid, 0, sizeof(uid));
00272                 if (get_option(&uid, 
00273                                &dhcp_universe,
00274                                packet,
00275                                NULL,
00276                                NULL,
00277                                packet->options,
00278                                NULL,
00279                                packet->options, 
00280                                &global_scope,
00281                                DHO_DHCP_CLIENT_IDENTIFIER,
00282                                MDL)) {
00283 
00284                         snprintf(dbg_info, 
00285                                  sizeof(dbg_info), 
00286                                  "client-id %s",
00287                                  print_hex_1(uid.len, uid.data, 60));
00288 
00289                         find_lease_by_uid(&tmp_lease, uid.data, uid.len, MDL);
00290                         data_string_forget(&uid, MDL);
00291                         get_newest_lease(&lease, tmp_lease, next_uid);
00292                         assoc_ip_cnt = get_associated_ips(tmp_lease,
00293                                                           next_uid, 
00294                                                           lease,
00295                                                           assoc_ips, 
00296                                                           nassoc_ips);
00297 
00298                 } else {
00299 
00300                         if (packet->raw->hlen+1 > sizeof(h.hbuf)) {
00301                                 log_info("%s: hardware length too long, "
00302                                          "no reply sent", msgbuf);
00303                                 option_state_dereference(&options, MDL);
00304                                 return;
00305                         }
00306 
00307                         h.hlen = packet->raw->hlen + 1;
00308                         h.hbuf[0] = packet->raw->htype;
00309                         memcpy(&h.hbuf[1], 
00310                                packet->raw->chaddr, 
00311                                packet->raw->hlen);
00312 
00313                         snprintf(dbg_info, 
00314                                  sizeof(dbg_info), 
00315                                  "MAC address %s",
00316                                  print_hw_addr(h.hbuf[0], 
00317                                                h.hlen - 1, 
00318                                                &h.hbuf[1]));
00319 
00320                         find_lease_by_hw_addr(&tmp_lease, h.hbuf, h.hlen, MDL);
00321                         get_newest_lease(&lease, tmp_lease, next_hw);
00322                         assoc_ip_cnt = get_associated_ips(tmp_lease,
00323                                                           next_hw, 
00324                                                           lease,
00325                                                           assoc_ips, 
00326                                                           nassoc_ips);
00327 
00328                 }
00329 
00330                 lease_dereference(&tmp_lease, MDL);
00331 
00332                 if (lease != NULL) {
00333                         memcpy(&packet->raw->ciaddr, 
00334                                lease->ip_addr.iabuf,
00335                                sizeof(packet->raw->ciaddr));
00336                 }
00337 
00338                 /*
00339                  * Log if we have too many IP addresses associated
00340                  * with this client.
00341                  */
00342                 if (want_associated_ip && (assoc_ip_cnt > nassoc_ips)) {
00343                         log_info("%d IP addresses associated with %s, "
00344                                  "only %d sent in reply.",
00345                                  assoc_ip_cnt, dbg_info, nassoc_ips);
00346                 }
00347         }
00348 
00349         /*
00350          * We now know the query target too, so can report this in 
00351          * our log message.
00352          */
00353         snprintf(msgbuf, sizeof(msgbuf), 
00354                 "DHCPLEASEQUERY from %s for %s",
00355                 inet_ntoa(packet->raw->giaddr), dbg_info);
00356 
00357         /*
00358          * Figure our our return type.
00359          */
00360         if (lease == NULL) {
00361                 dhcpMsgType = DHCPLEASEUNKNOWN;
00362                 dhcp_msg_type_name = "DHCPLEASEUNKNOWN";
00363         } else {
00364                 if (lease->binding_state == FTS_ACTIVE) {
00365                         dhcpMsgType = DHCPLEASEACTIVE;
00366                         dhcp_msg_type_name = "DHCPLEASEACTIVE";
00367                 } else {
00368                         dhcpMsgType = DHCPLEASEUNASSIGNED;
00369                         dhcp_msg_type_name = "DHCPLEASEUNASSIGNED";
00370                 }
00371         }
00372 
00373         /* 
00374          * Set options that only make sense if we have an active lease.
00375          */
00376 
00377         if (dhcpMsgType == DHCPLEASEACTIVE)
00378         {
00379                 /*
00380                  * RFC 4388 uses the PRL to request options for the agent to
00381                  * receive that are "about" the client.  It is confusing
00382                  * because in some cases it wants to know what was sent to
00383                  * the client (lease times, adjusted), and in others it wants
00384                  * to know information the client sent.  You're supposed to
00385                  * know this on a case-by-case basis.
00386                  *
00387                  * "Name servers", "domain name", and the like from the relay
00388                  * agent's scope seems less than useful.  Our options are to
00389                  * restart the option cache from the lease's best point of view
00390                  * (execute statements from the lease pool's group), or to
00391                  * simply restart the option cache from empty.
00392                  *
00393                  * I think restarting the option cache from empty best
00394                  * approaches RFC 4388's intent; specific options are included.
00395                  */
00396                 option_state_dereference(&options, MDL);
00397 
00398                 if (!option_state_allocate(&options, MDL)) {
00399                         log_error("%s: out of memory, no reply sent", msgbuf);
00400                         lease_dereference(&lease, MDL);
00401                         return;
00402                 }
00403 
00404                 /* 
00405                  * Set the hardware address fields.
00406                  */
00407 
00408                 packet->raw->hlen = lease->hardware_addr.hlen - 1;
00409                 packet->raw->htype = lease->hardware_addr.hbuf[0];
00410                 memcpy(packet->raw->chaddr, 
00411                        &lease->hardware_addr.hbuf[1], 
00412                        sizeof(packet->raw->chaddr));
00413 
00414                 /*
00415                  * Set client identifier option.
00416                  */
00417                 if (lease->uid_len > 0) {
00418                         if (!add_option(options,
00419                                         DHO_DHCP_CLIENT_IDENTIFIER,
00420                                         lease->uid,
00421                                         lease->uid_len)) {
00422                                 option_state_dereference(&options, MDL);
00423                                 lease_dereference(&lease, MDL);
00424                                 log_info("%s: out of memory, no reply sent",
00425                                          msgbuf);
00426                                 return;
00427                         }
00428                 }
00429 
00430 
00431                 /*
00432                  * Calculate T1 and T2, the times when the client
00433                  * tries to extend its lease on its networking
00434                  * address.
00435                  * These seem to be hard-coded in ISC DHCP, to 0.5 and
00436                  * 0.875 of the lease time.
00437                  */
00438 
00439                 lease_duration = lease->ends - lease->starts;
00440                 time_renewal = lease->starts + 
00441                         (lease_duration / 2);
00442                 time_rebinding = lease->starts + 
00443                         (lease_duration / 2) +
00444                         (lease_duration / 4) +
00445                         (lease_duration / 8);
00446 
00447                 if (time_renewal > cur_time) {
00448                         time_renewal = htonl(time_renewal - cur_time);
00449 
00450                         if (!add_option(options, 
00451                                         DHO_DHCP_RENEWAL_TIME,
00452                                         &time_renewal, 
00453                                         sizeof(time_renewal))) {
00454                                 option_state_dereference(&options, MDL);
00455                                 lease_dereference(&lease, MDL);
00456                                 log_info("%s: out of memory, no reply sent",
00457                                          msgbuf);
00458                                 return;
00459                         }
00460                 }
00461 
00462                 if (time_rebinding > cur_time) {
00463                         time_rebinding = htonl(time_rebinding - cur_time);
00464 
00465                         if (!add_option(options, 
00466                                         DHO_DHCP_REBINDING_TIME,
00467                                         &time_rebinding, 
00468                                         sizeof(time_rebinding))) {
00469                                 option_state_dereference(&options, MDL);
00470                                 lease_dereference(&lease, MDL);
00471                                 log_info("%s: out of memory, no reply sent",
00472                                          msgbuf);
00473                                 return;
00474                         }
00475                 }
00476 
00477                 if (lease->ends > cur_time) {
00478                         time_expiry = htonl(lease->ends - cur_time);
00479 
00480                         if (!add_option(options, 
00481                                         DHO_DHCP_LEASE_TIME,
00482                                         &time_expiry, 
00483                                         sizeof(time_expiry))) {
00484                                 option_state_dereference(&options, MDL);
00485                                 lease_dereference(&lease, MDL);
00486                                 log_info("%s: out of memory, no reply sent",
00487                                          msgbuf);
00488                                 return;
00489                         }
00490                 }
00491 
00492                 /* Supply the Vendor-Class-Identifier. */
00493                 if (lease->scope != NULL) {
00494                         struct data_string vendor_class;
00495 
00496                         memset(&vendor_class, 0, sizeof(vendor_class));
00497 
00498                         if (find_bound_string(&vendor_class, lease->scope,
00499                                               "vendor-class-identifier")) {
00500                                 if (!add_option(options,
00501                                                 DHO_VENDOR_CLASS_IDENTIFIER,
00502                                                 (void *)vendor_class.data,
00503                                                 vendor_class.len)) {
00504                                         option_state_dereference(&options,
00505                                                                  MDL);
00506                                         lease_dereference(&lease, MDL);
00507                                         log_error("%s: error adding vendor "
00508                                                   "class identifier, no reply "
00509                                                   "sent", msgbuf);
00510                                         data_string_forget(&vendor_class, MDL);
00511                                         return;
00512                                 }
00513                                 data_string_forget(&vendor_class, MDL);
00514                         }
00515                 }
00516 
00517                 /*
00518                  * Set the relay agent info.
00519                  *
00520                  * Note that because agent info is appended without regard
00521                  * to the PRL in cons_options(), this will be sent as the
00522                  * last option in the packet whether it is listed on PRL or
00523                  * not.
00524                  */
00525 
00526                 if (lease->agent_options != NULL) {
00527                         int idx = agent_universe.index;
00528                         struct option_chain_head **tmp1 = 
00529                                 (struct option_chain_head **)
00530                                 &(options->universes[idx]);
00531                                 struct option_chain_head *tmp2 = 
00532                                 (struct option_chain_head *)
00533                                 lease->agent_options;
00534 
00535                         option_chain_head_reference(tmp1, tmp2, MDL);
00536                 }
00537 
00538                 /* 
00539                  * Set the client last transaction time.
00540                  * We check to make sure we have a timestamp. For
00541                  * lease files that were saved before running a 
00542                  * timestamp-aware version of the server, this may
00543                  * not be set.
00544                  */
00545 
00546                 if (lease->cltt != MIN_TIME) {
00547                         if (cur_time > lease->cltt) {
00548                                 client_last_transaction_time = 
00549                                         htonl(cur_time - lease->cltt);
00550                         } else {
00551                                 client_last_transaction_time = htonl(0);
00552                         }
00553                         if (!add_option(options, 
00554                                         DHO_CLIENT_LAST_TRANSACTION_TIME,
00555                                         &client_last_transaction_time,
00556                                         sizeof(client_last_transaction_time))) {
00557                                 option_state_dereference(&options, MDL);
00558                                 lease_dereference(&lease, MDL);
00559                                 log_info("%s: out of memory, no reply sent",
00560                                          msgbuf);
00561                                 return;
00562                         }
00563                 }
00564 
00565                 /*
00566                  * Set associated IPs, if requested and there are some.
00567                  */
00568                 if (want_associated_ip && (assoc_ip_cnt > 0)) {
00569                         if (!add_option(options, 
00570                                         DHO_ASSOCIATED_IP,
00571                                         assoc_ips,
00572                                         assoc_ip_cnt * sizeof(assoc_ips[0]))) {
00573                                 option_state_dereference(&options, MDL);
00574                                 lease_dereference(&lease, MDL);
00575                                 log_info("%s: out of memory, no reply sent",
00576                                          msgbuf);
00577                                 return;
00578                         }
00579                 }
00580         }
00581 
00582         /* 
00583          * Set the message type.
00584          */
00585 
00586         packet->raw->op = BOOTREPLY;
00587 
00588         /*
00589          * Set DHCP message type.
00590          */
00591         if (!add_option(options, 
00592                         DHO_DHCP_MESSAGE_TYPE,
00593                         &dhcpMsgType, 
00594                         sizeof(dhcpMsgType))) {
00595                 option_state_dereference(&options, MDL);
00596                 lease_dereference(&lease, MDL);
00597                 log_info("%s: error adding option, no reply sent", msgbuf);
00598                 return;
00599         }
00600 
00601         /*
00602          * Log the message we've received.
00603          */
00604         log_info("%s", msgbuf);
00605 
00606         /*
00607          * Figure out which address to use to send from.
00608          */
00609         get_server_source_address(&siaddr, options, options, packet);
00610 
00611         /* 
00612          * Set up the option buffer.
00613          */
00614 
00615         memset(&prl, 0, sizeof(prl));
00616         oc = lookup_option(&dhcp_universe, options, 
00617                            DHO_DHCP_PARAMETER_REQUEST_LIST);
00618         if (oc != NULL) {
00619                 evaluate_option_cache(&prl, 
00620                                       packet, 
00621                                       NULL,
00622                                       NULL,
00623                                       packet->options,
00624                                       options,
00625                                       &global_scope,
00626                                       oc,
00627                                       MDL);
00628         }
00629         if (prl.len > 0) {
00630                 prl_ptr = &prl;
00631         } else {
00632                 prl_ptr = NULL;
00633         }
00634 
00635         packet->packet_length = cons_options(packet, 
00636                                              packet->raw, 
00637                                              lease,
00638                                              NULL,
00639                                              0,
00640                                              packet->options,
00641                                              options,
00642                                              &global_scope,
00643                                              0,
00644                                              0,
00645                                              0, 
00646                                              prl_ptr,
00647                                              NULL);
00648 
00649         data_string_forget(&prl, MDL);  /* SK: safe, even if empty */
00650         option_state_dereference(&options, MDL);
00651         lease_dereference(&lease, MDL);
00652 
00653         to.sin_family = AF_INET;
00654 #ifdef HAVE_SA_LEN
00655         to.sin_len = sizeof(to);
00656 #endif
00657         memset(to.sin_zero, 0, sizeof(to.sin_zero));
00658 
00659         /* 
00660          * Leasequery packets are be sent to the gateway address.
00661          */
00662         to.sin_addr = packet->raw->giaddr;
00663         if (packet->raw->giaddr.s_addr != htonl(INADDR_LOOPBACK)) {
00664                 to.sin_port = local_port;
00665         } else {
00666                 to.sin_port = remote_port; /* XXXSK: For debugging. */
00667         }
00668 
00669         /* 
00670          * The fallback_interface lets us send with a real IP
00671          * address. The packet interface sends from all-zeros.
00672          */
00673         if (fallback_interface != NULL) {
00674                 interface = fallback_interface;
00675         } else {
00676                 interface = packet->interface;
00677         }
00678 
00679         /*
00680          * Report what we're sending.
00681          */
00682         log_info("%s to %s for %s (%d associated IPs)",
00683                 dhcp_msg_type_name, 
00684                 inet_ntoa(to.sin_addr), dbg_info, assoc_ip_cnt);
00685 
00686         send_packet(interface,
00687                     NULL,
00688                     packet->raw, 
00689                     packet->packet_length,
00690                     siaddr,
00691                     &to,
00692                     NULL);
00693 }
00694 
00695 #ifdef DHCPv6
00696 
00697 /*
00698  * TODO: RFC5007 query-by-clientid.
00699  *
00700  * TODO: RFC5007 look at the pools according to the link-address.
00701  *
00702  * TODO: get fixed leases too.
00703  *
00704  * TODO: RFC5007 ORO in query-options.
00705  *
00706  * TODO: RFC5007 lq-relay-data.
00707  *
00708  * TODO: RFC5007 lq-client-link.
00709  *
00710  * Note: the code is still nearly compliant and usable for the target
00711  * case with these missing features!
00712  */
00713 
00714 /*
00715  * The structure to handle a leasequery.
00716  */
00717 struct lq6_state {
00718         struct packet *packet;
00719         struct data_string client_id;
00720         struct data_string server_id;
00721         struct data_string lq_query;
00722         uint8_t query_type;
00723         struct in6_addr link_addr;
00724         struct option_state *query_opts;
00725 
00726         struct option_state *reply_opts;
00727         unsigned cursor;
00728         union reply_buffer {
00729                 unsigned char data[65536];
00730                 struct dhcpv6_packet reply;
00731         } buf;
00732 };
00733 
00734 /*
00735  * Options that we want to send.
00736  */
00737 static const int required_opts_lq[] = {
00738         D6O_CLIENTID,
00739         D6O_SERVERID,
00740         D6O_STATUS_CODE,
00741         D6O_CLIENT_DATA,
00742         D6O_LQ_RELAY_DATA,
00743         D6O_LQ_CLIENT_LINK,
00744         0
00745 };
00746 static const int required_opt_CLIENT_DATA[] = {
00747         D6O_CLIENTID,
00748         D6O_IAADDR,
00749         D6O_IAPREFIX,
00750         D6O_CLT_TIME,
00751         0
00752 };
00753 
00754 /*
00755  * Get the lq-query option from the packet.
00756  */
00757 static isc_result_t
00758 get_lq_query(struct lq6_state *lq)
00759 {
00760         struct data_string *lq_query = &lq->lq_query;
00761         struct packet *packet = lq->packet;
00762         struct option_cache *oc;
00763 
00764         /*
00765          * Verify our lq_query structure is empty.
00766          */
00767         if ((lq_query->data != NULL) || (lq_query->len != 0)) {
00768                 return DHCP_R_INVALIDARG;
00769         }
00770 
00771         oc = lookup_option(&dhcpv6_universe, packet->options, D6O_LQ_QUERY);
00772         if (oc == NULL) {
00773                 return ISC_R_NOTFOUND;
00774         }
00775 
00776         if (!evaluate_option_cache(lq_query, packet, NULL, NULL,
00777                                    packet->options, NULL,
00778                                    &global_scope, oc, MDL)) {
00779                 return ISC_R_FAILURE;
00780         }
00781 
00782         return ISC_R_SUCCESS;
00783 }
00784 
00785 /*
00786  * Message validation, RFC 5007 section 4.2.1:
00787  *  dhcpv6.c:valid_client_msg() - unicast + lq-query option.
00788  */
00789 static int
00790 valid_query_msg(struct lq6_state *lq) {
00791         struct packet *packet = lq->packet;
00792         int ret_val = 0;
00793         struct option_cache *oc;
00794 
00795         /* INSIST((lq != NULL) || (packet != NULL)); */
00796 
00797         switch (get_client_id(packet, &lq->client_id)) {
00798                 case ISC_R_SUCCESS:
00799                         break;
00800                 case ISC_R_NOTFOUND:
00801                         log_debug("Discarding %s from %s; "
00802                                   "client identifier missing", 
00803                                   dhcpv6_type_names[packet->dhcpv6_msg_type],
00804                                   piaddr(packet->client_addr));
00805                         goto exit;
00806                 default:
00807                         log_error("Error processing %s from %s; "
00808                                   "unable to evaluate Client Identifier",
00809                                   dhcpv6_type_names[packet->dhcpv6_msg_type],
00810                                   piaddr(packet->client_addr));
00811                         goto exit;
00812         }
00813 
00814         oc = lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID);
00815         if (oc != NULL) {
00816                 if (evaluate_option_cache(&lq->server_id, packet, NULL, NULL,
00817                                           packet->options, NULL, 
00818                                           &global_scope, oc, MDL)) {
00819                         log_debug("Discarding %s from %s; " 
00820                                   "server identifier found "
00821                                   "(CLIENTID %s, SERVERID %s)", 
00822                                   dhcpv6_type_names[packet->dhcpv6_msg_type],
00823                                   piaddr(packet->client_addr),
00824                                   print_hex_1(lq->client_id.len, 
00825                                               lq->client_id.data, 60),
00826                                   print_hex_2(lq->server_id.len,
00827                                               lq->server_id.data, 60));
00828                 } else {
00829                         log_debug("Discarding %s from %s; " 
00830                                   "server identifier found "
00831                                   "(CLIENTID %s)", 
00832                                   dhcpv6_type_names[packet->dhcpv6_msg_type],
00833                                   print_hex_1(lq->client_id.len, 
00834                                               lq->client_id.data, 60),
00835                                   piaddr(packet->client_addr));
00836                 }
00837                 goto exit;
00838         }
00839 
00840         switch (get_lq_query(lq)) {
00841                 case ISC_R_SUCCESS:
00842                         break;
00843                 case ISC_R_NOTFOUND:
00844                         log_debug("Discarding %s from %s; lq-query missing",
00845                                   dhcpv6_type_names[packet->dhcpv6_msg_type],
00846                                   piaddr(packet->client_addr));
00847                         goto exit;
00848                 default:
00849                         log_error("Error processing %s from %s; "
00850                                   "unable to evaluate LQ-Query",
00851                                   dhcpv6_type_names[packet->dhcpv6_msg_type],
00852                                   piaddr(packet->client_addr));
00853                         goto exit;
00854         }
00855 
00856         /* looks good */
00857         ret_val = 1;
00858 
00859 exit:
00860         if (!ret_val) {
00861                 if (lq->client_id.len > 0) {
00862                         data_string_forget(&lq->client_id, MDL);
00863                 }
00864                 if (lq->server_id.len > 0) {
00865                         data_string_forget(&lq->server_id, MDL);
00866                 }
00867                 if (lq->lq_query.len > 0) {
00868                         data_string_forget(&lq->lq_query, MDL);
00869                 }
00870         }
00871         return ret_val;
00872 }
00873 
00874 /*
00875  * Set an error in a status-code option (from set_status_code).
00876  */
00877 static int
00878 set_error(struct lq6_state *lq, u_int16_t code, const char *message) {
00879         struct data_string d;
00880         int ret_val;
00881 
00882         memset(&d, 0, sizeof(d));
00883         d.len = sizeof(code) + strlen(message);
00884         if (!buffer_allocate(&d.buffer, d.len, MDL)) {
00885                 log_fatal("set_error: no memory for status code.");
00886         }
00887         d.data = d.buffer->data;
00888         putUShort(d.buffer->data, code);
00889         memcpy(d.buffer->data + sizeof(code), message, d.len - sizeof(code));
00890         if (!save_option_buffer(&dhcpv6_universe, lq->reply_opts,
00891                                 d.buffer, (unsigned char *)d.data, d.len, 
00892                                 D6O_STATUS_CODE, 0)) {
00893                 log_error("set_error: error saving status code.");
00894                 ret_val = 0;
00895         } else {
00896                 ret_val = 1;
00897         }
00898         data_string_forget(&d, MDL);
00899         return ret_val;
00900 }
00901 
00902 /*
00903  * Process a by-address lease query.
00904  */
00905 static int
00906 process_lq_by_address(struct lq6_state *lq) {
00907         struct packet *packet = lq->packet;
00908         struct option_cache *oc;
00909         struct ipv6_pool *pool = NULL;
00910         struct data_string data;
00911         struct in6_addr addr;
00912         struct iasubopt *iaaddr = NULL;
00913         struct option_state *opt_state = NULL;
00914         u_int32_t lifetime;
00915         unsigned opt_cursor;
00916         int ret_val = 0;
00917 
00918         /*
00919          * Get the IAADDR.
00920          */
00921         oc = lookup_option(&dhcpv6_universe, lq->query_opts, D6O_IAADDR);
00922         if (oc == NULL) {
00923                 if (!set_error(lq, STATUS_MalformedQuery,
00924                                "No OPTION_IAADDR.")) {
00925                         log_error("process_lq_by_address: unable "
00926                                   "to set MalformedQuery status code.");
00927                         return 0;
00928                 }
00929                 return 1;
00930         }
00931         memset(&data, 0, sizeof(data));
00932         if (!evaluate_option_cache(&data, packet,
00933                                    NULL, NULL,
00934                                    lq->query_opts, NULL,
00935                                    &global_scope, oc, MDL) ||
00936             (data.len < IAADDR_OFFSET)) {
00937                 log_error("process_lq_by_address: error evaluating IAADDR.");
00938                 goto exit;
00939         }
00940         memcpy(&addr, data.data, sizeof(addr));
00941         data_string_forget(&data, MDL);
00942 
00943         /*
00944          * Find the lease.
00945          * Note the RFC 5007 says to use the link-address to find the link
00946          * or the ia-aadr when it is :: but in any case the ia-addr has
00947          * to be on the link, so we ignore the link-address here.
00948          */
00949         if (find_ipv6_pool(&pool, D6O_IA_NA, &addr) != ISC_R_SUCCESS) {
00950                 if (!set_error(lq, STATUS_NotConfigured,
00951                                "Address not in a pool.")) {
00952                         log_error("process_lq_by_address: unable "
00953                                   "to set NotConfigured status code.");
00954                         goto exit;
00955                 }
00956                 ret_val = 1;
00957                 goto exit;
00958         }
00959         if (iasubopt_hash_lookup(&iaaddr, pool->leases, &addr,
00960                                  sizeof(addr), MDL) == 0) {
00961                 ret_val = 1;
00962                 goto exit;
00963         }
00964         if ((iaaddr == NULL) || (iaaddr->state != FTS_ACTIVE) ||
00965             (iaaddr->ia == NULL) || (iaaddr->ia->iaid_duid.len <= 4)) {
00966                 ret_val = 1;
00967                 goto exit;
00968         }
00969 
00970         /*
00971          * Build the client-data option (with client-id, ia-addr and clt-time).
00972          */
00973         if (!option_state_allocate(&opt_state, MDL)) {
00974                 log_error("process_lq_by_address: "
00975                           "no memory for option state.");
00976                 goto exit;
00977         }
00978 
00979         data_string_copy(&data, &iaaddr->ia->iaid_duid, MDL);
00980         data.data += 4;
00981         data.len -= 4;
00982         if (!save_option_buffer(&dhcpv6_universe, opt_state,
00983                                 NULL, (unsigned char *)data.data, data.len,
00984                                 D6O_CLIENTID, 0)) {
00985                 log_error("process_lq_by_address: error saving client ID.");
00986                 goto exit;
00987         }
00988         data_string_forget(&data, MDL);
00989 
00990         data.len = IAADDR_OFFSET;
00991         if (!buffer_allocate(&data.buffer, data.len, MDL)) {
00992                 log_error("process_lq_by_address: no memory for ia-addr.");
00993                 goto exit;
00994         }
00995         data.data = data.buffer->data;
00996         memcpy(data.buffer->data, &iaaddr->addr, 16);
00997         lifetime = iaaddr->prefer;
00998         putULong(data.buffer->data + 16, lifetime);
00999         lifetime = iaaddr->valid;
01000         putULong(data.buffer->data + 20, lifetime);
01001         if (!save_option_buffer(&dhcpv6_universe, opt_state,
01002                                 NULL, (unsigned char *)data.data, data.len,
01003                                 D6O_IAADDR, 0)) {
01004                 log_error("process_lq_by_address: error saving ia-addr.");
01005                 goto exit;
01006         }
01007         data_string_forget(&data, MDL);
01008 
01009         lifetime = htonl(iaaddr->ia->cltt);
01010         if (!save_option_buffer(&dhcpv6_universe, opt_state,
01011                                 NULL, (unsigned char *)&lifetime, 4,
01012                                 D6O_CLT_TIME, 0)) {
01013                 log_error("process_lq_by_address: error saving clt time.");
01014                 goto exit;
01015         }
01016 
01017         /*
01018          * Store the client-data option.
01019          */
01020         opt_cursor = lq->cursor;
01021         putUShort(lq->buf.data + lq->cursor, (unsigned)D6O_CLIENT_DATA);
01022         lq->cursor += 2;
01023         /* Skip option length. */
01024         lq->cursor += 2;
01025 
01026         lq->cursor += store_options6((char *)lq->buf.data + lq->cursor,
01027                                      sizeof(lq->buf) - lq->cursor,
01028                                      opt_state, lq->packet,
01029                                      required_opt_CLIENT_DATA, NULL);
01030         /* Reset the length. */
01031         putUShort(lq->buf.data + opt_cursor + 2,
01032                   lq->cursor - (opt_cursor + 4));
01033 
01034         /* Done. */
01035         ret_val = 1;
01036 
01037      exit:
01038         if (data.data != NULL)
01039                 data_string_forget(&data, MDL);
01040         if (pool != NULL)
01041                 ipv6_pool_dereference(&pool, MDL);
01042         if (iaaddr != NULL)
01043                 iasubopt_dereference(&iaaddr, MDL);
01044         if (opt_state != NULL)
01045                 option_state_dereference(&opt_state, MDL);
01046         return ret_val;
01047 }
01048 
01049 
01050 /*
01051  * Process a lease query.
01052  */
01053 void
01054 dhcpv6_leasequery(struct data_string *reply_ret, struct packet *packet) {
01055         static struct lq6_state lq;
01056         struct option_cache *oc;
01057         int allow_lq;
01058 
01059         /*
01060          * Initialize the lease query state.
01061          */
01062         lq.packet = NULL;
01063         memset(&lq.client_id, 0, sizeof(lq.client_id));
01064         memset(&lq.server_id, 0, sizeof(lq.server_id));
01065         memset(&lq.lq_query, 0, sizeof(lq.lq_query));
01066         lq.query_opts = NULL;
01067         lq.reply_opts = NULL;
01068         packet_reference(&lq.packet, packet, MDL);
01069 
01070         /*
01071          * Validate our input.
01072          */
01073         if (!valid_query_msg(&lq)) {
01074                 goto exit;
01075         }
01076 
01077         /*
01078          * Prepare our reply.
01079          */
01080         if (!option_state_allocate(&lq.reply_opts, MDL)) {
01081                 log_error("dhcpv6_leasequery: no memory for option state.");
01082                 goto exit;
01083         }
01084         execute_statements_in_scope(NULL, lq.packet, NULL, NULL,
01085                                     lq.packet->options, lq.reply_opts,
01086                                     &global_scope, root_group, NULL, NULL);
01087 
01088         lq.buf.reply.msg_type = DHCPV6_LEASEQUERY_REPLY;
01089 
01090         memcpy(lq.buf.reply.transaction_id,
01091                lq.packet->dhcpv6_transaction_id,
01092                sizeof(lq.buf.reply.transaction_id));
01093 
01094         /* 
01095          * Because LEASEQUERY has some privacy concerns, default to deny.
01096          */
01097         allow_lq = 0;
01098 
01099         /*
01100          * See if we are authorized to do LEASEQUERY.
01101          */
01102         oc = lookup_option(&server_universe, lq.reply_opts, SV_LEASEQUERY);
01103         if (oc != NULL) {
01104                 allow_lq = evaluate_boolean_option_cache(NULL,
01105                                                          lq.packet,
01106                                                          NULL, NULL,
01107                                                          lq.packet->options,
01108                                                          lq.reply_opts,
01109                                                          &global_scope,
01110                                                          oc, MDL);
01111         }
01112 
01113         if (!allow_lq) {
01114                 log_info("dhcpv6_leasequery: not allowed, query ignored.");
01115                 goto exit;
01116         }
01117             
01118         /*
01119          * Same than transmission of REPLY message in RFC 3315:
01120          *  server-id
01121          *  client-id
01122          */
01123 
01124         oc = lookup_option(&dhcpv6_universe, lq.reply_opts, D6O_SERVERID);
01125         if (oc == NULL) {
01126                 /* If not already in options, get from query then global. */
01127                 if (lq.server_id.data == NULL)
01128                         copy_server_duid(&lq.server_id, MDL);
01129                 if (!save_option_buffer(&dhcpv6_universe,
01130                                         lq.reply_opts,
01131                                         NULL,
01132                                         (unsigned char *)lq.server_id.data,
01133                                         lq.server_id.len, 
01134                                         D6O_SERVERID,
01135                                         0)) {
01136                         log_error("dhcpv6_leasequery: "
01137                                   "error saving server identifier.");
01138                         goto exit;
01139                 }
01140         }
01141 
01142         if (!save_option_buffer(&dhcpv6_universe,
01143                                 lq.reply_opts,
01144                                 lq.client_id.buffer,
01145                                 (unsigned char *)lq.client_id.data,
01146                                 lq.client_id.len,
01147                                 D6O_CLIENTID,
01148                                 0)) {
01149                 log_error("dhcpv6_leasequery: "
01150                           "error saving client identifier.");
01151                 goto exit;
01152         }
01153 
01154         lq.cursor = 4;
01155 
01156         /*
01157          * Decode the lq-query option.
01158          */
01159 
01160         if (lq.lq_query.len <= LQ_QUERY_OFFSET) {
01161                 if (!set_error(&lq, STATUS_MalformedQuery,
01162                                "OPTION_LQ_QUERY too short.")) {
01163                         log_error("dhcpv6_leasequery: unable "
01164                                   "to set MalformedQuery status code.");
01165                         goto exit;
01166                 }
01167                 goto done;
01168         }
01169 
01170         lq.query_type = lq.lq_query.data [0];
01171         memcpy(&lq.link_addr, lq.lq_query.data + 1, sizeof(lq.link_addr));
01172         switch (lq.query_type) {
01173                 case LQ6QT_BY_ADDRESS:
01174                         break;
01175                 case LQ6QT_BY_CLIENTID:
01176                         if (!set_error(&lq, STATUS_UnknownQueryType,
01177                                        "QUERY_BY_CLIENTID not supported.")) {
01178                                 log_error("dhcpv6_leasequery: unable to "
01179                                           "set UnknownQueryType status code.");
01180                                 goto exit;
01181                         }
01182                         goto done;
01183                 default:
01184                         if (!set_error(&lq, STATUS_UnknownQueryType,
01185                                        "Unknown query-type.")) {
01186                                 log_error("dhcpv6_leasequery: unable to "
01187                                           "set UnknownQueryType status code.");
01188                                 goto exit;
01189                         }
01190                         goto done;
01191         }
01192 
01193         if (!option_state_allocate(&lq.query_opts, MDL)) {
01194                 log_error("dhcpv6_leasequery: no memory for option state.");
01195                 goto exit;
01196         }
01197         if (!parse_option_buffer(lq.query_opts,
01198                                  lq.lq_query.data + LQ_QUERY_OFFSET,
01199                                  lq.lq_query.len - LQ_QUERY_OFFSET,
01200                                  &dhcpv6_universe)) {
01201                 log_error("dhcpv6_leasequery: error parsing query-options.");
01202                 if (!set_error(&lq, STATUS_MalformedQuery,
01203                                "Bad query-options.")) {
01204                         log_error("dhcpv6_leasequery: unable "
01205                                   "to set MalformedQuery status code.");
01206                         goto exit;
01207                 }
01208                 goto done;
01209         }
01210 
01211         /* Do it. */
01212         if (!process_lq_by_address(&lq))
01213                 goto exit;
01214 
01215       done:
01216         /* Store the options. */
01217         lq.cursor += store_options6((char *)lq.buf.data + lq.cursor,
01218                                     sizeof(lq.buf) - lq.cursor,
01219                                     lq.reply_opts,
01220                                     lq.packet,
01221                                     required_opts_lq,
01222                                     NULL);
01223 
01224         /* Return our reply to the caller. */
01225         reply_ret->len = lq.cursor;
01226         reply_ret->buffer = NULL;
01227         if (!buffer_allocate(&reply_ret->buffer, lq.cursor, MDL)) {
01228                 log_fatal("dhcpv6_leasequery: no memory to store Reply.");
01229         }
01230         memcpy(reply_ret->buffer->data, lq.buf.data, lq.cursor);
01231         reply_ret->data = reply_ret->buffer->data;
01232 
01233       exit:
01234         /* Cleanup. */
01235         if (lq.packet != NULL)
01236                 packet_dereference(&lq.packet, MDL);
01237         if (lq.client_id.data != NULL)
01238                 data_string_forget(&lq.client_id, MDL);
01239         if (lq.server_id.data != NULL)
01240                 data_string_forget(&lq.server_id, MDL);
01241         if (lq.lq_query.data != NULL)
01242                 data_string_forget(&lq.lq_query, MDL);
01243         if (lq.query_opts != NULL)
01244                 option_state_dereference(&lq.query_opts, MDL);
01245         if (lq.reply_opts != NULL)
01246                 option_state_dereference(&lq.reply_opts, MDL);
01247 }
01248 
01249 #endif /* DHCPv6 */

Generated on 5 Apr 2014 for ISC DHCP by  doxygen 1.6.1