void lease_pinged(struct iaddr from, u_int8_t *packet, int length) { struct lease *lp; /* * Don't try to look up a pinged lease if we aren't trying to * ping one - otherwise somebody could easily make us churn by * just forging repeated ICMP EchoReply packets for us to look * up. */ if (!outstanding_pings) return; lp = find_lease_by_ip_addr(from); if (!lp) { note("unexpected ICMP Echo Reply from %s", piaddr(from)); return; } if (!lp->state && !lp->releasing) { warning("ICMP Echo Reply for %s arrived late or is spurious.", piaddr(from)); return; } /* At this point it looks like we pinged a lease and got a * response, which shouldn't have happened. * if it did it's either one of two two cases: * 1 - we pinged this lease before offering it and * something answered, so we abandon it. * 2 - we pinged this lease before releasing it * and something answered, so we don't release it. */ if (lp->releasing) { warning("IP address %s answers a ping after sending a release", piaddr(lp->ip_addr)); warning("Possible release spoof - Not releasing address %s", piaddr(lp->ip_addr)); lp->releasing = 0; } else { free_lease_state(lp->state, "lease_pinged"); lp->state = NULL; abandon_lease(lp, "pinged before offer"); } cancel_timeout(lease_ping_timeout, lp); --outstanding_pings; }
void relinquish_lease_hunks () { struct lease *c, *n, **p; int i; /* Account for all the leases on the free list. */ for (n = lease_hunks; n; n = n->next) { for (i = 1; i < n->starts + 1; i++) { p = &free_leases; for (c = free_leases; c; c = c->next) { if (c == &n[i]) { *p = c->next; n->ends++; break; } p = &c->next; } if (!c) { log_info("lease %s refcnt %d", piaddr (n[i].ip_addr), n[i].refcnt); #if defined (DEBUG_RC_HISTORY) dump_rc_history(&n[i]); #endif } } } for (c = lease_hunks; c; c = n) { n = c->next; if (c->ends != c->starts) { log_info("lease hunk %lx leases %ld free %ld", (unsigned long)c, (unsigned long)(c->starts), (unsigned long)(c->ends)); } dfree(c, MDL); } /* Free all the rogue leases. */ for (c = free_leases; c; c = n) { n = c->next; dfree(c, MDL); } }
/* * Message validation, RFC 5007 section 4.2.1: * dhcpv6.c:valid_client_msg() - unicast + lq-query option. */ static int valid_query_msg(struct lq6_state *lq) { struct packet *packet = lq->packet; int ret_val = 0; struct option_cache *oc; /* INSIST((lq != NULL) || (packet != NULL)); */ switch (get_client_id(packet, &lq->client_id)) { case ISC_R_SUCCESS: break; case ISC_R_NOTFOUND: log_debug("Discarding %s from %s; " "client identifier missing", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr)); goto exit; default: log_error("Error processing %s from %s; " "unable to evaluate Client Identifier", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr)); goto exit; } oc = lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID); if (oc != NULL) { if (evaluate_option_cache(&lq->server_id, packet, NULL, NULL, packet->options, NULL, &global_scope, oc, MDL)) { log_debug("Discarding %s from %s; " "server identifier found " "(CLIENTID %s, SERVERID %s)", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr), print_hex_1(lq->client_id.len, lq->client_id.data, 60), print_hex_2(lq->server_id.len, lq->server_id.data, 60)); } else { log_debug("Discarding %s from %s; " "server identifier found " "(CLIENTID %s)", dhcpv6_type_names[packet->dhcpv6_msg_type], print_hex_1(lq->client_id.len, lq->client_id.data, 60), piaddr(packet->client_addr)); } goto exit; } switch (get_lq_query(lq)) { case ISC_R_SUCCESS: break; case ISC_R_NOTFOUND: log_debug("Discarding %s from %s; lq-query missing", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr)); goto exit; default: log_error("Error processing %s from %s; " "unable to evaluate LQ-Query", dhcpv6_type_names[packet->dhcpv6_msg_type], piaddr(packet->client_addr)); goto exit; } /* looks good */ ret_val = 1; exit: if (!ret_val) { if (lq->client_id.len > 0) { data_string_forget(&lq->client_id, MDL); } if (lq->server_id.len > 0) { data_string_forget(&lq->server_id, MDL); } if (lq->lq_query.len > 0) { data_string_forget(&lq->lq_query, MDL); } } return ret_val; }
void dhcpleasequery(struct packet *packet, int ms_nulltp) { char msgbuf[256]; char dbg_info[128]; struct iaddr cip; struct iaddr gip; struct data_string uid; struct hardware h; struct lease *tmp_lease; struct lease *lease; int want_associated_ip; int assoc_ip_cnt; u_int32_t assoc_ips[40]; /* XXXSK: arbitrary maximum number of IPs */ const int nassoc_ips = sizeof(assoc_ips) / sizeof(assoc_ips[0]); unsigned char dhcpMsgType; const char *dhcp_msg_type_name; struct subnet *subnet; struct group *relay_group; struct option_state *options; struct option_cache *oc; int allow_leasequery; int ignorep; u_int32_t lease_duration; u_int32_t time_renewal; u_int32_t time_rebinding; u_int32_t time_expiry; u_int32_t client_last_transaction_time; struct sockaddr_in to; struct in_addr siaddr; struct data_string prl; struct data_string *prl_ptr; int i; struct interface_info *interface; /* INSIST(packet != NULL); */ /* * Prepare log information. */ snprintf(msgbuf, sizeof(msgbuf), "DHCPLEASEQUERY from %s", inet_ntoa(packet->raw->giaddr)); /* * We can't reply if there is no giaddr field. */ if (!packet->raw->giaddr.s_addr) { log_info("%s: missing giaddr, ciaddr is %s, no reply sent", msgbuf, inet_ntoa(packet->raw->ciaddr)); return; } /* * Initially we use the 'giaddr' subnet options scope to determine if * the giaddr-identified relay agent is permitted to perform a * leasequery. The subnet is not required, and may be omitted, in * which case we are essentially interrogating the root options class * to find a globally permit. */ gip.len = sizeof(packet->raw->giaddr); memcpy(gip.iabuf, &packet->raw->giaddr, sizeof(packet->raw->giaddr)); subnet = NULL; find_subnet(&subnet, gip, MDL); if (subnet != NULL) relay_group = subnet->group; else relay_group = root_group; subnet_dereference(&subnet, MDL); options = NULL; if (!option_state_allocate(&options, MDL)) { log_error("No memory for option state."); log_info("%s: out of memory, no reply sent", msgbuf); return; } execute_statements_in_scope(NULL, packet, NULL, NULL, packet->options, options, &global_scope, relay_group, NULL, NULL); for (i=packet->class_count-1; i>=0; i--) { execute_statements_in_scope(NULL, packet, NULL, NULL, packet->options, options, &global_scope, packet->classes[i]->group, relay_group, NULL); } /* * Because LEASEQUERY has some privacy concerns, default to deny. */ allow_leasequery = 0; /* * See if we are authorized to do LEASEQUERY. */ oc = lookup_option(&server_universe, options, SV_LEASEQUERY); if (oc != NULL) { allow_leasequery = evaluate_boolean_option_cache(&ignorep, packet, NULL, NULL, packet->options, options, &global_scope, oc, MDL); } if (!allow_leasequery) { log_info("%s: LEASEQUERY not allowed, query ignored", msgbuf); option_state_dereference(&options, MDL); return; } /* * Copy out the client IP address. */ cip.len = sizeof(packet->raw->ciaddr); memcpy(cip.iabuf, &packet->raw->ciaddr, sizeof(packet->raw->ciaddr)); /* * If the client IP address is valid (not all zero), then we * are looking for information about that IP address. */ assoc_ip_cnt = 0; lease = tmp_lease = NULL; if (memcmp(cip.iabuf, "\0\0\0", 4)) { want_associated_ip = 0; snprintf(dbg_info, sizeof(dbg_info), "IP %s", piaddr(cip)); find_lease_by_ip_addr(&lease, cip, MDL); } else { want_associated_ip = 1; /* * If the client IP address is all zero, then we will * either look up by the client identifier (if we have * one), or by the MAC address. */ memset(&uid, 0, sizeof(uid)); if (get_option(&uid, &dhcp_universe, packet, NULL, NULL, packet->options, NULL, packet->options, &global_scope, DHO_DHCP_CLIENT_IDENTIFIER, MDL)) { snprintf(dbg_info, sizeof(dbg_info), "client-id %s", print_hex_1(uid.len, uid.data, 60)); find_lease_by_uid(&tmp_lease, uid.data, uid.len, MDL); data_string_forget(&uid, MDL); get_newest_lease(&lease, tmp_lease, next_uid); assoc_ip_cnt = get_associated_ips(tmp_lease, next_uid, lease, assoc_ips, nassoc_ips); } else { if (packet->raw->hlen+1 > sizeof(h.hbuf)) { log_info("%s: hardware length too long, " "no reply sent", msgbuf); option_state_dereference(&options, MDL); return; } h.hlen = packet->raw->hlen + 1; h.hbuf[0] = packet->raw->htype; memcpy(&h.hbuf[1], packet->raw->chaddr, packet->raw->hlen); snprintf(dbg_info, sizeof(dbg_info), "MAC address %s", print_hw_addr(h.hbuf[0], h.hlen - 1, &h.hbuf[1])); find_lease_by_hw_addr(&tmp_lease, h.hbuf, h.hlen, MDL); get_newest_lease(&lease, tmp_lease, next_hw); assoc_ip_cnt = get_associated_ips(tmp_lease, next_hw, lease, assoc_ips, nassoc_ips); } lease_dereference(&tmp_lease, MDL); if (lease != NULL) { memcpy(&packet->raw->ciaddr, lease->ip_addr.iabuf, sizeof(packet->raw->ciaddr)); } /* * Log if we have too many IP addresses associated * with this client. */ if (want_associated_ip && (assoc_ip_cnt > nassoc_ips)) { log_info("%d IP addresses associated with %s, " "only %d sent in reply.", assoc_ip_cnt, dbg_info, nassoc_ips); } } /* * We now know the query target too, so can report this in * our log message. */ snprintf(msgbuf, sizeof(msgbuf), "DHCPLEASEQUERY from %s for %s", inet_ntoa(packet->raw->giaddr), dbg_info); /* * Figure our our return type. */ if (lease == NULL) { dhcpMsgType = DHCPLEASEUNKNOWN; dhcp_msg_type_name = "DHCPLEASEUNKNOWN"; } else { if (lease->binding_state == FTS_ACTIVE) { dhcpMsgType = DHCPLEASEACTIVE; dhcp_msg_type_name = "DHCPLEASEACTIVE"; } else { dhcpMsgType = DHCPLEASEUNASSIGNED; dhcp_msg_type_name = "DHCPLEASEUNASSIGNED"; } } /* * Set options that only make sense if we have an active lease. */ if (dhcpMsgType == DHCPLEASEACTIVE) { /* * RFC 4388 uses the PRL to request options for the agent to * receive that are "about" the client. It is confusing * because in some cases it wants to know what was sent to * the client (lease times, adjusted), and in others it wants * to know information the client sent. You're supposed to * know this on a case-by-case basis. * * "Name servers", "domain name", and the like from the relay * agent's scope seems less than useful. Our options are to * restart the option cache from the lease's best point of view * (execute statements from the lease pool's group), or to * simply restart the option cache from empty. * * I think restarting the option cache from empty best * approaches RFC 4388's intent; specific options are included. */ option_state_dereference(&options, MDL); if (!option_state_allocate(&options, MDL)) { log_error("%s: out of memory, no reply sent", msgbuf); lease_dereference(&lease, MDL); return; } /* * Set the hardware address fields. */ packet->raw->hlen = lease->hardware_addr.hlen - 1; packet->raw->htype = lease->hardware_addr.hbuf[0]; memcpy(packet->raw->chaddr, &lease->hardware_addr.hbuf[1], sizeof(packet->raw->chaddr)); /* * Set client identifier option. */ if (lease->uid_len > 0) { if (!add_option(options, DHO_DHCP_CLIENT_IDENTIFIER, lease->uid, lease->uid_len)) { option_state_dereference(&options, MDL); lease_dereference(&lease, MDL); log_info("%s: out of memory, no reply sent", msgbuf); return; } } /* * Calculate T1 and T2, the times when the client * tries to extend its lease on its networking * address. * These seem to be hard-coded in ISC DHCP, to 0.5 and * 0.875 of the lease time. */ lease_duration = lease->ends - lease->starts; time_renewal = lease->starts + (lease_duration / 2); time_rebinding = lease->starts + (lease_duration / 2) + (lease_duration / 4) + (lease_duration / 8); if (time_renewal > cur_time) { time_renewal = htonl(time_renewal - cur_time); if (!add_option(options, DHO_DHCP_RENEWAL_TIME, &time_renewal, sizeof(time_renewal))) { option_state_dereference(&options, MDL); lease_dereference(&lease, MDL); log_info("%s: out of memory, no reply sent", msgbuf); return; } } if (time_rebinding > cur_time) { time_rebinding = htonl(time_rebinding - cur_time); if (!add_option(options, DHO_DHCP_REBINDING_TIME, &time_rebinding, sizeof(time_rebinding))) { option_state_dereference(&options, MDL); lease_dereference(&lease, MDL); log_info("%s: out of memory, no reply sent", msgbuf); return; } } if (lease->ends > cur_time) { time_expiry = htonl(lease->ends - cur_time); if (!add_option(options, DHO_DHCP_LEASE_TIME, &time_expiry, sizeof(time_expiry))) { option_state_dereference(&options, MDL); lease_dereference(&lease, MDL); log_info("%s: out of memory, no reply sent", msgbuf); return; } } /* Supply the Vendor-Class-Identifier. */ if (lease->scope != NULL) { struct data_string vendor_class; memset(&vendor_class, 0, sizeof(vendor_class)); if (find_bound_string(&vendor_class, lease->scope, "vendor-class-identifier")) { if (!add_option(options, DHO_VENDOR_CLASS_IDENTIFIER, (void *)vendor_class.data, vendor_class.len)) { option_state_dereference(&options, MDL); lease_dereference(&lease, MDL); log_error("%s: error adding vendor " "class identifier, no reply " "sent", msgbuf); data_string_forget(&vendor_class, MDL); return; } data_string_forget(&vendor_class, MDL); } } /* * Set the relay agent info. * * Note that because agent info is appended without regard * to the PRL in cons_options(), this will be sent as the * last option in the packet whether it is listed on PRL or * not. */ if (lease->agent_options != NULL) { int idx = agent_universe.index; struct option_chain_head **tmp1 = (struct option_chain_head **) &(options->universes[idx]); struct option_chain_head *tmp2 = (struct option_chain_head *) lease->agent_options; option_chain_head_reference(tmp1, tmp2, MDL); } /* * Set the client last transaction time. * We check to make sure we have a timestamp. For * lease files that were saved before running a * timestamp-aware version of the server, this may * not be set. */ if (lease->cltt != MIN_TIME) { if (cur_time > lease->cltt) { client_last_transaction_time = htonl(cur_time - lease->cltt); } else { client_last_transaction_time = htonl(0); } if (!add_option(options, DHO_CLIENT_LAST_TRANSACTION_TIME, &client_last_transaction_time, sizeof(client_last_transaction_time))) { option_state_dereference(&options, MDL); lease_dereference(&lease, MDL); log_info("%s: out of memory, no reply sent", msgbuf); return; } } /* * Set associated IPs, if requested and there are some. */ if (want_associated_ip && (assoc_ip_cnt > 0)) { if (!add_option(options, DHO_ASSOCIATED_IP, assoc_ips, assoc_ip_cnt * sizeof(assoc_ips[0]))) { option_state_dereference(&options, MDL); lease_dereference(&lease, MDL); log_info("%s: out of memory, no reply sent", msgbuf); return; } } } /* * Set the message type. */ packet->raw->op = BOOTREPLY; /* * Set DHCP message type. */ if (!add_option(options, DHO_DHCP_MESSAGE_TYPE, &dhcpMsgType, sizeof(dhcpMsgType))) { option_state_dereference(&options, MDL); lease_dereference(&lease, MDL); log_info("%s: error adding option, no reply sent", msgbuf); return; } /* * Log the message we've received. */ log_info("%s", msgbuf); /* * Figure out which address to use to send from. */ get_server_source_address(&siaddr, options, options, packet); /* * Set up the option buffer. */ memset(&prl, 0, sizeof(prl)); oc = lookup_option(&dhcp_universe, options, DHO_DHCP_PARAMETER_REQUEST_LIST); if (oc != NULL) { evaluate_option_cache(&prl, packet, NULL, NULL, packet->options, options, &global_scope, oc, MDL); } if (prl.len > 0) { prl_ptr = &prl; } else { prl_ptr = NULL; } packet->packet_length = cons_options(packet, packet->raw, lease, NULL, 0, packet->options, options, &global_scope, 0, 0, 0, prl_ptr, NULL); data_string_forget(&prl, MDL); /* SK: safe, even if empty */ option_state_dereference(&options, MDL); lease_dereference(&lease, MDL); to.sin_family = AF_INET; #ifdef HAVE_SA_LEN to.sin_len = sizeof(to); #endif memset(to.sin_zero, 0, sizeof(to.sin_zero)); /* * Leasequery packets are be sent to the gateway address. */ to.sin_addr = packet->raw->giaddr; if (packet->raw->giaddr.s_addr != htonl(INADDR_LOOPBACK)) { to.sin_port = local_port; } else { to.sin_port = remote_port; /* XXXSK: For debugging. */ } /* * The fallback_interface lets us send with a real IP * address. The packet interface sends from all-zeros. */ if (fallback_interface != NULL) { interface = fallback_interface; } else { interface = packet->interface; } /* * Report what we're sending. */ log_info("%s to %s for %s (%d associated IPs)", dhcp_msg_type_name, inet_ntoa(to.sin_addr), dbg_info, assoc_ip_cnt); send_packet(interface, NULL, packet->raw, packet->packet_length, siaddr, &to, NULL); }
/* * Write the specified lease to the current lease database file. */ int write_lease(struct lease *lease) { char tbuf[26]; /* "w yyyy/mm/dd hh:mm:ss UTC" */ size_t rsltsz; int errors = 0; int i; if (counting) ++count; if (fprintf(db_file, "lease %s {\n", piaddr(lease->ip_addr)) == -1) ++errors; rsltsz = strftime(tbuf, sizeof(tbuf), DB_TIMEFMT, gmtime(&lease->starts)); if (rsltsz == 0 || fprintf(db_file, "\tstarts %s;\n", tbuf) == -1) errors++; rsltsz = strftime(tbuf, sizeof(tbuf), DB_TIMEFMT, gmtime(&lease->ends)); if (rsltsz == 0 || fprintf(db_file, "\tends %s;\n", tbuf) == -1) errors++; if (lease->hardware_addr.hlen) { if (fprintf(db_file, "\thardware %s %s;", hardware_types[lease->hardware_addr.htype], print_hw_addr(lease->hardware_addr.htype, lease->hardware_addr.hlen, lease->hardware_addr.haddr)) == -1) ++errors; } if (lease->uid_len) { int j; if (fprintf(db_file, "\n\tuid %2.2x", lease->uid[0]) == -1) ++errors; for (j = 1; j < lease->uid_len; j++) { if (fprintf(db_file, ":%2.2x", lease->uid[j]) == -1) ++errors; } if (fputc(';', db_file) == EOF) ++errors; } if (lease->flags & BOOTP_LEASE) { if (fprintf(db_file, "\n\tdynamic-bootp;") == -1) ++errors; } if (lease->flags & ABANDONED_LEASE) { if (fprintf(db_file, "\n\tabandoned;") == -1) ++errors; } if (lease->client_hostname) { for (i = 0; lease->client_hostname[i]; i++) if (lease->client_hostname[i] < 33 || lease->client_hostname[i] > 126) goto bad_client_hostname; if (fprintf(db_file, "\n\tclient-hostname \"%s\";", lease->client_hostname) == -1) ++errors; } bad_client_hostname: if (lease->hostname) { for (i = 0; lease->hostname[i]; i++) if (lease->hostname[i] < 33 || lease->hostname[i] > 126) goto bad_hostname; if (fprintf(db_file, "\n\thostname \"%s\";", lease->hostname) == -1) ++errors; } bad_hostname: if (fputs("\n}\n", db_file) == EOF) ++errors; if (errors) note("write_lease: unable to write lease %s", piaddr(lease->ip_addr)); return (!errors); }
void bootp(struct packet *packet) { struct host_decl *hp, *host = NULL; struct packet outgoing; struct dhcp_packet raw; struct sockaddr_in to; struct in_addr from; struct tree_cache *options[256]; struct subnet *subnet = NULL; struct lease *lease; struct iaddr ip_address; int i; if (packet->raw->op != BOOTREQUEST) return; note("BOOTREQUEST from %s via %s%s", print_hw_addr(packet->raw->htype, packet->raw->hlen, packet->raw->chaddr), packet->raw->giaddr.s_addr ? inet_ntoa(packet->raw->giaddr) : packet->interface->name, packet->options_valid ? "" : " (non-rfc1048)"); if (!locate_network(packet)) return; hp = find_hosts_by_haddr(packet->raw->htype, packet->raw->chaddr, packet->raw->hlen); lease = find_lease(packet, packet->shared_network, 0); /* * Find an IP address in the host_decl that matches the specified * network. */ if (hp) subnet = find_host_for_network(&hp, &ip_address, packet->shared_network); if (!subnet) { /* * We didn't find an applicable host declaration. Just in case * we may be able to dynamically assign an address, see if * there's a host declaration that doesn't have an ip address * associated with it. */ if (hp) for (; hp; hp = hp->n_ipaddr) if (!hp->fixed_addr) { host = hp; break; } if (host && (!host->group->allow_booting)) { note("Ignoring excluded BOOTP client %s", host->name ? host->name : print_hw_addr (packet->raw->htype, packet->raw->hlen, packet->raw->chaddr)); return; } if (host && (!host->group->allow_bootp)) { note("Ignoring BOOTP request from client %s", host->name ? host->name : print_hw_addr(packet->raw->htype, packet->raw->hlen, packet->raw->chaddr)); return; } /* * If we've been told not to boot unknown clients, and we didn't * find any host record for this client, ignore it. */ if (!host && !(packet->shared_network->group->boot_unknown_clients)) { note("Ignoring unknown BOOTP client %s via %s", print_hw_addr(packet->raw->htype, packet->raw->hlen, packet->raw->chaddr), packet->raw->giaddr.s_addr ? inet_ntoa(packet->raw->giaddr) : packet->interface->name); return; } /* * If we've been told not to boot with bootp on this network, * ignore it. */ if (!host && !(packet->shared_network->group->allow_bootp)) { note("Ignoring BOOTP request from client %s via %s", print_hw_addr(packet->raw->htype, packet->raw->hlen, packet->raw->chaddr), packet->raw->giaddr.s_addr ? inet_ntoa(packet->raw->giaddr) : packet->interface->name); return; } /* * If the packet is from a host we don't know and there are no * dynamic bootp addresses on the network it came in on, drop * it on the floor. */ if (!(packet->shared_network->group->dynamic_bootp)) { lose: note("No applicable record for BOOTP host %s via %s", print_hw_addr(packet->raw->htype, packet->raw->hlen, packet->raw->chaddr), packet->raw->giaddr.s_addr ? inet_ntoa(packet->raw->giaddr) : packet->interface->name); return; } /* * If a lease has already been assigned to this client and it's * still okay to use dynamic bootp on that lease, reassign it. */ if (lease) { /* * If this lease can be used for dynamic bootp, do so. */ if ((lease->flags & DYNAMIC_BOOTP_OK)) { /* * If it's not a DYNAMIC_BOOTP lease, release it * before reassigning it so that we don't get a * lease conflict. */ if (!(lease->flags & BOOTP_LEASE)) release_lease(lease); lease->host = host; ack_lease(packet, lease, 0, 0); return; } /* * If dynamic BOOTP is no longer allowed for this * lease, set it free. */ release_lease(lease); } /* * If there are dynamic bootp addresses that might be * available, try to snag one. */ for (lease = packet->shared_network->last_lease; lease && lease->ends <= cur_time; lease = lease->prev) { if ((lease->flags & DYNAMIC_BOOTP_OK)) { lease->host = host; ack_lease(packet, lease, 0, 0); return; } } goto lose; } /* Make sure we're allowed to boot this client. */ if (hp && (!hp->group->allow_booting)) { note("Ignoring excluded BOOTP client %s", hp->name); return; } /* Make sure we're allowed to boot this client with bootp. */ if (hp && (!hp->group->allow_bootp)) { note("Ignoring BOOTP request from client %s", hp->name); return; } /* Set up the outgoing packet... */ memset(&outgoing, 0, sizeof outgoing); memset(&raw, 0, sizeof raw); outgoing.raw = &raw; /* * If we didn't get a known vendor magic number on the way in, just * copy the input options to the output. */ if (!packet->options_valid && !subnet->group->always_reply_rfc1048 && (!hp || !hp->group->always_reply_rfc1048)) { memcpy(outgoing.raw->options, packet->raw->options, DHCP_OPTION_LEN); outgoing.packet_length = BOOTP_MIN_LEN; } else { struct tree_cache netmask_tree; /* -- RBF */ /* * Come up with a list of options that we want to send to this * client. Start with the per-subnet options, and then override * those with client-specific options. */ memcpy(options, subnet->group->options, sizeof(options)); for (i = 0; i < 256; i++) if (hp->group->options[i]) options[i] = hp->group->options[i]; /* * Use the subnet mask from the subnet declaration if no other * mask has been provided. */ if (!options[DHO_SUBNET_MASK]) { options[DHO_SUBNET_MASK] = &netmask_tree; netmask_tree.flags = TC_TEMPORARY; netmask_tree.value = lease->subnet->netmask.iabuf; netmask_tree.len = lease->subnet->netmask.len; netmask_tree.buf_size = lease->subnet->netmask.len; netmask_tree.timeout = -1; netmask_tree.tree = NULL; } /* * Pack the options into the buffer. Unlike DHCP, we can't pack * options into the filename and server name buffers. */ outgoing.packet_length = cons_options(packet, outgoing.raw, 0, options, 0, 0, 1, NULL, 0); if (outgoing.packet_length < BOOTP_MIN_LEN) outgoing.packet_length = BOOTP_MIN_LEN; } /* Take the fields that we care about... */ raw.op = BOOTREPLY; raw.htype = packet->raw->htype; raw.hlen = packet->raw->hlen; memcpy(raw.chaddr, packet->raw->chaddr, sizeof(raw.chaddr)); raw.hops = packet->raw->hops; raw.xid = packet->raw->xid; raw.secs = packet->raw->secs; raw.flags = packet->raw->flags; raw.ciaddr = packet->raw->ciaddr; memcpy(&raw.yiaddr, ip_address.iabuf, sizeof(raw.yiaddr)); /* Figure out the address of the next server. */ if (hp && hp->group->next_server.len) memcpy(&raw.siaddr, hp->group->next_server.iabuf, 4); else if (subnet->group->next_server.len) memcpy(&raw.siaddr, subnet->group->next_server.iabuf, 4); else if (subnet->interface_address.len) memcpy(&raw.siaddr, subnet->interface_address.iabuf, 4); else raw.siaddr = packet->interface->primary_address; raw.giaddr = packet->raw->giaddr; if (hp->group->server_name) strncpy(raw.sname, hp->group->server_name, sizeof(raw.sname)); else if (subnet->group->server_name) strncpy(raw.sname, subnet->group->server_name, sizeof(raw.sname)); if (hp->group->filename) strncpy(raw.file, hp->group->filename, sizeof(raw.file)); else if (subnet->group->filename) strncpy(raw.file, subnet->group->filename, sizeof(raw.file)); else memcpy(raw.file, packet->raw->file, sizeof(raw.file)); from = packet->interface->primary_address; /* Report what we're doing... */ note("BOOTREPLY for %s to %s (%s) via %s", piaddr(ip_address), hp->name, print_hw_addr(packet->raw->htype, packet->raw->hlen, packet->raw->chaddr), packet->raw->giaddr.s_addr ? inet_ntoa(packet->raw->giaddr) : packet->interface->name); /* Set up the parts of the address that are in common. */ memset(&to, 0, sizeof(to)); to.sin_family = AF_INET; #ifdef HAVE_SA_LEN to.sin_len = sizeof(to); #endif /* If this was gatewayed, send it back to the gateway... */ if (raw.giaddr.s_addr) { to.sin_addr = raw.giaddr; to.sin_port = server_port; (void) send_packet(packet->interface, &raw, outgoing.packet_length, from, &to, packet->haddr); return; } /* * If it comes from a client that already knows its address and is not * requesting a broadcast response, and we can unicast to a client * without using the ARP protocol, sent it directly to that client. */ else if (!(raw.flags & htons(BOOTP_BROADCAST))) { to.sin_addr = raw.yiaddr; to.sin_port = client_port; } else { /* Otherwise, broadcast it on the local network. */ to.sin_addr.s_addr = INADDR_BROADCAST; to.sin_port = client_port; /* XXX */ } errno = 0; (void) send_packet(packet->interface, &raw, outgoing.packet_length, from, &to, packet->haddr); }
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); }