/* * Parse all available options out of the specified packet. */ void parse_options(struct packet *packet) { /* Initially, zero all option pointers. */ memset(packet->options, 0, sizeof(packet->options)); /* If we don't see the magic cookie, there's nothing to parse. */ if (memcmp(packet->raw->options, DHCP_OPTIONS_COOKIE, 4)) { packet->options_valid = 0; return; } /* * Go through the options field, up to the end of the packet or * the End field. */ parse_option_buffer(packet, &packet->raw->options[4], packet->packet_length - DHCP_FIXED_NON_UDP - 4); /* * If we parsed a DHCP Option Overload option, parse more * options out of the buffer(s) containing them. */ if (packet->options_valid && packet->options[DHO_DHCP_OPTION_OVERLOAD].data) { if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1) parse_option_buffer(packet, (unsigned char *)packet->raw->file, sizeof(packet->raw->file)); if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2) parse_option_buffer(packet, (unsigned char *)packet->raw->sname, sizeof(packet->raw->sname)); } }
/* * Process a lease query. */ void dhcpv6_leasequery(struct data_string *reply_ret, struct packet *packet) { static struct lq6_state lq; struct option_cache *oc; int allow_lq; /* * Initialize the lease query state. */ lq.packet = NULL; memset(&lq.client_id, 0, sizeof(lq.client_id)); memset(&lq.server_id, 0, sizeof(lq.server_id)); memset(&lq.lq_query, 0, sizeof(lq.lq_query)); lq.query_opts = NULL; lq.reply_opts = NULL; packet_reference(&lq.packet, packet, MDL); /* * Validate our input. */ if (!valid_query_msg(&lq)) { goto exit; } /* * Prepare our reply. */ if (!option_state_allocate(&lq.reply_opts, MDL)) { log_error("dhcpv6_leasequery: no memory for option state."); goto exit; } execute_statements_in_scope(NULL, lq.packet, NULL, NULL, lq.packet->options, lq.reply_opts, &global_scope, root_group, NULL, NULL); lq.buf.reply.msg_type = DHCPV6_LEASEQUERY_REPLY; memcpy(lq.buf.reply.transaction_id, lq.packet->dhcpv6_transaction_id, sizeof(lq.buf.reply.transaction_id)); /* * Because LEASEQUERY has some privacy concerns, default to deny. */ allow_lq = 0; /* * See if we are authorized to do LEASEQUERY. */ oc = lookup_option(&server_universe, lq.reply_opts, SV_LEASEQUERY); if (oc != NULL) { allow_lq = evaluate_boolean_option_cache(NULL, lq.packet, NULL, NULL, lq.packet->options, lq.reply_opts, &global_scope, oc, MDL); } if (!allow_lq) { log_info("dhcpv6_leasequery: not allowed, query ignored."); goto exit; } /* * Same than transmission of REPLY message in RFC 3315: * server-id * client-id */ oc = lookup_option(&dhcpv6_universe, lq.reply_opts, D6O_SERVERID); if (oc == NULL) { /* If not already in options, get from query then global. */ if (lq.server_id.data == NULL) copy_server_duid(&lq.server_id, MDL); if (!save_option_buffer(&dhcpv6_universe, lq.reply_opts, NULL, (unsigned char *)lq.server_id.data, lq.server_id.len, D6O_SERVERID, 0)) { log_error("dhcpv6_leasequery: " "error saving server identifier."); goto exit; } } if (!save_option_buffer(&dhcpv6_universe, lq.reply_opts, lq.client_id.buffer, (unsigned char *)lq.client_id.data, lq.client_id.len, D6O_CLIENTID, 0)) { log_error("dhcpv6_leasequery: " "error saving client identifier."); goto exit; } lq.cursor = 4; /* * Decode the lq-query option. */ if (lq.lq_query.len <= LQ_QUERY_OFFSET) { if (!set_error(&lq, STATUS_MalformedQuery, "OPTION_LQ_QUERY too short.")) { log_error("dhcpv6_leasequery: unable " "to set MalformedQuery status code."); goto exit; } goto done; } lq.query_type = lq.lq_query.data [0]; memcpy(&lq.link_addr, lq.lq_query.data + 1, sizeof(lq.link_addr)); switch (lq.query_type) { case LQ6QT_BY_ADDRESS: break; case LQ6QT_BY_CLIENTID: if (!set_error(&lq, STATUS_UnknownQueryType, "QUERY_BY_CLIENTID not supported.")) { log_error("dhcpv6_leasequery: unable to " "set UnknownQueryType status code."); goto exit; } goto done; default: if (!set_error(&lq, STATUS_UnknownQueryType, "Unknown query-type.")) { log_error("dhcpv6_leasequery: unable to " "set UnknownQueryType status code."); goto exit; } goto done; } if (!option_state_allocate(&lq.query_opts, MDL)) { log_error("dhcpv6_leasequery: no memory for option state."); goto exit; } if (!parse_option_buffer(lq.query_opts, lq.lq_query.data + LQ_QUERY_OFFSET, lq.lq_query.len - LQ_QUERY_OFFSET, &dhcpv6_universe)) { log_error("dhcpv6_leasequery: error parsing query-options."); if (!set_error(&lq, STATUS_MalformedQuery, "Bad query-options.")) { log_error("dhcpv6_leasequery: unable " "to set MalformedQuery status code."); goto exit; } goto done; } /* Do it. */ if (!process_lq_by_address(&lq)) goto exit; done: /* Store the options. */ lq.cursor += store_options6((char *)lq.buf.data + lq.cursor, sizeof(lq.buf) - lq.cursor, lq.reply_opts, lq.packet, required_opts_lq, NULL); /* Return our reply to the caller. */ reply_ret->len = lq.cursor; reply_ret->buffer = NULL; if (!buffer_allocate(&reply_ret->buffer, lq.cursor, MDL)) { log_fatal("dhcpv6_leasequery: no memory to store Reply."); } memcpy(reply_ret->buffer->data, lq.buf.data, lq.cursor); reply_ret->data = reply_ret->buffer->data; exit: /* Cleanup. */ if (lq.packet != NULL) packet_dereference(&lq.packet, MDL); if (lq.client_id.data != NULL) data_string_forget(&lq.client_id, MDL); if (lq.server_id.data != NULL) data_string_forget(&lq.server_id, MDL); if (lq.lq_query.data != NULL) data_string_forget(&lq.lq_query, MDL); if (lq.query_opts != NULL) option_state_dereference(&lq.query_opts, MDL); if (lq.reply_opts != NULL) option_state_dereference(&lq.reply_opts, MDL); }
void do_packet(unsigned int from_port, struct in_addr from, struct ether_addr *hfrom) { struct dhcp_packet *packet = &client->packet; struct option_data options[256]; struct reject_elem *ap; void (*handler)(struct in_addr, struct option_data *, char *); char *type, *info; int i, rslt, options_valid = 1; if (packet->hlen != ETHER_ADDR_LEN) { #ifdef DEBUG debug("Discarding packet with hlen != %s (%u)", ifi->name, packet->hlen); #endif return; } else if (memcmp(&ifi->hw_address, packet->chaddr, sizeof(ifi->hw_address))) { #ifdef DEBUG debug("Discarding packet with chaddr != %s (%s)", ifi->name, ether_ntoa((struct ether_addr *)packet->chaddr)); #endif return; } if (client->xid != client->packet.xid) { #ifdef DEBUG debug("Discarding packet with XID != %u (%u)", client->xid, client->packet.xid); #endif return; } TAILQ_FOREACH(ap, &config->reject_list, next) if (from.s_addr == ap->addr.s_addr) { #ifdef DEBUG debug("Discarding packet from address on reject list " "(%s)", inet_ntoa(from)); #endif return; } memset(options, 0, sizeof(options)); if (memcmp(&packet->options, DHCP_OPTIONS_COOKIE, 4) == 0) { /* Parse the BOOTP/DHCP options field. */ options_valid = parse_option_buffer(options, &packet->options[4], sizeof(packet->options) - 4); /* Only DHCP packets have overload areas for options. */ if (options_valid && options[DHO_DHCP_MESSAGE_TYPE].data && options[DHO_DHCP_OPTION_OVERLOAD].data) { if (options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1) options_valid = parse_option_buffer(options, (unsigned char *)packet->file, sizeof(packet->file)); if (options_valid && options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2) options_valid = parse_option_buffer(options, (unsigned char *)packet->sname, sizeof(packet->sname)); } } type = "<unknown>"; handler = NULL; if (options[DHO_DHCP_MESSAGE_TYPE].data) { /* Always try a DHCP packet, even if a bad option was seen. */ switch (options[DHO_DHCP_MESSAGE_TYPE].data[0]) { case DHCPOFFER: handler = dhcpoffer; type = "DHCPOFFER"; break; case DHCPNAK: handler = dhcpnak; type = "DHCPNACK"; break; case DHCPACK: handler = dhcpack; type = "DHCPACK"; break; default: #ifdef DEBUG debug("Discarding DHCP packet of unknown type (%d)", options[DHO_DHCP_MESSAGE_TYPE].data[0]); #endif break; } } else if (options_valid && packet->op == BOOTREPLY) { handler = dhcpoffer; type = "BOOTREPLY"; } else { #ifdef DEBUG debug("Discarding packet which is neither DHCP nor BOOTP"); #endif } rslt = asprintf(&info, "%s from %s (%s)", type, inet_ntoa(from), ether_ntoa(hfrom)); if (rslt == -1) error("no memory for info string"); if (handler) (*handler)(from, options, info); free(info); for (i = 0; i < 256; i++) if (options[i].len && options[i].data) free(options[i].data); }
void do_packet(int len, unsigned int from_port, struct iaddr from, struct hardware *hfrom) { struct dhcp_packet *packet = &client->packet; struct option_data options[256]; struct iaddrlist *ap; void (*handler)(struct iaddr, struct option_data *); char *type; int i, options_valid = 1; if (packet->hlen > sizeof(packet->chaddr)) { note("Discarding packet with invalid hlen."); return; } /* * Silently drop the packet if the client hardware address in the * packet is not the hardware address of the interface being managed. */ if ((ifi->hw_address.hlen != packet->hlen) || (memcmp(ifi->hw_address.haddr, packet->chaddr, packet->hlen))) return; memset(options, 0, sizeof(options)); if (memcmp(&packet->options, DHCP_OPTIONS_COOKIE, 4) == 0) { /* Parse the BOOTP/DHCP options field. */ options_valid = parse_option_buffer(options, &packet->options[4], sizeof(packet->options) - 4); /* Only DHCP packets have overload areas for options. */ if (options_valid && options[DHO_DHCP_MESSAGE_TYPE].data && options[DHO_DHCP_OPTION_OVERLOAD].data) { if (options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1) options_valid = parse_option_buffer(options, (unsigned char *)packet->file, sizeof(packet->file)); if (options_valid && options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2) options_valid = parse_option_buffer(options, (unsigned char *)packet->sname, sizeof(packet->sname)); } } type = ""; handler = NULL; if (options[DHO_DHCP_MESSAGE_TYPE].data) { /* Always try a DHCP packet, even if a bad option was seen. */ switch (options[DHO_DHCP_MESSAGE_TYPE].data[0]) { case DHCPOFFER: handler = dhcpoffer; type = "DHCPOFFER"; break; case DHCPNAK: handler = dhcpnak; type = "DHCPNACK"; break; case DHCPACK: handler = dhcpack; type = "DHCPACK"; break; default: break; } } else if (options_valid && packet->op == BOOTREPLY) { handler = dhcpoffer; type = "BOOTREPLY"; } for (ap = config->reject_list; ap && handler; ap = ap->next) if (addr_eq(from, ap->addr)) { note("%s from %s rejected.", type, piaddr(from)); handler = NULL; } if (handler) (*handler)(from, options); for (i = 0; i < 256; i++) if (options[i].len && options[i].data) free(options[i].data); }