コード例 #1
0
ファイル: misc.c プロジェクト: andreiw/polaris
/*
 * get_lrusort: quick sort of eligible lru container entries.
 */
static dn_rec_list_t *
get_lrusort(dsvc_dnet_t *pnd, dn_rec_list_t *lrup, uint_t *lrecords)
{
	size_t nel;
	size_t size = *lrecords * sizeof (dn_rec_list_t *);
	dn_rec_list_t *from, **to, *next, *freerec = NULL;
	dn_rec_list_t *lrupage;
	dn_rec_t *rp;
	time_t reuse_time = time(NULL) - min_lru;
	uint_t records = 0;
#ifndef	NDEBUG
	int cnt = 0;
#endif	/* !NDEBUG */

	(void) mutex_lock(&pnd->lrupage_mtx);
	if (pnd->lrupage == NULL || pnd->lrusize < size) {
		if (pnd->lrupage != NULL)
			free(pnd->lrupage);
		pnd->lrupage = (dn_rec_list_t **)smalloc(size);
		pnd->lrusize = size;
	}
	if ((to = pnd->lrupage) == NULL) {
		pnd->lrusize = 0;
		(void) mutex_unlock(&pnd->lrupage_mtx);
		return (lrup);
	}

	/*
	 * Build a list of entries, discarding those which are in use.
	 */
	*to = NULL;
	for (from = lrup; from != NULL; from = next) {
		next = from->dnl_next;
		rp = from->dnl_rec;
		if (rp->dn_lease > reuse_time ||
		    (rp->dn_flags & DN_FAUTOMATIC) ||
		    rp->dn_lease == DHCP_PERM) {
			from->dnl_next = freerec;
			freerec = from;
		} else {
			records++;
			*(to++) = from;
		}
		assert(++cnt <= *lrecords);
	}

	/*
	 * Sort any usable elements, and relink.
	 */
	nel = (int)(to - pnd->lrupage);
	if (nel > 0) {
		if (nel > 1)
			qsort(pnd->lrupage, nel, sizeof (dn_rec_list_t *),
			    cmp_lrusort);
		for (to = pnd->lrupage; nel > 0; to++, nel--)
			(*to)->dnl_next = *(to + 1);
		to--;
		(*to)->dnl_next = NULL;
	}

	/*
	 * Free any unusable elements, return any usable elements.
	 */
	if (freerec)
		dhcp_free_dd_list(pnd->dh, freerec);
	*lrecords = records;

	lrupage = *(pnd->lrupage);
	(void) mutex_unlock(&pnd->lrupage_mtx);
	return (lrupage);
}
コード例 #2
0
ファイル: misc.c プロジェクト: andreiw/polaris
/*
 * dhcp_lookup_dd_classify: perform lookup_dd(), or use existing records
 * if supplied, and classify the results based on the type of search criteria
 * being employed. Centralized policy for DN_FMANUAL and DN_FUNUSABLE flag
 * processing are implemented here. Classification is specialized
 * based on these specific search criteria:
 *
 *	S_CID		A CID match is requested. Perform DN_FMANUAL and
 *			DN_FUNUSABLE processing.
 *	S_FREE		A search for free records. Only examine first
 *			matching record.
 *	S_LRU		A search for lru records. Perform sort if needed,
 *			and only examine first matching record.
 *
 * A matching record is detached and returned if found (ok ||
 * manual + unusable). Other successful matches are returned in recordsp as
 * a cache.
 */
void *
dhcp_lookup_dd_classify(dsvc_dnet_t *pnd, boolean_t partial, uint_t query,
    int count, const dn_rec_t *targetp, void **recordsp, int searchtype)
{
	int		err;
	uint_t		rec_cnt = 0, manual = 0;
	dn_rec_t	*dnp;
	dn_rec_list_t	*nlp = NULL, *dnlp = NULL;
	dn_rec_list_t	*unulp = NULL;		/* list of unusables, !manual */
	dn_rec_list_t	*unu_m_lp = NULL;	/* list of unusable + manual */
	dn_rec_list_t	*m_lp = NULL;		/* list of manual records */
	dn_rec_list_t	*cachep = NULL;		/* match cache */
	struct in_addr	swapaddr;
	char		ntoab[INET_ADDRSTRLEN];

	/*
	 * Lookup records matching the specified criteria, or use
	 * records from a previous lookup supplied for classification.
	 */
	if (*recordsp == NULL) {

		TNF_PROBE_1_DEBUG(classify, "classify classify",
		    "classify_query%debug 'in func classify'",
		    tnf_long, query, query);

		err = dhcp_lookup_dd(pnd->dh, partial, query, count, targetp,
		    (void **)recordsp, &rec_cnt);

		TNF_PROBE_1_DEBUG(classify_cid_end, "classify classify_end",
		    "classify_end%debug 'in func classify'",
		    tnf_long, rec_cnt, rec_cnt);

		/*
		 * If any error occurs, mark the dsvc_dnet_t table
		 * for immediate close and reopen. Let the protocol
		 * perform recover, rather than attempting time-consuming
		 * in-place error recovery.
		 */
		if (err != DSVC_SUCCESS) {
			(void) mutex_lock(&pnd->pnd_mtx);
			pnd->flags |= DHCP_PND_ERROR;
			hash_Dtime(pnd->hand, 0);
			(void) mutex_unlock(&pnd->pnd_mtx);
#ifdef	DEBUG
			dhcpmsg(LOG_DEBUG, "classify failure %s\n",
				dhcpsvc_errmsg(err));
#endif	/* DEBUG */
			*recordsp = NULL;
			return (NULL);
		}

		/*
		 * For LRU classification, sort returned records based
		 * on dn_lease field. Discards records with valid lease
		 * times; adjusts rec_cnt accordingly.
		 */
		if (searchtype & S_LRU)
			*recordsp = get_lrusort(pnd, *recordsp, &rec_cnt);

	}

	/*
	 * Record classification: scan through all records, performing
	 * DN_FUNUSABLE and DN_FMANUAL processing. Note that most of the
	 * work has been performed by the datastore query. Remove the matching
	 * entry from the singlely-linked record list, for return. Free any
	 * non-matching entries prior to the match. Pass back any additional
	 * entries after the match in the recordsp pointer for possible re-use
	 * by the caching code.
	 */

	for (nlp = detach_dnrec_from_list(NULL, *recordsp,
	    (dn_rec_list_t **)recordsp); nlp != NULL;
	    nlp = detach_dnrec_from_list(NULL, *recordsp,
	    (dn_rec_list_t **)recordsp)) {
		/*
		 * If we find that there is a DN_FMANUAL entry that is
		 * DN_FUNUSABLE, we fail the request, when performing a
		 * CID search, even though there may be other CID matches. In
		 * the CID case, those other CID matches are errors, because
		 * there should be one and only one record for a client if that
		 * record is marked as being DN_FMANUALly assigned. We tell
		 * the user how many of those CID matches there are. If there
		 * are no DN_FMANUAL records, the first matching record which
		 * is USABLE wins.
		 */
		dnp = nlp->dnl_rec;
		if (dnp->dn_flags & DN_FUNUSABLE) {
			if ((searchtype & (S_CID|S_FREE|S_LRU)) == S_CID) {
				char	cidbuf[DHCP_MAX_OPT_SIZE];
				uint_t	blen = sizeof (cidbuf);

				(void) octet_to_hexascii(targetp->dn_cid,
				    targetp->dn_cid_len,
				    cidbuf, &blen);

				swapaddr.s_addr = htonl(dnp->dn_cip.s_addr);

				dhcpmsg(LOG_NOTICE, "(%1$s,%2$s) "
				    "currently marked as unusable.\n", cidbuf,
				    inet_ntop(AF_INET, &swapaddr, ntoab,
				    sizeof (ntoab)));
			}

			/* build list of unusable records */
			if (dnp->dn_flags & DN_FMANUAL) {
				attach_dnrec_to_list(nlp, &unu_m_lp);
				manual++;
			} else
				attach_dnrec_to_list(nlp, &unulp);
		} else {
			if (dnp->dn_flags & DN_FMANUAL) {
				attach_dnrec_to_list(nlp, &m_lp);
				manual++;
			} else
				attach_dnrec_to_list(nlp, &cachep);
			/*
			 * These searches do not require examining all
			 * matches.
			 */
			if (searchtype & (S_FREE|S_LRU))
				break;
		}
	}

	/*
	 * Warnings are printed for CID searches which end with
	 * DN_FUNUSABLE|DN_FMANUAL match(es).
	 */
	if (m_lp != NULL || unu_m_lp != NULL) {
		if (manual > 1) {
			char	cidbuf[DHCP_MAX_OPT_SIZE];
			uint_t	blen = sizeof (cidbuf);

			(void) octet_to_hexascii(targetp->dn_cid,
				targetp->dn_cid_len,
				cidbuf, &blen);
			dhcpmsg(LOG_WARNING,
			    "Manual allocation (%1$s) has %2$d other MANUAL"
			    " records. It should have 0.\n", cidbuf,
			    manual - 1);
		}
		if (unu_m_lp != NULL) {
			dnlp = detach_dnrec_from_list(NULL, unu_m_lp,
			    &unu_m_lp);
		} else
			dnlp = detach_dnrec_from_list(NULL, m_lp, &m_lp);
	}

	/* Free any unusable entries */
	if (unulp != NULL)
		dhcp_free_dd_list(pnd->dh, unulp);

	/* any other... */
	if (dnlp == NULL)
		dnlp = detach_dnrec_from_list(NULL, cachep, &cachep);

	/*
	 * Return any unused elements for possible caching use. These are
	 * the  additional manual + unusable (as punishment for having
	 * multiple items), manual, and and any others.
	 */
	if (cachep != NULL)
		attach_dnrec_to_list(cachep, (dn_rec_list_t **)recordsp);
	if (m_lp != NULL)
		attach_dnrec_to_list(m_lp, (dn_rec_list_t **)recordsp);
	if (unu_m_lp != NULL)
		attach_dnrec_to_list(unu_m_lp, (dn_rec_list_t **)recordsp);

	/*
	 * Return one of the matching record(s).
	 */
	return (dnlp);
}
コード例 #3
0
ファイル: bootp.c プロジェクト: AlainODea/illumos-gate
/*
 * We are guaranteed that the packet received is a BOOTP request packet,
 * e.g., *NOT* a DHCP packet.
 */
void
bootp(dsvc_clnt_t *pcd, PKT_LIST *plp)
{
	boolean_t	result, existing_offer = B_FALSE;
	int		err, write_error = DSVC_SUCCESS, flags = 0;
	int		pkt_len;
	uint_t		crecords = 0, irecords = 0, srecords = 0, clen;
	uint32_t	query;
	PKT		*rep_pktp = NULL;
	IF		*ifp = pcd->ifp;
	dsvc_dnet_t	*pnd = pcd->pnd;
	uchar_t		*optp;
	dn_rec_t	dn, ndn, *dnp;
	dn_rec_list_t	*dncp = NULL, *dnip = NULL, *dnlp = NULL;
	struct in_addr	ciaddr;
	struct in_addr	no_ip;	/* network order IP */
	ENCODE		*ecp, *hecp;
	MACRO		*mp, *nmp, *cmp;
	time_t		now = time(NULL);
	DHCP_MSG_CATEGORIES	log;
	struct		hostent	h, *hp;
	char		ntoab[INET_ADDRSTRLEN], cipbuf[INET_ADDRSTRLEN];
	char		cidbuf[DHCP_MAX_OPT_SIZE];
	char		hbuf[NSS_BUFLEN_HOSTS];

	ciaddr.s_addr = htonl(INADDR_ANY);

#ifdef	DEBUG
	dhcpmsg(LOG_DEBUG, "BOOTP request received on %s\n", ifp->nm);
#endif	/* DEBUG */

	if (pcd->off_ip.s_addr != htonl(INADDR_ANY) &&
	    PCD_OFFER_TIMEOUT(pcd, now))
		purge_offer(pcd, B_TRUE, B_TRUE);

	if (pcd->off_ip.s_addr != htonl(INADDR_ANY)) {
		existing_offer = B_TRUE;
		dnlp = pcd->dnlp;
		assert(dnlp != NULL);
		dnp = dnlp->dnl_rec;
		no_ip.s_addr = htonl(dnp->dn_cip.s_addr);
		crecords = 1;
	} else {
		/*
		 * Try to find a CID entry for the client. We don't care about
		 * lease info here, since a BOOTP client always has a permanent
		 * lease. We also don't care about the entry owner either,
		 * unless we end up allocating a new entry for the client.
		 */
		DSVC_QINIT(query);

		DSVC_QEQ(query, DN_QCID);
		(void) memcpy(dn.dn_cid, pcd->cid, pcd->cid_len);
		dn.dn_cid_len = pcd->cid_len;

		DSVC_QEQ(query, DN_QFBOOTP_ONLY);
		dn.dn_flags = DN_FBOOTP_ONLY;

		/*
		 * If a client address (ciaddr) is given, we simply trust that
		 * the client knows what it's doing, and we use that IP address
		 * to locate the client's record. If we can't find the client's
		 * record, then we keep silent. If the client id of the record
		 * doesn't match this client, then either the client or our
		 * database is inconsistent, and we'll ignore it (notice
		 * message generated).
		 */
		ciaddr.s_addr = plp->pkt->ciaddr.s_addr;
		if (ciaddr.s_addr != htonl(INADDR_ANY)) {
			DSVC_QEQ(query, DN_QCIP);
			dn.dn_cip.s_addr = ntohl(ciaddr.s_addr);
		}

		dnlp = dhcp_lookup_dd_classify(pcd->pnd, B_FALSE, query,
		    -1, &dn, (void **)&dncp, S_CID);
		if (dnlp != NULL) {
			crecords = 1;
			dnp = dnlp->dnl_rec;
			if (dnp->dn_flags & DN_FUNUSABLE)
				goto leave_bootp;
			no_ip.s_addr = htonl(dnp->dn_cip.s_addr);
		}
	}

	(void) inet_ntop(AF_INET, &no_ip, cipbuf, sizeof (cipbuf));

	if (crecords == 0 && !be_automatic) {
		if (verbose) {
			dhcpmsg(LOG_INFO, "BOOTP client: %1$s is looking for "
			    "a configuration on net %2$s\n", pcd->cidbuf,
			    pnd->network);
		}
		goto leave_bootp;
	}

	/*
	 * If the client thinks it knows who it is (ciaddr), and this doesn't
	 * match our registered IP address, then display an error message and
	 * give up.
	 */
	if (ciaddr.s_addr != htonl(INADDR_ANY) && crecords == 0) {
		/*
		 * If the client specified an IP address, then let's check
		 * whether it is available, since we have no CID mapping
		 * registered for this client. If it is available and
		 * unassigned but owned by a different server, we ignore the
		 * client.
		 */
		DSVC_QINIT(query);

		DSVC_QEQ(query, DN_QCIP);
		dn.dn_cip.s_addr = ntohl(ciaddr.s_addr);
		(void) inet_ntop(AF_INET, &ciaddr, cipbuf, sizeof (cipbuf));

		DSVC_QEQ(query, DN_QFBOOTP_ONLY);
		dn.dn_flags = DN_FBOOTP_ONLY;

		dnip = NULL;
		dnlp = dhcp_lookup_dd_classify(pcd->pnd, B_FALSE, query,
		    -1, &dn, (void **)&dncp, S_CID);
		if (dnlp == NULL) {
			/*
			 * We have no record of this client's IP address, thus
			 * we really can't respond to this client, because it
			 * doesn't have a configuration.
			 */
			if (verbose) {
				dhcpmsg(LOG_INFO, "No configuration for BOOTP "
				    "client: %1$s. IP address: %2$s not "
				    "administered by this server.\n",
				    pcd->cidbuf, inet_ntop(AF_INET, &ciaddr,
				    ntoab, sizeof (ntoab)));
			}
			goto leave_bootp;
		} else
			irecords = 1;

		dnp = dnlp->dnl_rec;
		if (dnp->dn_flags & DN_FUNUSABLE)
			goto leave_bootp;

		if (dn.dn_cid_len != 0) {
			if (dn.dn_cid_len != pcd->cid_len || memcmp(dn.dn_cid,
			    pcd->cid, pcd->cid_len) != 0) {
				if (verbose) {
					clen = sizeof (cidbuf);
					(void) octet_to_hexascii(dn.dn_cid,
					    dn.dn_cid_len, cidbuf, &clen);
					dhcpmsg(LOG_INFO, "BOOTP client: %1$s "
					    "thinks it owns %2$s, but that "
					    "address belongs to %3$s. Ignoring "
					    "client.\n", pcd->cidbuf, cipbuf,
					    cidbuf);
				}
				goto leave_bootp;
			}
		} else {
			if (match_ownerip(htonl(dn.dn_sip.s_addr)) == NULL) {
				if (verbose) {
					no_ip.s_addr =
					    htonl(dnp->dn_sip.s_addr);
					dhcpmsg(LOG_INFO, "BOOTP client: %1$s "
					    "believes it owns %2$s. That "
					    "address is free, but is owned by "
					    "DHCP server %3$s. Ignoring "
					    "client.\n", pcd->cidbuf, cipbuf,
					    inet_ntop(AF_INET, &no_ip, ntoab,
					    sizeof (ntoab)));
				}
				goto leave_bootp;
			}
		}
		no_ip.s_addr = htonl(dnp->dn_cip.s_addr);
		(void) inet_ntop(AF_INET, &no_ip, cipbuf, sizeof (cipbuf));
	}

	if (crecords == 0) {
		/*
		 * The dhcp-network table did not have any matching entries.
		 * Try to allocate a new one if possible.
		 */
		if (irecords == 0 && select_offer(pnd, plp, pcd, &dnlp)) {
			dnp = dnlp->dnl_rec;
			no_ip.s_addr = htonl(dnp->dn_cip.s_addr);
			(void) inet_ntop(AF_INET, &no_ip, cipbuf,
			    sizeof (cipbuf));
			srecords = 1;
		}
	}

	if (crecords == 0 && irecords == 0 && srecords == 0) {
		dhcpmsg(LOG_NOTICE,
		    "(%1$s) No more BOOTP IP addresses for %2$s network.\n",
		    pcd->cidbuf, pnd->network);
		goto leave_bootp;
	}

	/* Check the address. But only if client doesn't know its address. */
	ndn = *dnp;	/* struct copy */
	no_ip.s_addr = htonl(ndn.dn_cip.s_addr);
	(void) inet_ntop(AF_INET, &no_ip, cipbuf, sizeof (cipbuf));
	if (ciaddr.s_addr == htonl(INADDR_ANY)) {
		if ((ifp->flags & IFF_NOARP) == 0)
			(void) set_arp(ifp, &no_ip, NULL, 0, DHCP_ARP_DEL);
		if (!noping) {
			/*
			 * If icmp echo check fails,
			 * let the plp fall by the wayside.
			 */
			errno = icmp_echo_check(&no_ip, &result);
			if (errno != 0) {
				dhcpmsg(LOG_ERR, "ICMP ECHO check cannot be "
				    "performed for: %s, ignoring\n", cipbuf);
				goto leave_bootp;
			}
			if (result) {
				dhcpmsg(LOG_ERR, "ICMP ECHO reply to BOOTP "
				    "OFFER candidate: %s, disabling.\n",
				    cipbuf);

				ndn.dn_flags |= DN_FUNUSABLE;

				if ((err = dhcp_modify_dd_entry(pnd->dh,
				    dnp, &ndn)) == DSVC_SUCCESS) {
					/* Keep the cached entry current. */
					*dnp = ndn;    /* struct copy */
				}

				logtrans(P_BOOTP, L_ICMP_ECHO, 0, no_ip,
				    server_ip, plp);

				goto leave_bootp;
			}
		}
	}

	/*
	 * It is possible that the client could specify a REQUEST list,
	 * but then it would be a DHCP client, wouldn't it? Only copy the
	 * std option list, since that potentially could be changed by
	 * load_options().
	 */
	ecp = NULL;
	if (!no_dhcptab) {
		open_macros();
		if ((nmp = get_macro(pnd->network)) != NULL)
			ecp = dup_encode_list(nmp->head);
		if ((mp = get_macro(dnp->dn_macro)) != NULL)
			ecp = combine_encodes(ecp, mp->head, ENC_DONT_COPY);
		if ((cmp = get_macro(pcd->cidbuf)) != NULL)
			ecp = combine_encodes(ecp, cmp->head, ENC_DONT_COPY);

		/* If dhcptab configured to return hostname, do so. */
		if (find_encode(ecp, DSYM_INTERNAL, CD_BOOL_HOSTNAME) !=
		    NULL) {
			hp = gethostbyaddr_r((char *)&ndn.dn_cip,
			    sizeof (struct in_addr), AF_INET, &h, hbuf,
			    sizeof (hbuf), &err);
			if (hp != NULL) {
				hecp = make_encode(DSYM_STANDARD,
				    CD_HOSTNAME, strlen(hp->h_name),
				    hp->h_name, ENC_COPY);
				replace_encode(&ecp, hecp, ENC_DONT_COPY);
			}
		}
	}

	/* Produce a BOOTP reply. */
	rep_pktp = gen_bootp_pkt(sizeof (PKT), plp->pkt);

	rep_pktp->op = BOOTREPLY;
	optp = rep_pktp->options;

	/*
	 * Set the client's "your" IP address if client doesn't know it,
	 * otherwise echo the client's ciaddr back to him.
	 */
	if (ciaddr.s_addr == htonl(INADDR_ANY))
		rep_pktp->yiaddr.s_addr = htonl(ndn.dn_cip.s_addr);
	else
		rep_pktp->ciaddr.s_addr = ciaddr.s_addr;

	/*
	 * Omit lease time options implicitly, e.g.
	 * ~(DHCP_DHCP_CLNT | DHCP_SEND_LEASE)
	 */

	if (!plp->rfc1048)
		flags |= DHCP_NON_RFC1048;

	/* Now load in configured options. */
	pkt_len = load_options(flags, plp, rep_pktp, sizeof (PKT), optp, ecp,
	    NULL);

	free_encode_list(ecp);
	if (!no_dhcptab)
		close_macros();

	if (pkt_len < sizeof (PKT))
		pkt_len = sizeof (PKT);

	/*
	 * Only perform a write if we have selected an entry not yet
	 * assigned to the client (a matching DN_FBOOTP_ONLY entry from
	 * ip address lookup, or an unassigned entry from select_offer()).
	 */
	if (srecords > 0 || irecords > 0) {
		(void) memcpy(&ndn.dn_cid, pcd->cid, pcd->cid_len);
		ndn.dn_cid_len = pcd->cid_len;

		write_error = dhcp_modify_dd_entry(pnd->dh, dnp, &ndn);

		/* Keep state of the cached entry current. */
		*dnp = ndn;	/* struct copy */

		log = L_ASSIGN;
	} else {
		if (verbose) {
			dhcpmsg(LOG_INFO, "Database write unnecessary for "
			    "BOOTP client: %1$s, %2$s\n",
			    pcd->cidbuf, cipbuf);
		}
		log = L_REPLY;
	}

	if (write_error == DSVC_SUCCESS) {
		if (send_reply(ifp, rep_pktp, pkt_len, &no_ip) != 0) {
			dhcpmsg(LOG_ERR,
			    "Reply to BOOTP client %1$s with %2$s failed.\n",
			    pcd->cidbuf, cipbuf);
		} else {
			/* Note that the conversation has completed. */
			pcd->state = ACK;

			(void) update_offer(pcd, &dnlp, 0, &no_ip, B_TRUE);
			existing_offer = B_TRUE;
		}

		logtrans(P_BOOTP, log, ndn.dn_lease, no_ip, server_ip, plp);
	}

leave_bootp:
	if (rep_pktp != NULL)
		free(rep_pktp);
	if (dncp != NULL)
		dhcp_free_dd_list(pnd->dh, dncp);
	if (dnip != NULL)
		dhcp_free_dd_list(pnd->dh, dnip);
	if (dnlp != NULL && !existing_offer)
		dhcp_free_dd_list(pnd->dh, dnlp);
}