static uint32_t find_free_or_expired_nip(GDHCPServer *dhcp_server, const uint8_t *safe_mac) { uint32_t ip_addr; struct dhcp_lease *lease; GList *list; ip_addr = dhcp_server->start_ip; for (; ip_addr <= dhcp_server->end_ip; ip_addr++) { /* e.g. 192.168.55.0 */ if ((ip_addr & 0xff) == 0) continue; /* e.g. 192.168.55.255 */ if ((ip_addr & 0xff) == 0xff) continue; lease = find_lease_by_nip(dhcp_server, (uint32_t) htonl(ip_addr)); if (lease != NULL) continue; if (arp_check(htonl(ip_addr), safe_mac) == TRUE) return htonl(ip_addr); } /* The last lease is the oldest one */ list = g_list_last(dhcp_server->lease_list); if (list == NULL) return 0; lease = list->data; if (lease == NULL) return 0; if (is_expired_lease(lease) == FALSE) return 0; if (arp_check(lease->lease_nip, safe_mac) == FALSE) return 0; return lease->lease_nip; }
void net_bridge_wait_ip(Bridge *br) { assert(br); if (br->configured == 0 || br->arg_ip_none) return; // wait for the ip address to come up int cnt = 0; while (cnt < 5) { // arp_check has a 1s wait if (arp_check(br->dev, br->ipsandbox, br->ip) == 0) break; cnt++; } }
// assign a random IP address and check it // the address needs to be in the range if it --iprange was specified static uint32_t arp_random(const char *dev, Bridge *br) { assert(dev); assert(br); uint32_t ifip = br->ip; uint32_t ifmask = br->mask; assert(ifip); assert(ifmask); if (arg_debug) printf("ARP-scan %s, %d.%d.%d.%d/%d\n", dev, PRINT_IP(ifip), mask2bits(ifmask)); // determine the range based on network address uint32_t range = ~ifmask + 1; // the number of potential addresses // this software is not supported for /31 networks if (range < 4) return 0; // the user will have to set the IP address manually range -= 2; // subtract the network address and the broadcast address uint32_t start = (ifip & ifmask) + 1; // adjust range based on --iprange params if (br->iprange_start && br->iprange_end) { start = br->iprange_start; range = br->iprange_end - br->iprange_start; } if (arg_debug) printf("IP address range from %d.%d.%d.%d to %d.%d.%d.%d\n", PRINT_IP(start), PRINT_IP(start + range)); // generate a random address - 10 tries uint32_t dest = 0; int i = 0; for (i = 0; i < 10; i++) { dest = start + ((uint32_t) rand()) % range; if (dest == ifip) // do not allow the interface address continue; // try again // if we've made it up to here, we have a valid address break; } if (i == 10) // we failed 10 times return 0; // check address uint32_t rv = arp_check(dev, dest, ifip); if (!rv) return dest; return 0; }
static void sandbox_if_up(Bridge *br) { assert(br); if (!br->configured) return; char *dev = br->devsandbox; net_if_up(dev); if (br->arg_ip_none == 1); // do nothing else if (br->arg_ip_none == 0 && br->macvlan == 0) { if (br->ipsandbox == br->ip) { fprintf(stderr, "Error: %d.%d.%d.%d is interface %s address.\n", PRINT_IP(br->ipsandbox), br->dev); exit(1); } // just assign the address assert(br->ipsandbox); if (arg_debug) printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(br->ipsandbox), dev); net_if_ip(dev, br->ipsandbox, br->mask, br->mtu); net_if_up(dev); } else if (br->arg_ip_none == 0 && br->macvlan == 1) { // reassign the macvlan address if (br->ipsandbox == 0) // ip address assigned by arp-scan for a macvlan device br->ipsandbox = arp_assign(dev, br); //br->ip, br->mask); else { if (br->ipsandbox == br->ip) { fprintf(stderr, "Error: %d.%d.%d.%d is interface %s address.\n", PRINT_IP(br->ipsandbox), br->dev); exit(1); } uint32_t rv = arp_check(dev, br->ipsandbox, br->ip); if (rv) { fprintf(stderr, "Error: the address %d.%d.%d.%d is already in use.\n", PRINT_IP(br->ipsandbox)); exit(1); } } if (arg_debug) printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(br->ipsandbox), dev); net_if_ip(dev, br->ipsandbox, br->mask, br->mtu); net_if_up(dev); } if (br->ip6sandbox) net_if_ip6(dev, br->ip6sandbox); }
// go sequentially trough all IP addresses and assign the first one not in use static uint32_t arp_sequential(const char *dev, Bridge *br) { assert(dev); assert(br); uint32_t ifip = br->ip; uint32_t ifmask = br->mask; assert(ifip); assert(ifmask); // range based on network address uint32_t range = ~ifmask + 1; // the number of potential addresses // this software is not supported for /31 networks if (range < 4) return 0; // the user will have to set the IP address manually range -= 2; // subtract the network address and the broadcast address // try all possible ip addresses in ascending order // start address uint32_t dest = (ifip & ifmask) + 1; if (br->iprange_start) dest = br->iprange_start; // end address uint32_t last = dest + range - 1; if (br->iprange_end) last = br->iprange_end; if (arg_debug) printf("Trying IP address range from %d.%d.%d.%d to %d.%d.%d.%d\n", PRINT_IP(dest), PRINT_IP(last)); // loop through addresses and stop as soon as you find an unused one while (dest <= last) { if (dest == ifip) { dest++; continue; } uint32_t rv = arp_check(dev, dest, ifip); if (!rv) return dest; dest++; } return 0; }
void net_configure_sandbox_ip(Bridge *br) { assert(br); if (br->configured == 0) return; if (br->arg_ip_none) br->ipsandbox = 0; else if (br->ipsandbox) { // check network range char *rv = in_netrange(br->ipsandbox, br->ip, br->mask); if (rv) { fprintf(stderr, "%s", rv); exit(1); } // send an ARP request and check if there is anybody on this IP address if (arp_check(br->dev, br->ipsandbox, br->ip)) { fprintf(stderr, "Error: IP address %d.%d.%d.%d is already in use\n", PRINT_IP(br->ipsandbox)); exit(1); } } else br->ipsandbox = arp_assign(br->dev, br->ip, br->mask); }
/* This state machine is based on the one from udhcpc written by Russ Dill */ int dhcp_run (options_t *options) { interface_t *iface; int mode = SOCKET_CLOSED; int state = STATE_INIT; struct timeval tv; int xid = 0; long timeout = 0; fd_set rset; int maxfd; int retval; dhcpmessage_t message; dhcp_t *dhcp; int type = DHCP_DISCOVER; int last_type = DHCP_DISCOVER; bool daemonised = false; unsigned long start = 0; unsigned long last_send = 0; int sig; unsigned char *buffer = NULL; int buffer_len = 0; int buffer_pos = 0; if (! options || (iface = (read_interface (options->interface, options->metric))) == NULL) return -1; /* Remove all existing addresses. After all, we ARE a DHCP client whose job it is to configure the interface. We only do this on start, so persistent addresses can be added afterwards by the user if needed. */ flush_addresses (iface->name); dhcp = xmalloc (sizeof (dhcp_t)); memset (dhcp, 0, sizeof (dhcp_t)); strcpy (dhcp->classid, options->classid); if (options->clientid[0]) strcpy (dhcp->clientid, options->clientid); else sprintf (dhcp->clientid, "%s", ether_ntoa (&iface->ethernet_address)); if (options->requestaddress.s_addr != 0) dhcp->address.s_addr = options->requestaddress.s_addr; signal_setup (); while (1) { if (timeout > 0 || (options->timeout == 0 && (state != STATE_INIT || xid))) { if (options->timeout == 0 || dhcp->leasetime == -1) { logger (LOG_DEBUG, "waiting on select for infinity"); maxfd = signal_fd_set (&rset, iface->fd); retval = select (maxfd + 1, &rset, NULL, NULL, NULL); } else { /* Resend our message if we're getting loads of packets that aren't for us. This mainly happens on Linux as it doesn't have a nice BPF filter. */ if (iface->fd > -1 && uptime () - last_send >= TIMEOUT_MINI) SEND_MESSAGE (last_type); logger (LOG_DEBUG, "waiting on select for %d seconds", timeout); /* If we're waiting for a reply, then we re-send the last DHCP request periodically in-case of a bad line */ retval = 0; while (timeout > 0 && retval == 0) { if (iface->fd == -1) tv.tv_sec = SELECT_MAX; else tv.tv_sec = TIMEOUT_MINI; if (timeout < tv.tv_sec) tv.tv_sec = timeout; tv.tv_usec = 0; start = uptime (); maxfd = signal_fd_set (&rset, iface->fd); retval = select (maxfd + 1, &rset, NULL, NULL, &tv); timeout -= uptime () - start; if (retval == 0 && iface->fd != -1 && timeout > 0) SEND_MESSAGE (last_type); } } } else retval = 0; /* We should always handle our signals first */ if (retval > 0 && (sig = signal_read (&rset))) { switch (sig) { case SIGINT: logger (LOG_INFO, "receieved SIGINT, stopping"); retval = 0; goto eexit; case SIGTERM: logger (LOG_INFO, "receieved SIGTERM, stopping"); retval = 0; goto eexit; case SIGALRM: logger (LOG_INFO, "receieved SIGALRM, renewing lease"); switch (state) { case STATE_BOUND: case STATE_RENEWING: case STATE_REBINDING: state = STATE_RENEW_REQUESTED; break; case STATE_RENEW_REQUESTED: case STATE_REQUESTING: case STATE_RELEASED: state = STATE_INIT; break; } timeout = 0; xid = 0; break; case SIGHUP: if (state == STATE_BOUND || state == STATE_RENEWING || state == STATE_REBINDING) { logger (LOG_INFO, "received SIGHUP, releasing lease"); SOCKET_MODE (SOCKET_OPEN); xid = random_xid (); if ((open_socket (iface, false)) >= 0) SEND_MESSAGE (DHCP_RELEASE); SOCKET_MODE (SOCKET_CLOSED); unlink (iface->infofile); } else logger (LOG_ERR, "receieved SIGUP, but no we have lease to release"); retval = 0; goto eexit; default: logger (LOG_ERR, "received signal %d, but don't know what to do with it", sig); } } else if (retval == 0) /* timed out */ { switch (state) { case STATE_INIT: if (iface->previous_address.s_addr != 0) { logger (LOG_ERR, "lost lease"); xid = 0; SOCKET_MODE (SOCKET_CLOSED); if (! options->persistent) { free_dhcp (dhcp); memset (dhcp, 0, sizeof (dhcp_t)); configure (options, iface, dhcp); } if (! daemonised) { retval = -1; goto eexit; } break; } if (xid == 0) xid = random_xid (); else { logger (LOG_ERR, "timed out"); if (! daemonised) { retval = -1; goto eexit; } } SOCKET_MODE (SOCKET_OPEN); timeout = options->timeout; iface->start_uptime = uptime (); if (dhcp->address.s_addr == 0) { logger (LOG_INFO, "broadcasting for a lease"); SEND_MESSAGE (DHCP_DISCOVER); } else { logger (LOG_INFO, "broadcasting for a lease of %s", inet_ntoa (dhcp->address)); SEND_MESSAGE (DHCP_REQUEST); state = STATE_REQUESTING; } break; case STATE_BOUND: case STATE_RENEW_REQUESTED: state = STATE_RENEWING; xid = random_xid (); case STATE_RENEWING: iface->start_uptime = uptime (); logger (LOG_INFO, "renewing lease of %s", inet_ntoa (dhcp->address)); SOCKET_MODE (SOCKET_OPEN); SEND_MESSAGE (DHCP_REQUEST); timeout = dhcp->rebindtime - dhcp->renewaltime; state = STATE_REBINDING; break; case STATE_REBINDING: logger (LOG_ERR, "lost lease, attemping to rebind"); SOCKET_MODE (SOCKET_OPEN); SEND_MESSAGE (DHCP_DISCOVER); timeout = dhcp->leasetime - dhcp->rebindtime; state = STATE_INIT; break; case STATE_REQUESTING: logger (LOG_ERR, "timed out"); if (! daemonised) goto eexit; state = STATE_INIT; SOCKET_MODE (SOCKET_CLOSED); timeout = 0; xid = 0; free_dhcp (dhcp); memset (dhcp, 0, sizeof (dhcp_t)); break; case STATE_RELEASED: dhcp->leasetime = -1; break; } } else if (retval > 0 && mode != SOCKET_CLOSED && FD_ISSET(iface->fd, &rset)) { /* Allocate our buffer space for BPF. We cannot do this until we have opened our socket as we don't know how much of a buffer we need until then. */ if (! buffer) buffer = xmalloc (iface->buffer_length); buffer_len = iface->buffer_length; buffer_pos = -1; /* We loop through until our buffer is empty. The benefit is that if we get >1 DHCP packet in our buffer and the first one fails for any reason, we can use the next. */ memset (&message, 0, sizeof (struct dhcpmessage_t)); int valid = 0; struct dhcp_t *new_dhcp; new_dhcp = xmalloc (sizeof (dhcp_t)); while (buffer_pos != 0) { if (get_packet (iface, (unsigned char *) &message, buffer, &buffer_len, &buffer_pos) < 0) break; if (xid != message.xid) { logger (LOG_ERR, "ignoring packet with xid %d as it's not ours (%d)", message.xid, xid); continue; } logger (LOG_DEBUG, "got a packet with xid %d", message.xid); memset (new_dhcp, 0, sizeof (dhcp_t)); if ((type = parse_dhcpmessage (new_dhcp, &message)) < 0) { logger (LOG_ERR, "failed to parse packet"); free_dhcp (new_dhcp); continue; } /* If we got here then the DHCP packet is valid and appears to be for us, so let's clear the buffer as we don't care about any more DHCP packets at this point. */ valid = 1; break; } /* No packets for us, so wait until we get one */ if (! valid) { free (new_dhcp); continue; } /* new_dhcp is now our master DHCP message */ free_dhcp (dhcp); free (dhcp); dhcp = new_dhcp; new_dhcp = NULL; /* We should restart on a NAK */ if (type == DHCP_NAK) { logger (LOG_INFO, "received NAK: %s", dhcp->message); state = STATE_INIT; timeout = 0; xid = 0; free_dhcp (dhcp); memset (dhcp, 0, sizeof (dhcp_t)); configure (options, iface, dhcp); continue; } switch (state) { case STATE_INIT: if (type == DHCP_OFFER) { logger (LOG_INFO, "offered lease of %s", inet_ntoa (dhcp->address)); SEND_MESSAGE (DHCP_REQUEST); state = STATE_REQUESTING; } break; case STATE_RENEW_REQUESTED: case STATE_REQUESTING: case STATE_RENEWING: case STATE_REBINDING: if (type == DHCP_ACK) { SOCKET_MODE (SOCKET_CLOSED); if (options->doarp && iface->previous_address.s_addr != dhcp->address.s_addr) { if (arp_check (iface, dhcp->address)) { SOCKET_MODE (SOCKET_OPEN); SEND_MESSAGE (DHCP_DECLINE); SOCKET_MODE (SOCKET_CLOSED); free_dhcp (dhcp); memset (dhcp, 0, sizeof (dhcp)); if (daemonised) configure (options, iface, dhcp); xid = 0; state = STATE_INIT; /* RFC 2131 says that we should wait for 10 seconds before doing anything else */ sleep (10); continue; } } if (! dhcp->leasetime) { dhcp->leasetime = DEFAULT_TIMEOUT; logger(LOG_INFO, "no lease time supplied, assuming %d seconds", dhcp->leasetime); } if (! dhcp->renewaltime) { dhcp->renewaltime = dhcp->leasetime / 2; logger (LOG_INFO, "no renewal time supplied, assuming %d seconds", dhcp->renewaltime); } if (! dhcp->rebindtime) { dhcp->rebindtime = (dhcp->leasetime * 0x7) >> 3; logger (LOG_INFO, "no rebind time supplied, assuming %d seconds", dhcp->rebindtime); } if (dhcp->leasetime == -1) logger (LOG_INFO, "leased %s for infinity", inet_ntoa (dhcp->address)); else logger (LOG_INFO, "leased %s for %u seconds", inet_ntoa (dhcp->address), dhcp->leasetime, dhcp->renewaltime); state = STATE_BOUND; timeout = dhcp->renewaltime; xid = 0; if (configure (options, iface, dhcp) < 0 && ! daemonised) { retval = -1; goto eexit; } if (! daemonised) { if ((daemonise (options->pidfile)) < 0 ) { retval = -1; goto eexit; } daemonised = true; } } else if (type == DHCP_OFFER) logger (LOG_INFO, "got subsequent offer of %s, ignoring ", inet_ntoa (dhcp->address)); else logger (LOG_ERR, "no idea what to do with DHCP type %d at this point", type); break; }