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