1: /*
2: * Copyright (C) 2006-2007 by Internet Systems Consortium, Inc. ("ISC")
3: *
4: * Permission to use, copy, modify, and distribute this software for any
5: * purpose with or without fee is hereby granted, provided that the above
6: * copyright notice and this permission notice appear in all copies.
7: *
8: * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9: * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10: * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11: * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12: * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13: * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14: * PERFORMANCE OF THIS SOFTWARE.
15: */
16:
17: #include "dhcpd.h"
18:
19: /*
20: * TODO: RFC4388 specifies that the server SHOULD return the same
21: * options it would for a DHCREQUEST message, if no Parameter
22: * Request List option (option 55) is passed. We do not do that.
23: *
24: * TODO: RFC4388 specifies the creation of a "non-sensitive options"
25: * configuration list, and that these SHOULD be returned. We
26: * have no such list.
27: *
28: * TODO: RFC4388 says the server SHOULD use RFC3118, "Authentication
29: * for DHCP Messages".
30: *
31: * TODO: RFC4388 specifies that you SHOULD insure that you cannot be
32: * DoS'ed by DHCPLEASEQUERY message.
33: */
34:
35: /*
36: * If you query by hardware address or by client ID, then you may have
37: * more than one IP address for your query argument. We need to do two
38: * things:
39: *
40: * 1. Find the most recent lease.
41: * 2. Find all additional IP addresses for the query argument.
42: *
43: * We do this by looking through all of the leases associated with a
44: * given hardware address or client ID. We use the cltt (client last
45: * transaction time) of the lease, which only has a resolution of one
46: * second, so we might not actually give the very latest IP.
47: */
48:
49: static struct lease*
50: next_hw(const struct lease *lease) {
51: /* INSIST(lease != NULL); */
52: return lease->n_hw;
53: }
54:
55: static struct lease*
56: next_uid(const struct lease *lease) {
57: /* INSIST(lease != NULL); */
58: return lease->n_uid;
59: }
60:
61: void
62: get_newest_lease(struct lease **retval,
63: struct lease *lease,
64: struct lease *(*next)(const struct lease *)) {
65:
66: struct lease *p;
67: struct lease *newest;
68:
69: /* INSIST(newest != NULL); */
70: /* INSIST(next != NULL); */
71:
72: *retval = NULL;
73:
74: if (lease == NULL) {
75: return;
76: }
77:
78: newest = lease;
79: for (p=next(lease); p != NULL; p=next(p)) {
80: if (newest->binding_state == FTS_ACTIVE) {
81: if ((p->binding_state == FTS_ACTIVE) &&
82: (p->cltt > newest->cltt)) {
83: newest = p;
84: }
85: } else {
86: if (p->ends > newest->ends) {
87: newest = p;
88: }
89: }
90: }
91:
92: lease_reference(retval, newest, MDL);
93: }
94:
95: static int
96: get_associated_ips(const struct lease *lease,
97: struct lease *(*next)(const struct lease *),
98: const struct lease *newest,
99: u_int32_t *associated_ips,
100: unsigned int associated_ips_size) {
101:
102: const struct lease *p;
103: int cnt;
104:
105: /* INSIST(next != NULL); */
106: /* INSIST(associated_ips != NULL); */
107:
108: if (lease == NULL) {
109: return 0;
110: }
111:
112: cnt = 0;
113: for (p=lease; p != NULL; p=next(p)) {
114: if ((p->binding_state == FTS_ACTIVE) && (p != newest)) {
115: if (cnt < associated_ips_size) {
116: memcpy(&associated_ips[cnt],
117: p->ip_addr.iabuf,
118: sizeof(associated_ips[cnt]));
119: }
120: cnt++;
121: }
122: }
123: return cnt;
124: }
125:
126:
127: void
128: dhcpleasequery(struct packet *packet, int ms_nulltp) {
129: char msgbuf[256];
130: char dbg_info[128];
131: struct iaddr cip;
132: struct iaddr gip;
133: struct data_string uid;
134: struct hardware h;
135: struct lease *tmp_lease;
136: struct lease *lease;
137: int want_associated_ip;
138: int assoc_ip_cnt;
139: u_int32_t assoc_ips[40]; /* XXXSK: arbitrary maximum number of IPs */
140: const int nassoc_ips = sizeof(assoc_ips) / sizeof(assoc_ips[0]);
141:
142: unsigned char dhcpMsgType;
143: const char *dhcp_msg_type_name;
144: struct subnet *subnet;
145: struct group *relay_group;
146: struct option_state *options;
147: struct option_cache *oc;
148: int allow_leasequery;
149: int ignorep;
150: u_int32_t lease_duration;
151: u_int32_t time_renewal;
152: u_int32_t time_rebinding;
153: u_int32_t time_expiry;
154: u_int32_t client_last_transaction_time;
155: struct sockaddr_in to;
156: struct in_addr siaddr;
157: struct data_string prl;
158: struct data_string *prl_ptr;
159:
160: int i;
161: struct interface_info *interface;
162:
163: /* INSIST(packet != NULL); */
164:
165: /*
166: * Prepare log information.
167: */
168: snprintf(msgbuf, sizeof(msgbuf),
169: "DHCPLEASEQUERY from %s", inet_ntoa(packet->raw->giaddr));
170:
171: /*
172: * We can't reply if there is no giaddr field.
173: */
174: if (!packet->raw->giaddr.s_addr) {
175: log_info("%s: missing giaddr, ciaddr is %s, no reply sent",
176: msgbuf, inet_ntoa(packet->raw->ciaddr));
177: return;
178: }
179:
180: /*
181: * Initially we use the 'giaddr' subnet options scope to determine if
182: * the giaddr-identified relay agent is permitted to perform a
183: * leasequery. The subnet is not required, and may be omitted, in
184: * which case we are essentially interrogating the root options class
185: * to find a globally permit.
186: */
187: gip.len = sizeof(packet->raw->giaddr);
188: memcpy(gip.iabuf, &packet->raw->giaddr, sizeof(packet->raw->giaddr));
189:
190: subnet = NULL;
191: find_subnet(&subnet, gip, MDL);
192: if (subnet != NULL)
193: relay_group = subnet->group;
194: else
195: relay_group = root_group;
196:
197: subnet_dereference(&subnet, MDL);
198:
199: options = NULL;
200: if (!option_state_allocate(&options, MDL)) {
201: log_error("No memory for option state.");
202: log_info("%s: out of memory, no reply sent", msgbuf);
203: return;
204: }
205:
206: execute_statements_in_scope(NULL,
207: packet,
208: NULL,
209: NULL,
210: packet->options,
211: options,
212: &global_scope,
213: relay_group,
214: NULL);
215:
216: for (i=packet->class_count-1; i>=0; i--) {
217: execute_statements_in_scope(NULL,
218: packet,
219: NULL,
220: NULL,
221: packet->options,
222: options,
223: &global_scope,
224: packet->classes[i]->group,
225: relay_group);
226: }
227:
228: /*
229: * Because LEASEQUERY has some privacy concerns, default to deny.
230: */
231: allow_leasequery = 0;
232:
233: /*
234: * See if we are authorized to do LEASEQUERY.
235: */
236: oc = lookup_option(&server_universe, options, SV_LEASEQUERY);
237: if (oc != NULL) {
238: allow_leasequery = evaluate_boolean_option_cache(&ignorep,
239: packet, NULL, NULL, packet->options,
240: options, &global_scope, oc, MDL);
241: }
242:
243: if (!allow_leasequery) {
244: log_info("%s: LEASEQUERY not allowed, query ignored", msgbuf);
245: option_state_dereference(&options, MDL);
246: return;
247: }
248:
249:
250: /*
251: * Copy out the client IP address.
252: */
253: cip.len = sizeof(packet->raw->ciaddr);
254: memcpy(cip.iabuf, &packet->raw->ciaddr, sizeof(packet->raw->ciaddr));
255:
256: /*
257: * If the client IP address is valid (not all zero), then we
258: * are looking for information about that IP address.
259: */
260: assoc_ip_cnt = 0;
261: lease = tmp_lease = NULL;
262: if (memcmp(cip.iabuf, "\0\0\0", 4)) {
263:
264: want_associated_ip = 0;
265:
266: snprintf(dbg_info, sizeof(dbg_info), "IP %s", piaddr(cip));
267: find_lease_by_ip_addr(&lease, cip, MDL);
268:
269:
270: } else {
271:
272: want_associated_ip = 1;
273:
274: /*
275: * If the client IP address is all zero, then we will
276: * either look up by the client identifier (if we have
277: * one), or by the MAC address.
278: */
279:
280: memset(&uid, 0, sizeof(uid));
281: if (get_option(&uid,
282: &dhcp_universe,
283: packet,
284: NULL,
285: NULL,
286: packet->options,
287: NULL,
288: packet->options,
289: &global_scope,
290: DHO_DHCP_CLIENT_IDENTIFIER,
291: MDL)) {
292:
293: snprintf(dbg_info,
294: sizeof(dbg_info),
295: "client-id %s",
296: print_hex_1(uid.len, uid.data, 60));
297:
298: find_lease_by_uid(&tmp_lease, uid.data, uid.len, MDL);
299: data_string_forget(&uid, MDL);
300: get_newest_lease(&lease, tmp_lease, next_uid);
301: assoc_ip_cnt = get_associated_ips(tmp_lease,
302: next_uid,
303: lease,
304: assoc_ips,
305: nassoc_ips);
306:
307: } else {
308:
309: if (packet->raw->hlen+1 > sizeof(h.hbuf)) {
310: log_info("%s: hardware length too long, "
311: "no reply sent", msgbuf);
312: option_state_dereference(&options, MDL);
313: return;
314: }
315:
316: h.hlen = packet->raw->hlen + 1;
317: h.hbuf[0] = packet->raw->htype;
318: memcpy(&h.hbuf[1],
319: packet->raw->chaddr,
320: packet->raw->hlen);
321:
322: snprintf(dbg_info,
323: sizeof(dbg_info),
324: "MAC address %s",
325: print_hw_addr(h.hbuf[0],
326: h.hlen - 1,
327: &h.hbuf[1]));
328:
329: find_lease_by_hw_addr(&tmp_lease, h.hbuf, h.hlen, MDL);
330: get_newest_lease(&lease, tmp_lease, next_hw);
331: assoc_ip_cnt = get_associated_ips(tmp_lease,
332: next_hw,
333: lease,
334: assoc_ips,
335: nassoc_ips);
336:
337: }
338:
339: lease_dereference(&tmp_lease, MDL);
340:
341: if (lease != NULL) {
342: memcpy(&packet->raw->ciaddr,
343: lease->ip_addr.iabuf,
344: sizeof(packet->raw->ciaddr));
345: }
346:
347: /*
348: * Log if we have too many IP addresses associated
349: * with this client.
350: */
351: if (want_associated_ip && (assoc_ip_cnt > nassoc_ips)) {
352: log_info("%d IP addresses associated with %s, "
353: "only %d sent in reply.",
354: assoc_ip_cnt, dbg_info, nassoc_ips);
355: }
356: }
357:
358: /*
359: * We now know the query target too, so can report this in
360: * our log message.
361: */
362: snprintf(msgbuf, sizeof(msgbuf),
363: "DHCPLEASEQUERY from %s for %s",
364: inet_ntoa(packet->raw->giaddr), dbg_info);
365:
366: /*
367: * Figure our our return type.
368: */
369: if (lease == NULL) {
370: dhcpMsgType = DHCPLEASEUNKNOWN;
371: dhcp_msg_type_name = "DHCPLEASEUNKNOWN";
372: } else {
373: if (lease->binding_state == FTS_ACTIVE) {
374: dhcpMsgType = DHCPLEASEACTIVE;
375: dhcp_msg_type_name = "DHCPLEASEACTIVE";
376: } else {
377: dhcpMsgType = DHCPLEASEUNASSIGNED;
378: dhcp_msg_type_name = "DHCPLEASEUNASSIGNED";
379: }
380: }
381:
382: /*
383: * Set options that only make sense if we have an active lease.
384: */
385:
386: if (dhcpMsgType == DHCPLEASEACTIVE)
387: {
388: /*
389: * RFC 4388 uses the PRL to request options for the agent to
390: * receive that are "about" the client. It is confusing
391: * because in some cases it wants to know what was sent to
392: * the client (lease times, adjusted), and in others it wants
393: * to know information the client sent. You're supposed to
394: * know this on a case-by-case basis.
395: *
396: * "Name servers", "domain name", and the like from the relay
397: * agent's scope seems less than useful. Our options are to
398: * restart the option cache from the lease's best point of view
399: * (execute statements from the lease pool's group), or to
400: * simply restart the option cache from empty.
401: *
402: * I think restarting the option cache from empty best
403: * approaches RFC 4388's intent; specific options are included.
404: */
405: option_state_dereference(&options, MDL);
406:
407: if (!option_state_allocate(&options, MDL)) {
408: log_error("%s: out of memory, no reply sent", msgbuf);
409: lease_dereference(&lease, MDL);
410: return;
411: }
412:
413: /*
414: * Set the hardware address fields.
415: */
416:
417: packet->raw->hlen = lease->hardware_addr.hlen - 1;
418: packet->raw->htype = lease->hardware_addr.hbuf[0];
419: memcpy(packet->raw->chaddr,
420: &lease->hardware_addr.hbuf[1],
421: sizeof(packet->raw->chaddr));
422:
423: /*
424: * Set client identifier option.
425: */
426: if (lease->uid_len > 0) {
427: if (!add_option(options,
428: DHO_DHCP_CLIENT_IDENTIFIER,
429: lease->uid,
430: lease->uid_len)) {
431: option_state_dereference(&options, MDL);
432: lease_dereference(&lease, MDL);
433: log_info("%s: out of memory, no reply sent",
434: msgbuf);
435: return;
436: }
437: }
438:
439:
440: /*
441: * Calculate T1 and T2, the times when the client
442: * tries to extend its lease on its networking
443: * address.
444: * These seem to be hard-coded in ISC DHCP, to 0.5 and
445: * 0.875 of the lease time.
446: */
447:
448: lease_duration = lease->ends - lease->starts;
449: time_renewal = lease->starts +
450: (lease_duration / 2);
451: time_rebinding = lease->starts +
452: (lease_duration / 2) +
453: (lease_duration / 4) +
454: (lease_duration / 8);
455:
456: if (time_renewal > cur_time) {
457: if (time_renewal < cur_time)
458: time_renewal = 0;
459: else
460: time_renewal = htonl(time_renewal - cur_time);
461:
462: if (!add_option(options,
463: DHO_DHCP_RENEWAL_TIME,
464: &time_renewal,
465: sizeof(time_renewal))) {
466: option_state_dereference(&options, MDL);
467: lease_dereference(&lease, MDL);
468: log_info("%s: out of memory, no reply sent",
469: msgbuf);
470: return;
471: }
472: }
473:
474: if (time_rebinding > cur_time) {
475: time_rebinding = htonl(time_rebinding - cur_time);
476:
477: if (!add_option(options,
478: DHO_DHCP_REBINDING_TIME,
479: &time_rebinding,
480: sizeof(time_rebinding))) {
481: option_state_dereference(&options, MDL);
482: lease_dereference(&lease, MDL);
483: log_info("%s: out of memory, no reply sent",
484: msgbuf);
485: return;
486: }
487: }
488:
489: if (lease->ends > cur_time) {
490: if (time_expiry < cur_time) {
491: log_error("Impossible condition at %s:%d.",
492: MDL);
493:
494: option_state_dereference(&options, MDL);
495: lease_dereference(&lease, MDL);
496: return;
497: }
498: time_expiry = htonl(lease->ends - cur_time);
499: if (!add_option(options,
500: DHO_DHCP_LEASE_TIME,
501: &time_expiry,
502: sizeof(time_expiry))) {
503: option_state_dereference(&options, MDL);
504: lease_dereference(&lease, MDL);
505: log_info("%s: out of memory, no reply sent",
506: msgbuf);
507: return;
508: }
509: }
510:
511: /* Supply the Vendor-Class-Identifier. */
512: if (lease->scope != NULL) {
513: struct data_string vendor_class;
514:
515: memset(&vendor_class, 0, sizeof(vendor_class));
516:
517: if (find_bound_string(&vendor_class, lease->scope,
518: "vendor-class-identifier")) {
519: if (!add_option(options,
520: DHO_VENDOR_CLASS_IDENTIFIER,
521: (void *)vendor_class.data,
522: vendor_class.len)) {
523: option_state_dereference(&options,
524: MDL);
525: lease_dereference(&lease, MDL);
526: log_error("%s: error adding vendor "
527: "class identifier, no reply "
528: "sent", msgbuf);
529: data_string_forget(&vendor_class, MDL);
530: return;
531: }
532: data_string_forget(&vendor_class, MDL);
533: }
534: }
535:
536: /*
537: * Set the relay agent info.
538: *
539: * Note that because agent info is appended without regard
540: * to the PRL in cons_options(), this will be sent as the
541: * last option in the packet whether it is listed on PRL or
542: * not.
543: */
544:
545: if (lease->agent_options != NULL) {
546: int idx = agent_universe.index;
547: struct option_chain_head **tmp1 =
548: (struct option_chain_head **)
549: &(options->universes[idx]);
550: struct option_chain_head *tmp2 =
551: (struct option_chain_head *)
552: lease->agent_options;
553:
554: option_chain_head_reference(tmp1, tmp2, MDL);
555: }
556:
557: /*
558: * Set the client last transaction time.
559: * We check to make sure we have a timestamp. For
560: * lease files that were saved before running a
561: * timestamp-aware version of the server, this may
562: * not be set.
563: */
564:
565: if (lease->cltt != MIN_TIME) {
566: if (cur_time > lease->cltt) {
567: client_last_transaction_time =
568: htonl(cur_time - lease->cltt);
569: } else {
570: client_last_transaction_time = htonl(0);
571: }
572: if (!add_option(options,
573: DHO_CLIENT_LAST_TRANSACTION_TIME,
574: &client_last_transaction_time,
575: sizeof(client_last_transaction_time))) {
576: option_state_dereference(&options, MDL);
577: lease_dereference(&lease, MDL);
578: log_info("%s: out of memory, no reply sent",
579: msgbuf);
580: return;
581: }
582: }
583:
584: /*
585: * Set associated IPs, if requested and there are some.
586: */
587: if (want_associated_ip && (assoc_ip_cnt > 0)) {
588: if (!add_option(options,
589: DHO_ASSOCIATED_IP,
590: assoc_ips,
591: assoc_ip_cnt * sizeof(assoc_ips[0]))) {
592: option_state_dereference(&options, MDL);
593: lease_dereference(&lease, MDL);
594: log_info("%s: out of memory, no reply sent",
595: msgbuf);
596: return;
597: }
598: }
599: }
600:
601: /*
602: * Set the message type.
603: */
604:
605: packet->raw->op = BOOTREPLY;
606:
607: /*
608: * Set DHCP message type.
609: */
610: if (!add_option(options,
611: DHO_DHCP_MESSAGE_TYPE,
612: &dhcpMsgType,
613: sizeof(dhcpMsgType))) {
614: option_state_dereference(&options, MDL);
615: lease_dereference(&lease, MDL);
616: log_info("%s: error adding option, no reply sent", msgbuf);
617: return;
618: }
619:
620: /*
621: * Log the message we've received.
622: */
623: log_info("%s", msgbuf);
624:
625: /*
626: * Figure out which address to use to send from.
627: */
628: get_server_source_address(&siaddr, options, packet);
629:
630: /*
631: * Set up the option buffer.
632: */
633:
634: memset(&prl, 0, sizeof(prl));
635: oc = lookup_option(&dhcp_universe, options,
636: DHO_DHCP_PARAMETER_REQUEST_LIST);
637: if (oc != NULL) {
638: evaluate_option_cache(&prl,
639: packet,
640: NULL,
641: NULL,
642: packet->options,
643: options,
644: &global_scope,
645: oc,
646: MDL);
647: }
648: if (prl.len > 0) {
649: prl_ptr = &prl;
650: } else {
651: prl_ptr = NULL;
652: }
653:
654: packet->packet_length = cons_options(packet,
655: packet->raw,
656: lease,
657: NULL,
658: 0,
659: packet->options,
660: options,
661: &global_scope,
662: 0,
663: 0,
664: 0,
665: prl_ptr,
666: NULL);
667:
668: data_string_forget(&prl, MDL); /* SK: safe, even if empty */
669: option_state_dereference(&options, MDL);
670: lease_dereference(&lease, MDL);
671:
672: to.sin_family = AF_INET;
673: #ifdef HAVE_SA_LEN
674: to.sin_len = sizeof(to);
675: #endif
676: memset(to.sin_zero, 0, sizeof(to.sin_zero));
677:
678: /*
679: * Leasequery packets are be sent to the gateway address.
680: */
681: to.sin_addr = packet->raw->giaddr;
682: if (packet->raw->giaddr.s_addr != htonl(INADDR_LOOPBACK)) {
683: to.sin_port = local_port;
684: } else {
685: to.sin_port = remote_port; /* XXXSK: For debugging. */
686: }
687:
688: /*
689: * The fallback_interface lets us send with a real IP
690: * address. The packet interface sends from all-zeros.
691: */
692: if (fallback_interface != NULL) {
693: interface = fallback_interface;
694: } else {
695: interface = packet->interface;
696: }
697:
698: /*
699: * Report what we're sending.
700: */
701: log_info("%s to %s for %s (%d associated IPs)",
702: dhcp_msg_type_name,
703: inet_ntoa(to.sin_addr), dbg_info, assoc_ip_cnt);
704:
705: send_packet(interface,
706: NULL,
707: packet->raw,
708: packet->packet_length,
709: siaddr,
710: &to,
711: NULL);
712: }
713:
714: #ifdef DHCPv6
715:
716: /*
717: * TODO: RFC5007 query-by-clientid.
718: *
719: * TODO: RFC5007 look at the pools according to the link-address.
720: *
721: * TODO: get fixed leases too.
722: *
723: * TODO: RFC5007 ORO in query-options.
724: *
725: * TODO: RFC5007 lq-relay-data.
726: *
727: * TODO: RFC5007 lq-client-link.
728: *
729: * Note: the code is still nearly compliant and usable for the target
730: * case with these missing features!
731: */
732:
733: /*
734: * The structure to handle a leasequery.
735: */
736: struct lq6_state {
737: struct packet *packet;
738: struct data_string client_id;
739: struct data_string server_id;
740: struct data_string lq_query;
741: uint8_t query_type;
742: struct in6_addr link_addr;
743: struct option_state *query_opts;
744:
745: struct option_state *reply_opts;
746: unsigned cursor;
747: union reply_buffer {
748: unsigned char data[65536];
749: struct dhcpv6_packet reply;
750: } buf;
751: };
752:
753: /*
754: * Options that we want to send.
755: */
756: static const int required_opts_lq[] = {
757: D6O_CLIENTID,
758: D6O_SERVERID,
759: D6O_STATUS_CODE,
760: D6O_CLIENT_DATA,
761: D6O_LQ_RELAY_DATA,
762: D6O_LQ_CLIENT_LINK,
763: 0
764: };
765: static const int required_opt_CLIENT_DATA[] = {
766: D6O_CLIENTID,
767: D6O_IAADDR,
768: D6O_IAPREFIX,
769: D6O_CLT_TIME,
770: 0
771: };
772:
773: /*
774: * Get the lq-query option from the packet.
775: */
776: static isc_result_t
777: get_lq_query(struct lq6_state *lq)
778: {
779: struct data_string *lq_query = &lq->lq_query;
780: struct packet *packet = lq->packet;
781: struct option_cache *oc;
782:
783: /*
784: * Verify our lq_query structure is empty.
785: */
786: if ((lq_query->data != NULL) || (lq_query->len != 0)) {
787: return ISC_R_INVALIDARG;
788: }
789:
790: oc = lookup_option(&dhcpv6_universe, packet->options, D6O_LQ_QUERY);
791: if (oc == NULL) {
792: return ISC_R_NOTFOUND;
793: }
794:
795: if (!evaluate_option_cache(lq_query, packet, NULL, NULL,
796: packet->options, NULL,
797: &global_scope, oc, MDL)) {
798: return ISC_R_FAILURE;
799: }
800:
801: return ISC_R_SUCCESS;
802: }
803:
804: /*
805: * Message validation, RFC 5007 section 4.2.1:
806: * dhcpv6.c:valid_client_msg() - unicast + lq-query option.
807: */
808: static int
809: valid_query_msg(struct lq6_state *lq) {
810: struct packet *packet = lq->packet;
811: int ret_val = 0;
812: struct option_cache *oc;
813:
814: /* INSIST((lq != NULL) || (packet != NULL)); */
815:
816: switch (get_client_id(packet, &lq->client_id)) {
817: case ISC_R_SUCCESS:
818: break;
819: case ISC_R_NOTFOUND:
820: log_debug("Discarding %s from %s; "
821: "client identifier missing",
822: dhcpv6_type_names[packet->dhcpv6_msg_type],
823: piaddr(packet->client_addr));
824: goto exit;
825: default:
826: log_error("Error processing %s from %s; "
827: "unable to evaluate Client Identifier",
828: dhcpv6_type_names[packet->dhcpv6_msg_type],
829: piaddr(packet->client_addr));
830: goto exit;
831: }
832:
833: oc = lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID);
834: if (oc != NULL) {
835: if (evaluate_option_cache(&lq->server_id, packet, NULL, NULL,
836: packet->options, NULL,
837: &global_scope, oc, MDL)) {
838: log_debug("Discarding %s from %s; "
839: "server identifier found "
840: "(CLIENTID %s, SERVERID %s)",
841: dhcpv6_type_names[packet->dhcpv6_msg_type],
842: piaddr(packet->client_addr),
843: print_hex_1(lq->client_id.len,
844: lq->client_id.data, 60),
845: print_hex_2(lq->server_id.len,
846: lq->server_id.data, 60));
847: } else {
848: log_debug("Discarding %s from %s; "
849: "server identifier found "
850: "(CLIENTID %s)",
851: dhcpv6_type_names[packet->dhcpv6_msg_type],
852: print_hex_1(lq->client_id.len,
853: lq->client_id.data, 60),
854: piaddr(packet->client_addr));
855: }
856: goto exit;
857: }
858:
859: switch (get_lq_query(lq)) {
860: case ISC_R_SUCCESS:
861: break;
862: case ISC_R_NOTFOUND:
863: log_debug("Discarding %s from %s; lq-query missing",
864: dhcpv6_type_names[packet->dhcpv6_msg_type],
865: piaddr(packet->client_addr));
866: goto exit;
867: default:
868: log_error("Error processing %s from %s; "
869: "unable to evaluate LQ-Query",
870: dhcpv6_type_names[packet->dhcpv6_msg_type],
871: piaddr(packet->client_addr));
872: goto exit;
873: }
874:
875: /* looks good */
876: ret_val = 1;
877:
878: exit:
879: if (!ret_val) {
880: if (lq->client_id.len > 0) {
881: data_string_forget(&lq->client_id, MDL);
882: }
883: if (lq->server_id.len > 0) {
884: data_string_forget(&lq->server_id, MDL);
885: }
886: if (lq->lq_query.len > 0) {
887: data_string_forget(&lq->lq_query, MDL);
888: }
889: }
890: return ret_val;
891: }
892:
893: /*
894: * Set an error in a status-code option (from set_status_code).
895: */
896: static int
897: set_error(struct lq6_state *lq, u_int16_t code, const char *message) {
898: struct data_string d;
899: int ret_val;
900:
901: memset(&d, 0, sizeof(d));
902: d.len = sizeof(code) + strlen(message);
903: if (!buffer_allocate(&d.buffer, d.len, MDL)) {
904: log_fatal("set_error: no memory for status code.");
905: }
906: d.data = d.buffer->data;
907: putUShort(d.buffer->data, code);
908: memcpy(d.buffer->data + sizeof(code), message, d.len - sizeof(code));
909: if (!save_option_buffer(&dhcpv6_universe, lq->reply_opts,
910: d.buffer, (unsigned char *)d.data, d.len,
911: D6O_STATUS_CODE, 0)) {
912: log_error("set_error: error saving status code.");
913: ret_val = 0;
914: } else {
915: ret_val = 1;
916: }
917: data_string_forget(&d, MDL);
918: return ret_val;
919: }
920:
921: /*
922: * Process a by-address lease query.
923: */
924: static int
925: process_lq_by_address(struct lq6_state *lq) {
926: struct packet *packet = lq->packet;
927: struct option_cache *oc;
928: struct ipv6_pool *pool = NULL;
929: struct data_string data;
930: struct in6_addr addr;
931: struct iasubopt *iaaddr = NULL;
932: struct option_state *opt_state = NULL;
933: u_int32_t lifetime;
934: unsigned opt_cursor;
935: int ret_val = 0;
936:
937: /*
938: * Get the IAADDR.
939: */
940: oc = lookup_option(&dhcpv6_universe, lq->query_opts, D6O_IAADDR);
941: if (oc == NULL) {
942: if (!set_error(lq, STATUS_MalformedQuery,
943: "No OPTION_IAADDR.")) {
944: log_error("process_lq_by_address: unable "
945: "to set MalformedQuery status code.");
946: return 0;
947: }
948: return 1;
949: }
950: memset(&data, 0, sizeof(data));
951: if (!evaluate_option_cache(&data, packet,
952: NULL, NULL,
953: lq->query_opts, NULL,
954: &global_scope, oc, MDL) ||
955: (data.len < IAADDR_OFFSET)) {
956: log_error("process_lq_by_address: error evaluating IAADDR.");
957: goto exit;
958: }
959: memcpy(&addr, data.data, sizeof(addr));
960: data_string_forget(&data, MDL);
961:
962: /*
963: * Find the lease.
964: * Note the RFC 5007 says to use the link-address to find the link
965: * or the ia-aadr when it is :: but in any case the ia-addr has
966: * to be on the link, so we ignore the link-address here.
967: */
968: if (find_ipv6_pool(&pool, D6O_IA_NA, &addr) != ISC_R_SUCCESS) {
969: if (!set_error(lq, STATUS_NotConfigured,
970: "Address not in a pool.")) {
971: log_error("process_lq_by_address: unable "
972: "to set NotConfigured status code.");
973: goto exit;
974: }
975: ret_val = 1;
976: goto exit;
977: }
978: if (iasubopt_hash_lookup(&iaaddr, pool->leases, &addr,
979: sizeof(addr), MDL) == 0) {
980: ret_val = 1;
981: goto exit;
982: }
983: if ((iaaddr == NULL) || (iaaddr->state != FTS_ACTIVE) ||
984: (iaaddr->ia == NULL) || (iaaddr->ia->iaid_duid.len <= 4)) {
985: ret_val = 1;
986: goto exit;
987: }
988:
989: /*
990: * Build the client-data option (with client-id, ia-addr and clt-time).
991: */
992: if (!option_state_allocate(&opt_state, MDL)) {
993: log_error("process_lq_by_address: "
994: "no memory for option state.");
995: goto exit;
996: }
997:
998: data_string_copy(&data, &iaaddr->ia->iaid_duid, MDL);
999: data.data += 4;
1000: data.len -= 4;
1001: if (!save_option_buffer(&dhcpv6_universe, opt_state,
1002: NULL, (unsigned char *)data.data, data.len,
1003: D6O_CLIENTID, 0)) {
1004: log_error("process_lq_by_address: error saving client ID.");
1005: goto exit;
1006: }
1007: data_string_forget(&data, MDL);
1008:
1009: data.len = IAADDR_OFFSET;
1010: if (!buffer_allocate(&data.buffer, data.len, MDL)) {
1011: log_error("process_lq_by_address: no memory for ia-addr.");
1012: goto exit;
1013: }
1014: data.data = data.buffer->data;
1015: memcpy(data.buffer->data, &iaaddr->addr, 16);
1016: lifetime = iaaddr->prefer;
1017: putULong(data.buffer->data + 16, lifetime);
1018: lifetime = iaaddr->valid;
1019: putULong(data.buffer->data + 20, lifetime);
1020: if (!save_option_buffer(&dhcpv6_universe, opt_state,
1021: NULL, (unsigned char *)data.data, data.len,
1022: D6O_IAADDR, 0)) {
1023: log_error("process_lq_by_address: error saving ia-addr.");
1024: goto exit;
1025: }
1026: data_string_forget(&data, MDL);
1027:
1028: lifetime = htonl(iaaddr->ia->cltt);
1029: if (!save_option_buffer(&dhcpv6_universe, opt_state,
1030: NULL, (unsigned char *)&lifetime, 4,
1031: D6O_CLT_TIME, 0)) {
1032: log_error("process_lq_by_address: error saving clt time.");
1033: goto exit;
1034: }
1035:
1036: /*
1037: * Store the client-data option.
1038: */
1039: opt_cursor = lq->cursor;
1040: putUShort(lq->buf.data + lq->cursor, (unsigned)D6O_CLIENT_DATA);
1041: lq->cursor += 2;
1042: /* Skip option length. */
1043: lq->cursor += 2;
1044:
1045: lq->cursor += store_options6((char *)lq->buf.data + lq->cursor,
1046: sizeof(lq->buf) - lq->cursor,
1047: opt_state, lq->packet,
1048: required_opt_CLIENT_DATA, NULL);
1049: /* Reset the length. */
1050: putUShort(lq->buf.data + opt_cursor + 2,
1051: lq->cursor - (opt_cursor + 4));
1052:
1053: /* Done. */
1054: ret_val = 1;
1055:
1056: exit:
1057: if (data.data != NULL)
1058: data_string_forget(&data, MDL);
1059: if (pool != NULL)
1060: ipv6_pool_dereference(&pool, MDL);
1061: if (iaaddr != NULL)
1062: iasubopt_dereference(&iaaddr, MDL);
1063: if (opt_state != NULL)
1064: option_state_dereference(&opt_state, MDL);
1065: return ret_val;
1066: }
1067:
1068:
1069: /*
1070: * Process a lease query.
1071: */
1072: void
1073: dhcpv6_leasequery(struct data_string *reply_ret, struct packet *packet) {
1074: static struct lq6_state lq;
1075: struct option_cache *oc;
1076: int allow_lq;
1077:
1078: /*
1079: * Initialize the lease query state.
1080: */
1081: lq.packet = NULL;
1082: memset(&lq.client_id, 0, sizeof(lq.client_id));
1083: memset(&lq.server_id, 0, sizeof(lq.server_id));
1084: memset(&lq.lq_query, 0, sizeof(lq.lq_query));
1085: lq.query_opts = NULL;
1086: lq.reply_opts = NULL;
1087: packet_reference(&lq.packet, packet, MDL);
1088:
1089: /*
1090: * Validate our input.
1091: */
1092: if (!valid_query_msg(&lq)) {
1093: goto exit;
1094: }
1095:
1096: /*
1097: * Prepare our reply.
1098: */
1099: if (!option_state_allocate(&lq.reply_opts, MDL)) {
1100: log_error("dhcpv6_leasequery: no memory for option state.");
1101: goto exit;
1102: }
1103: execute_statements_in_scope(NULL, lq.packet, NULL, NULL,
1104: lq.packet->options, lq.reply_opts,
1105: &global_scope, root_group, NULL);
1106:
1107: lq.buf.reply.msg_type = DHCPV6_LEASEQUERY_REPLY;
1108:
1109: memcpy(lq.buf.reply.transaction_id,
1110: lq.packet->dhcpv6_transaction_id,
1111: sizeof(lq.buf.reply.transaction_id));
1112:
1113: /*
1114: * Because LEASEQUERY has some privacy concerns, default to deny.
1115: */
1116: allow_lq = 0;
1117:
1118: /*
1119: * See if we are authorized to do LEASEQUERY.
1120: */
1121: oc = lookup_option(&server_universe, lq.reply_opts, SV_LEASEQUERY);
1122: if (oc != NULL) {
1123: allow_lq = evaluate_boolean_option_cache(NULL,
1124: lq.packet,
1125: NULL, NULL,
1126: lq.packet->options,
1127: lq.reply_opts,
1128: &global_scope,
1129: oc, MDL);
1130: }
1131:
1132: if (!allow_lq) {
1133: log_info("dhcpv6_leasequery: not allowed, query ignored.");
1134: goto exit;
1135: }
1136:
1137: /*
1138: * Same than transmission of REPLY message in RFC 3315:
1139: * server-id
1140: * client-id
1141: */
1142:
1143: oc = lookup_option(&dhcpv6_universe, lq.reply_opts, D6O_SERVERID);
1144: if (oc == NULL) {
1145: /* If not already in options, get from query then global. */
1146: if (lq.server_id.data == NULL)
1147: copy_server_duid(&lq.server_id, MDL);
1148: if (!save_option_buffer(&dhcpv6_universe,
1149: lq.reply_opts,
1150: NULL,
1151: (unsigned char *)lq.server_id.data,
1152: lq.server_id.len,
1153: D6O_SERVERID,
1154: 0)) {
1155: log_error("dhcpv6_leasequery: "
1156: "error saving server identifier.");
1157: goto exit;
1158: }
1159: }
1160:
1161: if (!save_option_buffer(&dhcpv6_universe,
1162: lq.reply_opts,
1163: lq.client_id.buffer,
1164: (unsigned char *)lq.client_id.data,
1165: lq.client_id.len,
1166: D6O_CLIENTID,
1167: 0)) {
1168: log_error("dhcpv6_leasequery: "
1169: "error saving client identifier.");
1170: goto exit;
1171: }
1172:
1173: lq.cursor = 4;
1174:
1175: /*
1176: * Decode the lq-query option.
1177: */
1178:
1179: if (lq.lq_query.len <= LQ_QUERY_OFFSET) {
1180: if (!set_error(&lq, STATUS_MalformedQuery,
1181: "OPTION_LQ_QUERY too short.")) {
1182: log_error("dhcpv6_leasequery: unable "
1183: "to set MalformedQuery status code.");
1184: goto exit;
1185: }
1186: goto done;
1187: }
1188:
1189: lq.query_type = lq.lq_query.data [0];
1190: memcpy(&lq.link_addr, lq.lq_query.data + 1, sizeof(lq.link_addr));
1191: switch (lq.query_type) {
1192: case LQ6QT_BY_ADDRESS:
1193: break;
1194: case LQ6QT_BY_CLIENTID:
1195: if (!set_error(&lq, STATUS_UnknownQueryType,
1196: "QUERY_BY_CLIENTID not supported.")) {
1197: log_error("dhcpv6_leasequery: unable to "
1198: "set UnknownQueryType status code.");
1199: goto exit;
1200: }
1201: goto done;
1202: default:
1203: if (!set_error(&lq, STATUS_UnknownQueryType,
1204: "Unknown query-type.")) {
1205: log_error("dhcpv6_leasequery: unable to "
1206: "set UnknownQueryType status code.");
1207: goto exit;
1208: }
1209: goto done;
1210: }
1211:
1212: if (!option_state_allocate(&lq.query_opts, MDL)) {
1213: log_error("dhcpv6_leasequery: no memory for option state.");
1214: goto exit;
1215: }
1216: if (!parse_option_buffer(lq.query_opts,
1217: lq.lq_query.data + LQ_QUERY_OFFSET,
1218: lq.lq_query.len - LQ_QUERY_OFFSET,
1219: &dhcpv6_universe)) {
1220: log_error("dhcpv6_leasequery: error parsing query-options.");
1221: if (!set_error(&lq, STATUS_MalformedQuery,
1222: "Bad query-options.")) {
1223: log_error("dhcpv6_leasequery: unable "
1224: "to set MalformedQuery status code.");
1225: goto exit;
1226: }
1227: goto done;
1228: }
1229:
1230: /* Do it. */
1231: if (!process_lq_by_address(&lq))
1232: goto exit;
1233:
1234: done:
1235: /* Store the options. */
1236: lq.cursor += store_options6((char *)lq.buf.data + lq.cursor,
1237: sizeof(lq.buf) - lq.cursor,
1238: lq.reply_opts,
1239: lq.packet,
1240: required_opts_lq,
1241: NULL);
1242:
1243: /* Return our reply to the caller. */
1244: reply_ret->len = lq.cursor;
1245: reply_ret->buffer = NULL;
1246: if (!buffer_allocate(&reply_ret->buffer, lq.cursor, MDL)) {
1247: log_fatal("dhcpv6_leasequery: no memory to store Reply.");
1248: }
1249: memcpy(reply_ret->buffer->data, lq.buf.data, lq.cursor);
1250: reply_ret->data = reply_ret->buffer->data;
1251:
1252: exit:
1253: /* Cleanup. */
1254: if (lq.packet != NULL)
1255: packet_dereference(&lq.packet, MDL);
1256: if (lq.client_id.data != NULL)
1257: data_string_forget(&lq.client_id, MDL);
1258: if (lq.server_id.data != NULL)
1259: data_string_forget(&lq.server_id, MDL);
1260: if (lq.lq_query.data != NULL)
1261: data_string_forget(&lq.lq_query, MDL);
1262: if (lq.query_opts != NULL)
1263: option_state_dereference(&lq.query_opts, MDL);
1264: if (lq.reply_opts != NULL)
1265: option_state_dereference(&lq.reply_opts, MDL);
1266: }
1267:
1268: #endif /* DHCPv6 */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>