Example #1
0
static int
dhcp_finish_expire(dhcp_smach_t *dsmp, void *arg)
{
	dhcp_lif_t *lif = arg;
	dhcp_lease_t *dlp;

	dhcpmsg(MSG_DEBUG, "lease expired on %s; removing", lif->lif_name);

	dlp = lif->lif_lease;
	unplumb_lif(lif);
	if (dlp->dl_nlifs == 0)
		remove_lease(dlp);
	release_lif(lif);

	/* If some valid leases remain, then drive on */
	if (dsmp->dsm_leases != NULL) {
		dhcpmsg(MSG_DEBUG,
		    "dhcp_finish_expire: some leases remain on %s",
		    dsmp->dsm_name);
		return (1);
	}

	(void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);

	dhcpmsg(MSG_INFO, "last lease expired on %s -- restarting DHCP",
	    dsmp->dsm_name);

	/*
	 * in the case where the lease is less than DHCP_REBIND_MIN
	 * seconds, we will never enter dhcp_renew() and thus the packet
	 * counters will not be reset.  in that case, reset them here.
	 */

	if (dsmp->dsm_state == BOUND) {
		dsmp->dsm_bad_offers	= 0;
		dsmp->dsm_sent		= 0;
		dsmp->dsm_received	= 0;
	}

	deprecate_leases(dsmp);

	/* reset_smach() in dhcp_selecting() will clean up any leftover state */
	dhcp_selecting(dsmp);

	return (1);
}
Example #2
0
File: server.c Project: rzr/connman
static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
							gpointer user_data)
{
	GDHCPServer *dhcp_server = user_data;
	struct dhcp_packet packet;
	struct dhcp_lease *lease;
	uint32_t requested_nip = 0;
	uint8_t type, *server_id_option, *request_ip_option, *host_name;
	int re;

	GDHCPOptionType option_type;
	char *option_value;

	if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
		dhcp_server->listener_watch = 0;
		return FALSE;
	}

	re = dhcp_recv_l3_packet(&packet, dhcp_server->listener_sockfd);
	if (re < 0)
		return TRUE;

	type = check_packet_type(&packet);
	if (type == 0)
		return TRUE;

	server_id_option = dhcp_get_option(&packet, DHCP_SERVER_ID);
	if (server_id_option) {
		uint32_t server_nid = get_be32(server_id_option);

		if (server_nid != dhcp_server->server_nip)
			return TRUE;
	}

	request_ip_option = dhcp_get_option(&packet, DHCP_REQUESTED_IP);
	if (request_ip_option)
		requested_nip = get_be32(request_ip_option);

	lease = find_lease_by_mac(dhcp_server, packet.chaddr);

	switch (type) {
	case DHCPDISCOVER:
		debug(dhcp_server, "Received DISCOVER");

		send_offer(dhcp_server, &packet, lease, requested_nip);
		break;
	case DHCPREQUEST:
		debug(dhcp_server, "Received REQUEST NIP %d",
							requested_nip);
		if (requested_nip == 0) {
			requested_nip = packet.ciaddr;
			if (requested_nip == 0)
				break;
		}

		if (lease && requested_nip == lease->lease_nip) {
			debug(dhcp_server, "Sending ACK");
			host_name =
				dhcp_get_option(&packet,
					 DHCP_HOST_NAME);
			option_type =
				dhcp_get_code_type(DHCP_HOST_NAME);
			option_value =
				malloc_option_value_string(host_name,
					 option_type);

			send_ACK(dhcp_server, &packet,
				lease->lease_nip);

			if (dhcp_server->save_ack_lease_func)
				dhcp_server->save_ack_lease_func(
					option_value,
					lease->lease_mac,
					lease->lease_nip);

			g_free(option_value);

			break;
		}

		if (server_id_option || !lease) {
			debug(dhcp_server, "Sending NAK");
			send_NAK(dhcp_server, &packet);
		}

		break;
	case DHCPDECLINE:
		debug(dhcp_server, "Received DECLINE");

		if (!server_id_option)
			break;

		if (!request_ip_option)
			break;

		if (!lease)
			break;

		if (requested_nip == lease->lease_nip)
			remove_lease(dhcp_server, lease);

		break;
	case DHCPRELEASE:
		debug(dhcp_server, "Received RELEASE");

		if (!server_id_option)
			break;

		if (!lease)
			break;

		if (packet.ciaddr == lease->lease_nip)
			lease_set_expire(dhcp_server, lease,
					time(NULL));
		break;
	case DHCPINFORM:
		debug(dhcp_server, "Received INFORM");
		send_inform(dhcp_server, &packet);
		break;
	}

	return TRUE;
}
Example #3
0
File: server.c Project: rzr/connman
/* Clear the old lease and create the new one */
static int get_lease(GDHCPServer *dhcp_server, uint32_t yiaddr,
				const uint8_t *mac, struct dhcp_lease **lease)
{
	struct dhcp_lease *lease_nip, *lease_mac;

	if (yiaddr == 0)
		return -ENXIO;

	if (ntohl(yiaddr) < dhcp_server->start_ip)
		return -ENXIO;

	if (ntohl(yiaddr) > dhcp_server->end_ip)
		return -ENXIO;

	if (memcmp(mac, MAC_BCAST_ADDR, ETH_ALEN) == 0)
		return -ENXIO;

	if (memcmp(mac, MAC_ANY_ADDR, ETH_ALEN) == 0)
		return -ENXIO;

	lease_mac = find_lease_by_mac(dhcp_server, mac);

	lease_nip = g_hash_table_lookup(dhcp_server->nip_lease_hash,
					GINT_TO_POINTER((int) ntohl(yiaddr)));
	debug(dhcp_server, "lease_mac %p lease_nip %p", lease_mac, lease_nip);

	if (lease_nip) {
		dhcp_server->lease_list =
				g_list_remove(dhcp_server->lease_list,
								lease_nip);
		g_hash_table_remove(dhcp_server->nip_lease_hash,
				GINT_TO_POINTER((int) ntohl(yiaddr)));

		if (!lease_mac)
			*lease = lease_nip;
		else if (lease_nip != lease_mac) {
			remove_lease(dhcp_server, lease_mac);
			*lease = lease_nip;
		} else
			*lease = lease_nip;

		return 0;
	}

	if (lease_mac) {
		dhcp_server->lease_list =
				g_list_remove(dhcp_server->lease_list,
								lease_mac);
		g_hash_table_remove(dhcp_server->nip_lease_hash,
				GINT_TO_POINTER((int) lease_mac->lease_nip));
		*lease = lease_mac;

		return 0;
	}

	*lease = g_try_new0(struct dhcp_lease, 1);
	if (!*lease)
		return -ENOMEM;

	return 0;
}
Example #4
0
static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
							gpointer user_data)
{
	GDHCPServer *dhcp_server = user_data;
	struct dhcp_packet packet;
	struct dhcp_lease *lease;
	uint32_t requested_nip = 0;
	uint8_t type, *server_id_option, *request_ip_option;
	int re;

	if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
		dhcp_server->listener_watch = 0;
		return FALSE;
	}

	re = dhcp_recv_l3_packet(&packet, dhcp_server->listener_sockfd);
	if (re < 0)
		return TRUE;

	type = check_packet_type(&packet);
	if (type == 0)
		return TRUE;

	server_id_option = dhcp_get_option(&packet, DHCP_SERVER_ID);
	if (server_id_option) {
		uint32_t server_nid = dhcp_get_unaligned(
					(uint32_t *) server_id_option);

		if (server_nid != dhcp_server->server_nip)
			return TRUE;
	}

	request_ip_option = dhcp_get_option(&packet, DHCP_REQUESTED_IP);
	if (request_ip_option)
		requested_nip = dhcp_get_unaligned(
					(uint32_t *) request_ip_option);

	lease = find_lease_by_mac(dhcp_server, packet.chaddr);

	switch (type) {
		case DHCPDISCOVER:
			debug(dhcp_server, "Received DISCOVER");

			send_offer(dhcp_server, &packet, lease, requested_nip);
		break;
		case DHCPREQUEST:
			debug(dhcp_server, "Received REQUEST NIP %d",
							requested_nip);
			if (requested_nip == 0) {
				requested_nip = packet.ciaddr;
				if (requested_nip == 0)
					break;
			}

			if (lease && requested_nip == lease->lease_nip) {
				debug(dhcp_server, "Sending ACK");
				send_ACK(dhcp_server, &packet,
						lease->lease_nip);
				break;
			}

			if (server_id_option || lease == NULL) {
				debug(dhcp_server, "Sending NAK");
				send_NAK(dhcp_server, &packet);
			}

		break;
		case DHCPDECLINE:
			debug(dhcp_server, "Received DECLINE");

			if (server_id_option == NULL)
				break;

			if (request_ip_option == NULL)
				break;

			if (lease == NULL)
				break;

			if (requested_nip == lease->lease_nip)
				remove_lease(dhcp_server, lease);

		break;
		case DHCPRELEASE:
			debug(dhcp_server, "Received RELEASE");

			if (server_id_option == NULL)
				break;

			if (lease == NULL)
				break;

			if (packet.ciaddr == lease->lease_nip)
				lease_set_expire(dhcp_server, lease,
								time(NULL));
		break;
		case DHCPINFORM:
			debug(dhcp_server, "Received INFORM");
			send_inform(dhcp_server, &packet);
		break;
	}

	return TRUE;
}
Example #5
0
/* ARGSUSED */
static void
rtsock_event(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg)
{
	dhcp_smach_t *dsmp, *dsmnext;
	union {
		struct ifa_msghdr ifam;
		struct if_msghdr ifm;
		char buf[1024];
	} msg;
	uint16_t ifindex;
	int msglen;
	boolean_t isv6;

	if ((msglen = read(fd, &msg, sizeof (msg))) <= 0)
		return;

	/* Note that the routing socket interface index is just 16 bits */
	if (msg.ifm.ifm_type == RTM_IFINFO) {
		ifindex = msg.ifm.ifm_index;
		isv6 = (msg.ifm.ifm_flags & IFF_IPV6) ? B_TRUE : B_FALSE;
	} else if (msg.ifam.ifam_type == RTM_DELADDR ||
	    msg.ifam.ifam_type == RTM_NEWADDR) {
		ifindex = msg.ifam.ifam_index;
		isv6 = is_rtm_v6(&msg.ifam, msglen);
	} else {
		return;
	}

	for (dsmp = lookup_smach_by_uindex(ifindex, NULL, isv6);
	    dsmp != NULL; dsmp = dsmnext) {
		DHCPSTATE oldstate;
		boolean_t lif_finished;
		boolean_t lease_removed;
		dhcp_lease_t *dlp, *dlnext;

		/*
		 * Note that script_start can call dhcp_drop directly, and
		 * that will do release_smach.
		 */
		dsmnext = lookup_smach_by_uindex(ifindex, dsmp, isv6);
		oldstate = dsmp->dsm_state;

		/*
		 * Ignore state machines that are currently processing drop or
		 * release; there is nothing more we can do for them.
		 */
		if (dsmp->dsm_droprelease)
			continue;

		/*
		 * Look for link up/down notifications.  These occur on a
		 * physical interface basis.
		 */
		if (msg.ifm.ifm_type == RTM_IFINFO) {
			process_link_up_down(dsmp->dsm_lif->lif_pif, &msg.ifm);
			continue;
		}

		/*
		 * Since we cannot trust the flags reported by the routing
		 * socket (they're just 32 bits -- and thus never include
		 * IFF_DUPLICATE), and we can't trust the ifindex (it's only 16
		 * bits and also doesn't reflect the alias in use), we get
		 * flags on all matching interfaces, and go by that.
		 */
		lif_finished = B_FALSE;
		lease_removed = B_FALSE;
		for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlnext) {
			dhcp_lif_t *lif, *lifnext;
			uint_t nlifs = dlp->dl_nlifs;

			dlnext = dlp->dl_next;
			for (lif = dlp->dl_lifs; lif != NULL && nlifs > 0;
			    lif = lifnext, nlifs--) {
				lifnext = lif->lif_next;
				if (check_lif(lif, &msg.ifam, msglen)) {
					dsmp->dsm_lif_wait--;
					lif_finished = B_TRUE;
				}
			}
			if (dlp->dl_nlifs == 0) {
				remove_lease(dlp);
				lease_removed = B_TRUE;
			}
		}

		if ((isv6 && !check_main_lif(dsmp, &msg.ifam, msglen)) ||
		    (!isv6 && !verify_lif(dsmp->dsm_lif))) {
			finished_smach(dsmp, DHCP_IPC_E_INVIF);
			continue;
		}

		/*
		 * Ignore this state machine if nothing interesting has
		 * happened.
		 */
		if (!lif_finished && dsmp->dsm_lif_down == 0 &&
		    (dsmp->dsm_leases != NULL || !lease_removed))
			continue;

		/*
		 * If we're still waiting for DAD to complete on some of the
		 * configured LIFs, then don't send a response.
		 */
		if (dsmp->dsm_lif_wait != 0) {
			dhcpmsg(MSG_VERBOSE, "rtsock_event: %s still has %d "
			    "LIFs waiting on DAD", dsmp->dsm_name,
			    dsmp->dsm_lif_wait);
			continue;
		}

		/*
		 * If we have some failed LIFs, then handle them now.  We'll
		 * remove them from the list.  Any leases that become empty are
		 * also removed as part of the decline-generation process.
		 */
		if (dsmp->dsm_lif_down != 0)
			send_declines(dsmp);

		if (dsmp->dsm_leases == NULL) {
			dsmp->dsm_bad_offers++;
			/*
			 * For DHCPv6, we'll process the restart once we're
			 * done sending Decline messages, because these are
			 * supposed to be acknowledged.  With DHCPv4, there's
			 * no acknowledgment for a DECLINE, so after sending
			 * it, we just restart right away.
			 */
			if (!dsmp->dsm_isv6) {
				dhcpmsg(MSG_VERBOSE, "rtsock_event: %s has no "
				    "LIFs left", dsmp->dsm_name);
				dhcp_restart(dsmp);
			}
		} else {
			/*
			 * If we're now up on at least some of the leases and
			 * we were waiting for that, then kick off the rest of
			 * configuration.  Lease validation and DAD are done.
			 */
			dhcpmsg(MSG_VERBOSE, "rtsock_event: all LIFs verified "
			    "on %s in %s state", dsmp->dsm_name,
			    dhcp_state_to_string(oldstate));
			if (oldstate == PRE_BOUND ||
			    oldstate == ADOPTING)
				dhcp_bound_complete(dsmp);
			if (oldstate == ADOPTING)
				dhcp_adopt_complete(dsmp);
		}
	}
}