Ejemplo n.º 1
0
/*
 * Internal dhcp_network record update routine, used to factor out the
 * common code between add_dn(), delete_dn(), and modify_dn().  If
 * `origp' is NULL, then act like add_dn(); if `newp' is NULL, then
 * act like delete_dn(); otherwise act like modify_dn().
 */
static int
update_dn(const dn_handle_t *dhp, const dn_rec_t *origp, dn_rec_t *newp)
{
	char		dnpath[MAXPATHLEN], newpath[MAXPATHLEN];
	int		retval = DSVC_SUCCESS;
	off_t		recoff, recnext;
	dn_rec_list_t	*reclist;
	int		fd, newfd;
	uint_t		found;
	int		query;
	struct stat	st;


	if ((dhp->dh_oflags & DSVC_WRITE) == 0)
		return (DSVC_ACCESS);

	/*
	 * Open the container to update and a new container file which we
	 * will store the updated version of the container in.  When the
	 * update is done, rename the new file to be the real container.
	 */
	net2path(dnpath, MAXPATHLEN, dhp->dh_location, dhp->dh_net, "");
	fd = open(dnpath, O_RDONLY);
	if (fd == -1)
		return (syserr_to_dsvcerr(errno));

	net2path(newpath, MAXPATHLEN, dhp->dh_location, dhp->dh_net, ".new");
	newfd = open(newpath, O_CREAT|O_TRUNC|O_WRONLY, 0644);
	if (newfd == -1) {
		(void) close(fd);
		return (syserr_to_dsvcerr(errno));
	}

	DSVC_QINIT(query);
	DSVC_QEQ(query, DN_QCIP);

	/*
	 * If we're changing the key for this record, make sure the key
	 * we're changing to doesn't already exist.
	 */
	if (origp != NULL && newp != NULL) {
		if (origp->dn_cip.s_addr != newp->dn_cip.s_addr) {
			retval = find_dn(fd, 0, query, 1, newp, NULL, &found);
			if (retval != DSVC_SUCCESS)
				goto out;
			if (found != 0) {
				retval = DSVC_EXISTS;
				goto out;
			}
		}
	}

	/*
	 * If we're adding a new record, make sure the record doesn't
	 * already exist.
	 */
	if (newp != NULL && origp == NULL) {
		retval = find_dn(fd, 0, query, 1, newp, NULL, &found);
		if (retval != DSVC_SUCCESS)
			goto out;
		if (found != 0) {
			retval = DSVC_EXISTS;
			goto out;
		}
	}

	/*
	 * If we're deleting or modifying record, make sure the record
	 * still exists and that our copy isn't stale.  Note that we don't
	 * check signatures if we're deleting the record and origp->dn_sig
	 * is zero, so that records that weren't looked up can be deleted.
	 */
	if (origp != NULL) {
		retval = find_dn(fd, FIND_POSITION, query, 1, origp, &reclist,
		    &found);
		if (retval != DSVC_SUCCESS)
			goto out;
		if (found == 0) {
			retval = DSVC_NOENT;
			goto out;
		}

		if (reclist->dnl_rec->dn_sig != origp->dn_sig) {
			if (newp != NULL || origp->dn_sig != 0) {
				free_dnrec_list(reclist);
				retval = DSVC_COLLISION;
				goto out;
			}
		}

		/*
		 * Note the offset of the record we're modifying or deleting
		 * for use down below.
		 */
		recoff  = ((dn_recpos_t *)reclist->dnl_rec)->dnp_off;
		recnext = recoff + ((dn_recpos_t *)reclist->dnl_rec)->dnp_size;

		free_dnrec_list(reclist);
	} else {
		/*
		 * No record to modify or delete, so set `recoff' and
		 * `recnext' appropriately.
		 */
		recoff = 0;
		recnext = 0;
	}

	/*
	 * Make a new copy of the container.  If we're deleting or
	 * modifying a record, don't copy that record to the new container.
	 */
	if (fstat(fd, &st) == -1) {
		retval = DSVC_INTERNAL;
		goto out;
	}

	retval = copy_range(fd, 0, newfd, 0, recoff);
	if (retval != DSVC_SUCCESS)
		goto out;

	retval = copy_range(fd, recnext, newfd, recoff, st.st_size - recnext);
	if (retval != DSVC_SUCCESS)
		goto out;

	/*
	 * If there's a new/modified record, append it to the new container.
	 */
	if (newp != NULL) {
		if (origp == NULL)
			newp->dn_sig = gensig();
		else
			newp->dn_sig = origp->dn_sig + 1;

		retval = write_rec(newfd, newp, recoff + st.st_size - recnext);
		if (retval != DSVC_SUCCESS)
			goto out;
	}

	/*
	 * Note: we close these descriptors before the rename(2) (rather
	 * than just having the `out:' label clean them up) to save NFS
	 * some work (otherwise, NFS has to save `dnpath' to an alternate
	 * name since its vnode would still be active).
	 */
	(void) close(fd);
	(void) close(newfd);

	if (rename(newpath, dnpath) == -1)
		retval = syserr_to_dsvcerr(errno);

	return (retval);
out:
	(void) close(fd);
	(void) close(newfd);
	(void) unlink(newpath);
	return (retval);
}
Ejemplo n.º 2
0
/*
 * Internal dhcptab record update routine, used to factor out the
 * common code between add_dt(), delete_dt(), and modify_dt().  If
 * `origp' is NULL, then act like add_dt(); if `newp' is NULL, then
 * act like delete_dt(); otherwise act like modify_dt().
 */
static int
update_dt(const dt_handle_t *dhp, const dt_rec_t *origp, dt_rec_t *newp)
{
	char		dtpath[MAXPATHLEN], newpath[MAXPATHLEN];
	int		retval = DSVC_SUCCESS;
	off_t		recoff, recnext;
	dt_rec_list_t	*reclist;
	FILE		*fp;
	int		newfd;
	uint_t		found;
	int		query;
	struct stat	st;

	if ((dhp->dh_oflags & DSVC_WRITE) == 0)
		return (DSVC_ACCESS);

	/*
	 * Open the container to update and a new container file which we
	 * will store the updated version of the container in.  When the
	 * update is done, rename the new file to be the real container.
	 */
	dt2path(dtpath, MAXPATHLEN, dhp->dh_location, "");
	fp = fopen(dtpath, "r");
	if (fp == NULL)
		return (syserr_to_dsvcerr(errno));

	dt2path(newpath, MAXPATHLEN, dhp->dh_location, ".new");
	(void) unlink(newpath);
	newfd = open(newpath, O_CREAT|O_EXCL|O_WRONLY, 0644);
	if (newfd == -1) {
		(void) fclose(fp);
		return (syserr_to_dsvcerr(errno));
	}

	DSVC_QINIT(query);
	DSVC_QEQ(query, DT_QKEY|DT_QTYPE);

	/*
	 * If we're adding a new record or changing a key for an existing
	 * record, bail if the record we want to add already exists.
	 */
	if (newp != NULL) {
		if (origp == NULL || origp->dt_type != newp->dt_type ||
		    strcmp(origp->dt_key, newp->dt_key) != 0) {
			retval = find_dt(fp, 0, query, 1, newp, NULL, &found);
			if (retval != DSVC_SUCCESS)
				goto out;
			if (found != 0) {
				retval = DSVC_EXISTS;
				goto out;
			}
		}
	}

	/*
	 * If we're deleting or modifying record, make sure the record
	 * still exists.  Note that we don't check signatures because this
	 * is a legacy format that has no signatures.
	 */
	if (origp != NULL) {
		retval = find_dt(fp, FIND_POSITION, query, 1, origp, &reclist,
		    &found);
		if (retval != DSVC_SUCCESS)
			goto out;
		if (found == 0) {
			retval = DSVC_NOENT;
			goto out;
		}

		/*
		 * Note the offset of the record we're modifying or deleting
		 * for use down below.
		 */
		recoff  = ((dt_recpos_t *)reclist->dtl_rec)->dtp_off;
		recnext = recoff + ((dt_recpos_t *)reclist->dtl_rec)->dtp_size;

		free_dtrec_list(reclist);
	} else {
		/*
		 * No record to modify or delete, so set `recoff' and
		 * `recnext' appropriately.
		 */
		recoff = 0;
		recnext = 0;
	}

	/*
	 * Make a new copy of the container.  If we're deleting or
	 * modifying a record, don't copy that record to the new container.
	 */
	if (fstat(fileno(fp), &st) == -1) {
		retval = DSVC_INTERNAL;
		goto out;
	}

	retval = copy_range(fileno(fp), 0, newfd, 0, recoff);
	if (retval != DSVC_SUCCESS)
		goto out;

	retval = copy_range(fileno(fp), recnext, newfd, recoff,
	    st.st_size - recnext);
	if (retval != DSVC_SUCCESS)
		goto out;

	/*
	 * If there's a new record, append it to the new container.
	 */
	if (newp != NULL) {
		retval = write_rec(newfd, newp, recoff + st.st_size - recnext);
		if (retval != DSVC_SUCCESS)
			goto out;
	}

	/*
	 * Note: we close these descriptors before the rename(2) (rather
	 * than just having the `out:' label clean them up) to save NFS
	 * some work (otherwise, NFS has to save `dtpath' to an alternate
	 * name since its vnode would still be active).
	 */
	(void) fclose(fp);
	(void) close(newfd);

	if (rename(newpath, dtpath) == -1)
		retval = syserr_to_dsvcerr(errno);

	return (retval);
out:
	(void) fclose(fp);
	(void) close(newfd);
	(void) unlink(newpath);
	return (retval);
}
Ejemplo n.º 3
0
/*
 * 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);
}