int locate_network(struct packet *packet) { struct iaddr ia; /* If this came through a gateway, find the corresponding subnet... */ if (packet->raw->giaddr.s_addr) { struct subnet *subnet; ia.len = 4; memcpy(ia.iabuf, &packet->raw->giaddr, 4); subnet = find_subnet(ia); if (subnet) packet->shared_network = subnet->shared_network; else packet->shared_network = NULL; } else { packet->shared_network = packet->interface->shared_network; } if (packet->shared_network) return 1; return 0; }
void udpsock_handler(struct protocol *protocol) { int sockio; char cbuf[256], ifname[IF_NAMESIZE]; ssize_t len; struct udpsock *udpsock = protocol->local; struct msghdr m; struct cmsghdr *cm; struct iovec iov[1]; struct sockaddr_storage ss; struct sockaddr_in *sin4; struct sockaddr_dl *sdl = NULL; struct interface_info iface; struct iaddr from, addr; unsigned char packetbuf[4095]; struct dhcp_packet *packet = (struct dhcp_packet *)packetbuf; struct hardware hw; struct ifreq ifr; struct subnet *subnet; memset(&hw, 0, sizeof(hw)); iov[0].iov_base = packetbuf; iov[0].iov_len = sizeof(packetbuf); memset(&m, 0, sizeof(m)); m.msg_name = &ss; m.msg_namelen = sizeof(ss); m.msg_iov = iov; m.msg_iovlen = 1; m.msg_control = cbuf; m.msg_controllen = sizeof(cbuf); memset(&iface, 0, sizeof(iface)); if ((len = recvmsg(udpsock->sock, &m, 0)) < 0) { warning("receiving a DHCP message failed: %s", strerror(errno)); return; } if (ss.ss_family != AF_INET) { warning("received DHCP message is not AF_INET"); return; } sin4 = (struct sockaddr_in *)&ss; for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&m); m.msg_controllen != 0 && cm; cm = (struct cmsghdr *)CMSG_NXTHDR(&m, cm)) { if (cm->cmsg_level == IPPROTO_IP && cm->cmsg_type == IP_RECVIF) sdl = (struct sockaddr_dl *)CMSG_DATA(cm); } if (sdl == NULL) { warning("could not get the received interface by IP_RECVIF"); return; } if_indextoname(sdl->sdl_index, ifname); if ((sockio = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { warning("socket creation failed: %s", strerror(errno)); return; } strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(sockio, SIOCGIFADDR, &ifr, sizeof(ifr)) != 0) { warning("Failed to get address for %s: %s", ifname, strerror(errno)); close(sockio); return; } close(sockio); if (ifr.ifr_addr.sa_family != AF_INET) return; iface.is_udpsock = 1; iface.send_packet = udpsock_send_packet; iface.wfdesc = udpsock->sock; iface.ifp = 𝔦 iface.index = sdl->sdl_index; iface.primary_address = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr; strlcpy(iface.name, ifname, sizeof(iface.name)); addr.len = 4; memcpy(&addr.iabuf, &iface.primary_address, addr.len); if ((subnet = find_subnet(addr)) == NULL) return; iface.shared_network = subnet->shared_network ; from.len = 4; memcpy(&from.iabuf, &sin4->sin_addr, from.len); do_packet(&iface, packet, len, sin4->sin_port, from, &hw); }
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); }
void discover_interfaces(int *rdomain) { struct interface_info *tmp; struct interface_info *last, *next; struct subnet *subnet; struct shared_network *share; struct sockaddr_in foo; int ir = 0, ird; struct ifreq *tif; struct ifaddrs *ifap, *ifa; if (getifaddrs(&ifap) != 0) error("getifaddrs failed"); /* * If we already have a list of interfaces, the interfaces were * requested. */ if (interfaces != NULL) ir = 1; else /* must specify an interface when rdomains are used */ *rdomain = 0; /* Cycle through the list of interfaces looking for IP addresses. */ for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { /* * See if this is the sort of interface we want to * deal with. Skip loopback, point-to-point and down * interfaces, except don't skip down interfaces if we're * trying to get a list of configurable interfaces. */ if ((ifa->ifa_flags & IFF_LOOPBACK) || (ifa->ifa_flags & IFF_POINTOPOINT) || (!(ifa->ifa_flags & IFF_UP)) || (!(ifa->ifa_flags & IFF_BROADCAST))) continue; /* See if we've seen an interface that matches this one. */ for (tmp = interfaces; tmp; tmp = tmp->next) if (!strcmp(tmp->name, ifa->ifa_name)) break; /* If we are looking for specific interfaces, ignore others. */ if (tmp == NULL && ir) continue; ird = get_rdomain(ifa->ifa_name); if (*rdomain == -1) *rdomain = ird; else if (*rdomain != ird && ir) error("Interface %s is not in rdomain %d", tmp->name, *rdomain); else if (*rdomain != ird && !ir) continue; /* If there isn't already an interface by this name, allocate one. */ if (tmp == NULL) { tmp = calloc(1, sizeof *tmp); if (!tmp) error("Insufficient memory to %s %s", "record interface", ifa->ifa_name); strlcpy(tmp->name, ifa->ifa_name, sizeof(tmp->name)); tmp->next = interfaces; tmp->noifmedia = tmp->dead = tmp->errors = 0; interfaces = tmp; } /* If we have the capability, extract link information and record it in a linked list. */ if (ifa->ifa_addr->sa_family == AF_LINK) { struct sockaddr_dl *foo = ((struct sockaddr_dl *)(ifa->ifa_addr)); tmp->index = foo->sdl_index; tmp->hw_address.hlen = foo->sdl_alen; tmp->hw_address.htype = HTYPE_ETHER; /* XXX */ memcpy(tmp->hw_address.haddr, LLADDR(foo), foo->sdl_alen); } else if (ifa->ifa_addr->sa_family == AF_INET) { struct iaddr addr; /* Get a pointer to the address... */ memcpy(&foo, ifa->ifa_addr, sizeof(foo)); /* We don't want the loopback interface. */ if (foo.sin_addr.s_addr == htonl (INADDR_LOOPBACK)) continue; /* If this is the first real IP address we've found, keep a pointer to ifreq structure in which we found it. */ if (!tmp->ifp) { int len = (IFNAMSIZ + ifa->ifa_addr->sa_len); tif = (struct ifreq *)malloc(len); if (!tif) error("no space to remember ifp."); strlcpy(tif->ifr_name, ifa->ifa_name, IFNAMSIZ); memcpy(&tif->ifr_addr, ifa->ifa_addr, ifa->ifa_addr->sa_len); tmp->ifp = tif; tmp->primary_address = foo.sin_addr; } /* Grab the address... */ addr.len = 4; memcpy(addr.iabuf, &foo.sin_addr.s_addr, addr.len); /* If there's a registered subnet for this address, connect it together... */ if ((subnet = find_subnet(addr))) { /* If this interface has multiple aliases on the same subnet, ignore all but the first we encounter. */ if (!subnet->interface) { subnet->interface = tmp; subnet->interface_address = addr; } else if (subnet->interface != tmp) { warning("Multiple %s %s: %s %s", "interfaces match the", "same subnet", subnet->interface->name, tmp->name); } share = subnet->shared_network; if (tmp->shared_network && tmp->shared_network != share) { warning("Interface %s matches %s", tmp->name, "multiple shared networks"); } else { tmp->shared_network = share; } if (!share->interface) { share->interface = tmp; } else if (share->interface != tmp) { warning("Multiple %s %s: %s %s", "interfaces match the", "same shared network", share->interface->name, tmp->name); } } } } /* Discard interfaces we can't listen on. */ last = NULL; for (tmp = interfaces; tmp; tmp = next) { next = tmp->next; if (!tmp->ifp) { warning("Can't listen on %s - it has no IP address.", tmp->name); /* Remove tmp from the list of interfaces. */ if (!last) interfaces = interfaces->next; else last->next = tmp->next; continue; } memcpy(&foo, &tmp->ifp->ifr_addr, sizeof tmp->ifp->ifr_addr); if (!tmp->shared_network) { warning("Can't listen on %s - dhcpd.conf has no subnet " "declaration for %s.", tmp->name, inet_ntoa(foo.sin_addr)); /* Remove tmp from the list of interfaces. */ if (!last) interfaces = interfaces->next; else last->next = tmp->next; continue; } last = tmp; /* Find subnets that don't have valid interface addresses. */ for (subnet = (tmp->shared_network ? tmp->shared_network->subnets : NULL); subnet; subnet = subnet->next_sibling) { if (!subnet->interface_address.len) { /* * Set the interface address for this subnet * to the first address we found. */ subnet->interface_address.len = 4; memcpy(subnet->interface_address.iabuf, &foo.sin_addr.s_addr, 4); } } /* Register the interface... */ if_register_receive(tmp); if_register_send(tmp); note("Listening on %s (%s).", tmp->name, inet_ntoa(foo.sin_addr)); } if (interfaces == NULL) error("No interfaces to listen on."); /* Now register all the remaining interfaces as protocols. */ for (tmp = interfaces; tmp; tmp = tmp->next) add_protocol(tmp->name, tmp->rfdesc, got_one, tmp); freeifaddrs(ifap); }