3464 lines
95 KiB
C
3464 lines
95 KiB
C
/*
|
|
* BOOTP (bootstrap protocol) server daemon.
|
|
*
|
|
* Answers BOOTP request packets from booting client machines.
|
|
* See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol.
|
|
*/
|
|
/*
|
|
#define PERFORMANCE
|
|
# define EDHCP to compile in enhanced server features
|
|
# current enhanced features are ping check, mac address filtering,
|
|
# ldap backend, dhcp server failsafe, dynamic dns updates
|
|
# note that some of these are not available yet
|
|
# EDHCP is defined in the Makefile to build teh enhanced product
|
|
*/
|
|
#include <sys/param.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/file.h>
|
|
#include <fcntl.h>
|
|
|
|
#include <net/if.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include "dhcp.h"
|
|
#include "dhcpdefs.h"
|
|
#include <arpa/inet.h>
|
|
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <strings.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
#include <netdb.h>
|
|
#include <setjmp.h>
|
|
#include <syslog.h>
|
|
|
|
#include <net/raw.h>
|
|
#include <netinet/if_ether.h>
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/ip.h>
|
|
#include <netinet/ip_icmp.h>
|
|
#include <netinet/udp.h>
|
|
|
|
#include <sys/time.h>
|
|
#include <rpc/rpc.h>
|
|
#include <rpcsvc/ypclnt.h>
|
|
|
|
#include <ndbm.h>
|
|
|
|
#include "bootptab.h"
|
|
|
|
#ifndef ISM_VERSION
|
|
#define ISM_VERSION "\"1.0\""
|
|
#endif
|
|
static char *_identString = "$MyProductVersion: "ISM_VERSION" $";
|
|
#define DHCP_CONFIG_DIR "/var/dhcp/config"
|
|
|
|
#ifdef EDHCP
|
|
#include "ddns/dhcp_dnsupd.h"
|
|
#include "dhcptab.h"
|
|
extern unsigned int getLicense(char *prog, char **msg);
|
|
#ifdef EDHCP_LDAP
|
|
#include <lber.h>
|
|
#include <ldap.h>
|
|
#include "ldap/ldap_api.h"
|
|
extern void cleanup_restabm(void);
|
|
extern void cleanup_caches(void);
|
|
extern void cleanup_DHCPServiceLdap(void);
|
|
extern void free_ld_root(void);
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef MAXDNAME
|
|
#define MAXDNAME 1025 /* required for client fqdn option */
|
|
#endif
|
|
|
|
DBM *db;
|
|
extern int dblock;
|
|
char reg_netmask[30], *reg_net;
|
|
char reg_hostnet[30];
|
|
|
|
int debug = 0;
|
|
int sleepmode = 0;
|
|
int ProclaimServer = 0;
|
|
char * alt_sysname = (char *)0;
|
|
char * alt_config_dir = (char *)0;
|
|
char * useConfigDir = (char *)0;
|
|
int using_nis = 0;
|
|
int standalone_mode = 0;
|
|
int dh0_timeouts_cnt = 0;
|
|
int alt_naming_flag = 0;
|
|
int always_return_netmask = 0;
|
|
|
|
/*
|
|
* data to support the icmp ping check
|
|
*/
|
|
int ping_blocking_check = 0; /* off for now */
|
|
int ping_nonblocking_check = 0; /* on for now */
|
|
int ping_dns = 0; /* check dns to see if ipaddr assigned */
|
|
int ping_sd = -1; /* ping socket */
|
|
int ping_number_pending = 0; /* pings sent - not recd/timed out */
|
|
int ping_timeout = DHCPPING_TIMEOUT;
|
|
int ping_maxout = DHCPPING_MAXOUT;
|
|
#ifdef DEBUG
|
|
#define RETRY_STOLEN_TIMEOUT 300
|
|
#else
|
|
#define RETRY_STOLEN_TIMEOUT 12*60*60
|
|
#endif
|
|
u_long retry_stolen_timeout = RETRY_STOLEN_TIMEOUT;
|
|
static int ident = 0;
|
|
int ntransmitted = 0;
|
|
extern int init_ping(void);
|
|
extern int send_ping(int sd, u_long ipaddr, u_short seqnum, u_short ident);
|
|
extern struct icmp *recv_icmp_msg( int sd, u_long *ipaddr,
|
|
u_char *inbuf, int inbuflen);
|
|
extern int recv_echo_reply(int *seqnum, int *ident, struct icmp *inbuf);
|
|
|
|
/* changes to call a script whenever there is a state change */
|
|
extern char * script_file_to_run;
|
|
extern int script_state_change_index; /* if > 0 implies changes took place */
|
|
int script_msgtype; /* reply type or client message
|
|
that cause the change */
|
|
extern state_change_t script_state_changes[];
|
|
extern int execute_script(void);
|
|
|
|
int s; /* socket fd */
|
|
int rsockout; /* raw socket fd for broadcast */
|
|
int rsockout_err; /* Return Error code */
|
|
struct sockaddr_raw braw_addr; /* raw socket addr struct */
|
|
u_char buf[1024]; /* receive packet buffer */
|
|
time_t TimeNow; /* time of day */
|
|
struct arpreq arpreq; /* arp request ioctl block */
|
|
struct sockaddr_in sin;
|
|
time_t msg_recv_time; /* time at which a message is received */
|
|
|
|
#ifdef PERFORMANCE
|
|
int perf_flag;
|
|
u_long discover_time = 0;/*performance calculations related stuff*/
|
|
u_long request_time = 0;
|
|
long discover_ctr=0;
|
|
long request_ctr=0;
|
|
struct timeval perf_time_start;
|
|
struct timeval perf_time_end;
|
|
struct timezone perf_tz;
|
|
#define MIL 1000000
|
|
#endif
|
|
|
|
#ifdef EDHCP_LDAP
|
|
/* ldap related declarations */
|
|
char *ldap_conf_file = (char*)0;
|
|
#endif
|
|
|
|
struct dhcp_request_list *dhcp_rlist = 0;
|
|
struct dhcp_timelist *dhcp_tmlist_first = 0;
|
|
struct dhcp_timelist *dhcp_tmlist_last = 0;
|
|
|
|
char *DHCP_MASTER = "proclaim_server";
|
|
char *INCORRECT_MAPPING_MSG = "Invalid Address: incorrect mapping";
|
|
char *WRONG_SUBNET_MSG = "Invalid address: wrong subnet";
|
|
char *WRONG_SERVER_MSG = "Wrong Server requested for renew/rebind";
|
|
|
|
|
|
extern char *alt_hosts_file;
|
|
extern char *alt_ethers_file;
|
|
extern int dont_update_hosts;
|
|
extern int dont_update_ethers;
|
|
extern rfc1533opts *rfc1533ptr;
|
|
extern struct ifconf ifconf;
|
|
extern struct ifreq ifreq[];
|
|
extern struct netaddr nets[];
|
|
extern int ifcount;
|
|
extern char *EtherIpFile;
|
|
extern char *lockEtherIpFile;
|
|
|
|
extern dhcpConfig *cf0_get_config(u_long, int flag);
|
|
extern rfc1533opts *get_rfc1533_config(void);
|
|
extern int cf0_encode_rfc1533_opts(u_char []);
|
|
extern int sr_create_ip_send_packet(char **, int *,
|
|
struct bootp *, int);
|
|
extern int sr_initialize_nets(void);
|
|
extern int sr_initialize_broadcast_sockets(void);
|
|
extern struct netaddr* sr_match_address(u_long);
|
|
extern int sr_initialize_single_broadcast_socket(void);
|
|
extern int sr_get_interface_type(char *);
|
|
extern int cf0_initialize_config(void);
|
|
extern int cf0_reread_config(void);
|
|
#ifdef EDHCP_LDAP
|
|
extern void cf0_free_config_ldap(void);
|
|
#endif
|
|
extern int dh0_set_timeval(struct timeval *, long);
|
|
extern int dh0_free_timelist_entry(struct dhcp_timelist *);
|
|
extern int dh0_remove_from_dhcp_reqlist(char *, int);
|
|
extern int loc0_remove_entry(EtherAddr *, int, char *, int, DBM *);
|
|
extern struct dhcp_request_list *dh0_find_dhcp_rqlist_entry(char *,int);
|
|
extern long dh0_update_and_get_next_timeout(time_t);
|
|
extern int dh0_get_dhcp_msg_type(u_char *);
|
|
extern struct dhcp_request_list * dh0_find_dhcp_rqlist_entry_by_ipaddr(u_long ipaddr);
|
|
extern int ether_hostton(char *, struct ether_addr *);
|
|
extern int ether_ntohost(char *, struct ether_addr *);
|
|
extern int registerinethost(char *, char *, char *,
|
|
struct in_addr *, char *);
|
|
extern int dh0_decode_dhcp_client_packet(u_char *,
|
|
struct getParams *,
|
|
u_long *, u_int *,
|
|
u_long *, char **,
|
|
char **, int *,
|
|
int *, int *, struct dhcp_client_info *);
|
|
extern int loc0_is_ipaddress_for_cid(EtherAddr *, u_long, char *, int, DBM *);
|
|
extern int cf0_get_new_ipaddress(EtherAddr *, u_long, u_long,
|
|
u_long *, dhcpConfig **,char *,
|
|
int, DBM *, char *name);
|
|
extern int chkconf(char *, char *);
|
|
extern int dh0_addto_timelist(char *, int, EtherAddr *, time_t,
|
|
struct dhcp_request_list*);
|
|
extern int dh0_encode_dhcp_server_packet(u_char *, int,
|
|
struct getParams *,
|
|
u_long, char *, char *,
|
|
char *, dhcpConfig *,
|
|
struct dhcp_client_info*);
|
|
extern struct dhcp_request_list*
|
|
dh0_add_to_dhcp_reqlist(char *, int, EtherAddr *, u_long, char *,
|
|
struct getParams *, u_long, u_int,
|
|
dhcpConfig *, ping_status_t, int, int,
|
|
char *, int, struct bootp*, struct bootp*,
|
|
struct dhcp_client_info*);
|
|
extern int sys0_hostnameIsValid(char *);
|
|
extern int loc0_is_hostname_assigned(char *, DBM *);
|
|
extern int dh0_remove_from_timelist(char *, int);
|
|
extern int cf0_is_req_addr_valid(u_long, u_long);
|
|
extern int dh0_upd_dhcp_rq_entry(struct dhcp_request_list *,
|
|
struct getParams *, u_int);
|
|
extern int dh0_create_system_mapping(u_long, EtherAddr *,
|
|
char *, char *);
|
|
extern int loc0_create_update_entry(char *, int, EtherAddr *,
|
|
u_long, char *,
|
|
time_t, u_long, DBM *);
|
|
extern int loc0_update_lease(EtherAddr *,char *, int,
|
|
time_t,u_long, DBM *);
|
|
extern long loc0_get_lease(u_long ipaddr, DBM*);
|
|
extern int loc0_zero_expired_lease(EtherAddr *, char *, int,
|
|
DBM *db);
|
|
extern int dh0_remove_system_mapping(char *, EtherAddr *, int,
|
|
DBM *);
|
|
extern FILE *log_fopen(char *, char *);
|
|
extern DBM *log_dbmopen(char *);
|
|
extern void log_dbmclose(DBM *);
|
|
extern int loc0_is_etherToIP_inconsistent(char *, int,EtherAddr *,
|
|
u_long, char *, DBM *);
|
|
static int check_selected_name_address(char *rhsname, char **dhcp_msg, u_long dhcp_ipaddr, struct bootp *rq, struct dhcp_request_list *req, char *cid_ptr, int cid_length);
|
|
extern char* sys0_name_errmsg(char *, int);
|
|
extern char* sys0_addr_errmsg(u_long, int);
|
|
extern char* loc0_get_hostname_from_cid(char *, int, EtherAddr *, DBM *);
|
|
extern int loc0_is_ipaddress_assigned(u_long ipa, DBM *);
|
|
extern void set_addl_options_to_send(char *pstr);
|
|
extern void mk_str(int cid_flag, char *cid_ptr, int cid_length,
|
|
char *str);
|
|
extern void cleanup_atexit(void);
|
|
extern void free_vendor_options(void);
|
|
extern int cf0_free_config_list(dhcpConfig *delptr);
|
|
extern u_long loc0_get_ipaddr_from_cid(EtherAddr *, char *,
|
|
int, DBM *);
|
|
#ifdef EDHCP
|
|
extern int process_expired_ping_send(void);
|
|
extern int dh0_append_client_fqdn(u_char dhbuf[],
|
|
char *client_fqdn);
|
|
extern int ping_send_DHCPOFFER(struct dhcp_request_list*);
|
|
extern int handle_ping_nonblocking_reply(int);
|
|
|
|
/* globals associated with DHCP-DNS updates */
|
|
#define DHCP_DNS_DEFAULT_TTL 3600
|
|
#define DEFAULT_DDNS_CONF_FILE "/var/dhcp/config/dhcp_ddns.conf"
|
|
int dhcp_dnsupd_on = 0;
|
|
int dhcp_dnsupd_secure = 1;
|
|
char *ddns_conf_file = DEFAULT_DDNS_CONF_FILE;
|
|
int dhcp_dnsupd_beforeACK = 0;
|
|
int dhcp_dnsupd_clientARR = 0;
|
|
int dhcp_dnsupd_always = 0;
|
|
int dhcp_dnsupd_ttl = DHCP_DNS_DEFAULT_TTL; /* 1 hour */
|
|
#endif
|
|
/*
|
|
* Globals below are associated with the bootp database file (bootptab).
|
|
*/
|
|
|
|
char VERS[] = "SGI bootp/dhcp Server V3.1.2";
|
|
char *bootptab = "/etc/bootptab";
|
|
static char *opts_file;
|
|
|
|
FILE *fp;
|
|
char line[256]; /* line buffer for reading bootptab */
|
|
char *linep; /* pointer to 'line' */
|
|
int linenum; /* current line number in bootptab */
|
|
char homedir[64]; /* bootfile homedirectory */
|
|
char defaultboot[64]; /* default file to boot */
|
|
|
|
int nhosts; /* current number of hosts */
|
|
long modtime; /* last modification time of bootptab */
|
|
|
|
int max_addr_alloc = 64;
|
|
int numaddrs;
|
|
u_int *myaddrs; /* save addresses of executing host */
|
|
char myname[MAXHOSTNAMELEN+1]; /* my primary name */
|
|
char *mydomain; /* pointer into myname */
|
|
iaddr_t myhostaddr; /* save (main) internet address of executing host */
|
|
int forwarding; /* flag that controls cross network forwarding */
|
|
u_long mynetmask; /* Netmask of the primary interface - used by DHCP */
|
|
int dhcp_timeout, dhcp_timer_set = 0;
|
|
|
|
struct netaddr *np_recv, *np_send;
|
|
|
|
static char *iaddr_ntoa(iaddr_t addr);
|
|
|
|
void request(struct bootp *);
|
|
void reply(struct bootp *);
|
|
void forward(struct bootp *, iaddr_t *, iaddr_t *);
|
|
void sendreply(struct bootp *, int, int, int, int);
|
|
void setarp(iaddr_t *, u_char *, int );
|
|
void readtab(void);
|
|
void getfield(char *, int );
|
|
void makenamelist(void);
|
|
|
|
/*
|
|
* For sgi, bootp is a single threaded server invoked
|
|
* by the inet master daemon (/usr/etc/inetd). Once
|
|
* a bootp has been started, it will handle all subsequent
|
|
* bootp requests until it has been idle for TIMEOUT
|
|
* seconds, at which point the bootp process terminates
|
|
* and inetd will start listening to the IPPORT_BOOTPS
|
|
* socket again.
|
|
*/
|
|
#define TIMEOUT 300 /* number of idle seconds to wait */
|
|
|
|
extern int addl_options_len;
|
|
|
|
void
|
|
init_dhcp_opts(void)
|
|
{
|
|
using_nis = 0;
|
|
dont_update_hosts = 0;
|
|
dont_update_ethers = 0;
|
|
if (alt_hosts_file) { free(alt_hosts_file); alt_hosts_file = NULL; }
|
|
if (alt_ethers_file) { free(alt_ethers_file); alt_ethers_file = NULL;}
|
|
if (alt_sysname) { free(alt_sysname); alt_sysname = NULL; }
|
|
if (alt_config_dir) { free(alt_config_dir); alt_config_dir = NULL; }
|
|
alt_naming_flag = 0;
|
|
always_return_netmask = 0;
|
|
dhcp_timeout = TIMEOUT; dhcp_timer_set = 0;
|
|
addl_options_len = 0;
|
|
if (script_file_to_run) {
|
|
free(script_file_to_run); script_file_to_run = NULL; }
|
|
#ifdef EDHCP
|
|
ping_blocking_check = 0;
|
|
ping_nonblocking_check = 0;
|
|
ping_timeout = DHCPPING_TIMEOUT;
|
|
ping_maxout = DHCPPING_MAXOUT;
|
|
ping_dns = 0;
|
|
#ifdef EDHCP_LDAP
|
|
if (ldap_conf_file) {
|
|
free(ldap_conf_file); ldap_conf_file = NULL;
|
|
}
|
|
#endif
|
|
dhcp_dnsupd_on = 0;
|
|
dhcp_dnsupd_secure = 1;
|
|
dhcp_dnsupd_beforeACK = 0;
|
|
dhcp_dnsupd_clientARR = 0;
|
|
dhcp_dnsupd_always = 0;
|
|
dhcp_dnsupd_ttl = DHCP_DNS_DEFAULT_TTL;
|
|
#endif
|
|
}
|
|
|
|
static long dhcpopts_modtime = 0; /* last modification time of dhcpmtab */
|
|
|
|
int
|
|
process_dhcp_opts(char *fname)
|
|
{
|
|
FILE *fp;
|
|
char dbuf[256];
|
|
char *p, *str;
|
|
char tbuf[256];
|
|
struct stat st;
|
|
|
|
if(!fname || (*fname == '\0') )
|
|
return 1;
|
|
fp = log_fopen(fname, "r");
|
|
if(!fp)
|
|
return 1;
|
|
fstat(fileno(fp), &st);
|
|
if (st.st_mtime == dhcpopts_modtime && st.st_nlink) {
|
|
fclose(fp);
|
|
return 0;
|
|
}
|
|
if (dhcpopts_modtime != 0)
|
|
init_dhcp_opts(); /* only second time onwards */
|
|
dhcpopts_modtime = st.st_mtime;
|
|
while(fgets(dbuf, 256, fp) != NULL) {
|
|
p = dbuf;
|
|
|
|
for(;;) {
|
|
if(*p == '#')
|
|
break;
|
|
while(*p && isspace(*p)) p++;
|
|
if(*p == '\0')
|
|
break;
|
|
str = p;
|
|
while(*p && !isspace(*p)) p++;
|
|
|
|
if(strncmp(str, "-h", 2) == 0) {
|
|
syslog(LOG_ERR, "The -h option is no longer supported");
|
|
fclose(fp);
|
|
return 2;
|
|
}
|
|
else
|
|
if(strncmp(str, "-y", 2) == 0) {
|
|
/* DHCP will use nis, this system better be the NIS master */
|
|
using_nis++;
|
|
p++;
|
|
}
|
|
else
|
|
if(strncmp(str, "-s", 2) == 0) {
|
|
syslog(LOG_ERR, "The -s option is no longer supported");
|
|
fclose(fp);
|
|
return 2;
|
|
}
|
|
else
|
|
if(strncmp(str, "-W", 2) == 0) {
|
|
dont_update_hosts++;
|
|
}
|
|
else
|
|
if(strncmp(str, "-w", 2) == 0) {
|
|
/* DHCP: Optional location of the hosts map */
|
|
if(*p == '\0') {
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
while(isspace(*p)) p++;
|
|
str = p;
|
|
while(*p && !isspace(*p)) p++;
|
|
*p++ = '\0';
|
|
alt_hosts_file = strdup(str);
|
|
}
|
|
else
|
|
if(strncmp(str, "-E", 2) == 0) {
|
|
dont_update_ethers++;
|
|
}
|
|
else
|
|
if(strncmp(str, "-e", 2) == 0) {
|
|
/* DHCP: Optional location of the ethers map */
|
|
if(*p == '\0') {
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
while(isspace(*p)) p++;
|
|
str = p;
|
|
while(*p && !isspace(*p)) p++;
|
|
*p++ = '\0';
|
|
alt_ethers_file = strdup(str);
|
|
}
|
|
else
|
|
if(strncmp(str, "-u", 2) == 0) {
|
|
/* DHCP: Optional sysname file, default is /unix */
|
|
if(*p == '\0') {
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
while(isspace(*p)) p++;
|
|
str = p;
|
|
while(*p && !isspace(*p)) p++;
|
|
*p++ = '\0';
|
|
alt_sysname = strdup(str);
|
|
}
|
|
else
|
|
if(strncmp(str, "-c", 2) == 0) {
|
|
/* DHCP: Optional DHCP Server config directory,
|
|
* default is /var/dhcp/config.
|
|
*/
|
|
if(*p == '\0') {
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
while(isspace(*p)) p++;
|
|
str = p;
|
|
while(*p && !isspace(*p)) p++;
|
|
*p++ = '\0';
|
|
alt_config_dir = strdup(str);
|
|
}
|
|
else
|
|
if(strncmp(str, "-x", 2) == 0) {
|
|
alt_naming_flag++;
|
|
}
|
|
else
|
|
if(strncmp(str, "-n", 2) == 0) {
|
|
/* This is a workaround for a bug in the windows
|
|
* dhcp client, where the windows client expects
|
|
* to get a valid netmask back from the server
|
|
* without asking for it.
|
|
*/
|
|
always_return_netmask++;
|
|
}
|
|
else
|
|
if(strncmp(str, "-t", 2) == 0) {
|
|
if(*p == '\0') {
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
while(isspace(*p)) p++;
|
|
str = p;
|
|
while(*p && !isspace(*p)) p++;
|
|
sscanf(str, "%d", &dhcp_timeout);
|
|
dhcp_timer_set = 1;
|
|
}
|
|
else
|
|
if (strncmp(str, "-m", 2) == 0) {
|
|
if (*p == '\0') {
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
while (isspace(*p)) p++;
|
|
str = p;
|
|
while (*p && !isspace(*p)) p++;
|
|
strncpy(tbuf,str,p-str);
|
|
tbuf[p-str] = '\0';
|
|
set_addl_options_to_send(tbuf);
|
|
}
|
|
else
|
|
if (strncmp(str, "-r", 2) == 0) {
|
|
if (*p == '\0') {
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
while (isspace(*p)) p++;
|
|
str = p;
|
|
while (*p && !isspace(*p)) p++;
|
|
*p++ = '\0';
|
|
script_file_to_run = strdup(str);
|
|
if (stat(script_file_to_run, &st) == -1) {
|
|
syslog(LOG_ERR, "Script file %s not found (%m)",
|
|
script_file_to_run);
|
|
script_file_to_run = (char*)0;
|
|
}
|
|
}
|
|
#ifdef EDHCP
|
|
else
|
|
if (strncmp(str, "-pn", 3) == 0) { /* non blocking ping */
|
|
ping_nonblocking_check = 1;
|
|
ping_blocking_check = 0;
|
|
}
|
|
else if (strncmp(str, "-pb", 3) == 0) { /* blocking ping */
|
|
ping_blocking_check = 1;
|
|
ping_nonblocking_check = 0;
|
|
}
|
|
else if (strncmp(str, "-pt", 3) == 0) { /* ping timeout */
|
|
if(*p == '\0') {
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
while(isspace(*p)) p++;
|
|
str = p;
|
|
while(*p && !isspace(*p)) p++;
|
|
sscanf(str, "%d", &ping_timeout);
|
|
}
|
|
else if (strncmp(str, "-pl", 3) == 0) {
|
|
/* max pings pending to deactivate sending PING */
|
|
if(*p == '\0') {
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
while(isspace(*p)) p++;
|
|
str = p;
|
|
while(*p && !isspace(*p)) p++;
|
|
sscanf(str, "%d", &ping_maxout);
|
|
}
|
|
else if (strncmp(str, "-pd", 3) == 0) {
|
|
/* should we ping dns to check if an address is taken */
|
|
ping_dns = 1;
|
|
}
|
|
#ifdef EDHCP_LDAP
|
|
else if (strncmp(str, "-l", 2) == 0) {
|
|
/* ldap database */
|
|
if(*p == '\0') {
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
while(isspace(*p)) p++;
|
|
str = p;
|
|
while(*p && !isspace(*p)) p++;
|
|
*p++ = '\0';
|
|
ldap_conf_file = strdup(str);
|
|
}
|
|
#endif
|
|
else if (strncmp(str, "-dn", 3) == 0)
|
|
dhcp_dnsupd_on = 1; /* do dynamic dns updates */
|
|
else if (strncmp(str, "-ds", 3) == 0)
|
|
dhcp_dnsupd_secure = 0; /* don't use security */
|
|
else if (strncmp(str, "-db", 3) == 0)
|
|
dhcp_dnsupd_beforeACK = 1;
|
|
else if (strncmp(str, "-dc", 3) == 0)
|
|
dhcp_dnsupd_clientARR = 1;
|
|
else if (strncmp(str, "-da", 3) == 0)
|
|
dhcp_dnsupd_always = 1;
|
|
else if (strncmp(str, "-dt", 3) == 0) { /* ttl */
|
|
if(*p == '\0') {
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
while(isspace(*p)) p++;
|
|
str = p;
|
|
while(*p && !isspace(*p)) p++;
|
|
sscanf(str, "%d", &dhcp_dnsupd_ttl);
|
|
}
|
|
else if (strncmp(str, "-df", 3) == 0) {
|
|
if (*p == '\0') {
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
while(isspace(*p)) p++;
|
|
str = p;
|
|
while(*p && !isspace(*p)) p++;
|
|
*p++ = '\0';
|
|
ddns_conf_file = strdup(str);
|
|
}
|
|
#endif
|
|
else {
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
fclose(fp);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void
|
|
setIncrDbgFlg()
|
|
{
|
|
(void)signal(SIGUSR1, (void (*)())setIncrDbgFlg);
|
|
|
|
debug++;
|
|
if (debug)
|
|
syslog(LOG_DEBUG, "Debug turned ON, Level %d\n", debug);
|
|
#ifdef EDHCP_LDAP
|
|
ldhcp_level++;
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
setNoDbgFlg()
|
|
{
|
|
(void)signal(SIGUSR2, (void (*)())setNoDbgFlg);
|
|
|
|
if (debug)
|
|
syslog(LOG_DEBUG, "Debug turned OFF\n");
|
|
debug = 0;
|
|
#ifdef EDHCP_LDAP
|
|
ldhcp_level = LDHCP_LOG_RESOURCE;
|
|
#endif
|
|
}
|
|
|
|
|
|
#ifdef EDHCP_LDAP
|
|
static int rereadLdapConf = 0;
|
|
#endif
|
|
static int rereadConfig = 0;
|
|
|
|
static void
|
|
setRereadConfFlg(int sig)
|
|
{
|
|
|
|
(void)signal(sig, (void (*)())setRereadConfFlg);
|
|
if (sig == SIGHUP) {
|
|
rereadConfig = 1;
|
|
syslog(LOG_INFO, "received SIGHUP");
|
|
}
|
|
|
|
#ifdef EDHCP_LDAP
|
|
if (sig == SIGALRM)
|
|
rereadLdapConf = 1;
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
check_options_config(void)
|
|
{
|
|
int rc;
|
|
|
|
syslog(LOG_INFO, "received SIGHUP: reconfiguring");
|
|
process_dhcp_opts(opts_file);
|
|
if(alt_config_dir)
|
|
useConfigDir = alt_config_dir;
|
|
else
|
|
useConfigDir = DHCP_CONFIG_DIR;
|
|
#ifdef EDHCP_LDAP
|
|
/* read the ldap configuration file */
|
|
if (ldap_conf_file) {
|
|
rc = parse_ldap_dhcp_config(ldap_conf_file);
|
|
if (rc) {
|
|
exit(3);
|
|
}
|
|
}
|
|
else
|
|
free_ld_root();
|
|
#ifdef EDHCP
|
|
if ((ping_blocking_check) ||
|
|
(ping_nonblocking_check)) {
|
|
if (ping_sd < 0) {
|
|
ping_sd = init_ping();
|
|
if (ping_sd < 0) {
|
|
syslog(LOG_ERR, "Error in opening ping socket");
|
|
exit(1);
|
|
}
|
|
ident = htons(getpid()) & 0xFFFF;
|
|
}
|
|
}
|
|
else {
|
|
if (ping_sd > 0) {
|
|
close(ping_sd);
|
|
ping_sd = -1;
|
|
}
|
|
}
|
|
read_dtab();
|
|
#endif
|
|
cf0_free_config_ldap();
|
|
#endif
|
|
if (rc = cf0_reread_config()) {
|
|
syslog(LOG_ERR, "unable to re-read DHCP Master Configuration %d", rc);
|
|
exit(1);
|
|
}
|
|
|
|
#ifdef EDHCP
|
|
if (dhcp_dnsupd_secure) {
|
|
rc = parse_ddns_dhcp_config(ddns_conf_file);
|
|
if (rc)
|
|
exit(4);
|
|
}
|
|
#endif
|
|
rereadConfig = 0;
|
|
}
|
|
|
|
void
|
|
cleanup_atexit(void)
|
|
{
|
|
#ifdef EDHCP_LDAP
|
|
(void) cleanup_restabm();
|
|
(void) cleanup_caches();
|
|
(void) cleanup_DHCPServiceLdap();
|
|
#endif
|
|
free_vendor_options();
|
|
cf0_free_config_list(0);
|
|
}
|
|
|
|
|
|
void
|
|
main(int argc, char *argv[])
|
|
{
|
|
register struct bootp *bp;
|
|
register int n;
|
|
struct sockaddr_in from;
|
|
int fromlen;
|
|
int ch;
|
|
int len, i;
|
|
fd_set readfds;
|
|
struct timeval tmout, *ptmout;
|
|
long next_tm;
|
|
struct dhcp_timelist *tm_entry;
|
|
char sifname[IFNAMSIZ+1];
|
|
iaddr_t ipn;
|
|
int on = 1;
|
|
int off = 0;
|
|
int rc = 0;
|
|
#ifdef EDHCP
|
|
struct dhcp_request_list* dhcp_rq;
|
|
#endif
|
|
int maxfd;
|
|
|
|
static int ignore_first_packet = 1;
|
|
|
|
struct dhcp_timelist *dh0_pop_timelist_timer(void);
|
|
extern char *optarg;
|
|
|
|
|
|
if(chkconf(DHCP_MASTER, 0) == CHK_ON)
|
|
ProclaimServer = 1;
|
|
|
|
/* Before we do anything we check for a valid Flexlm License. */
|
|
#ifdef EDHCP
|
|
{
|
|
char *message;
|
|
if ( ProclaimServer &&
|
|
(getLicense(argv[0], &message) == 0) ) { /* return 1 is ok */
|
|
openlog(argv[0], (LOG_PID | LOG_NDELAY), LOG_DAEMON);
|
|
syslog(LOG_NOTICE, "EDHCP %s: %s", argv[0], message);
|
|
exit(1);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
while ((ch = getopt(argc, argv, "dfpbo:")) != -1) {
|
|
switch(ch) {
|
|
case 'd':
|
|
/* Debug Info into syslog */
|
|
debug++;
|
|
#ifdef EDHCP_LDAP
|
|
ldhcp_level++;
|
|
#endif
|
|
break;
|
|
case 'f':
|
|
/* enable cross network forwarding */
|
|
forwarding++;
|
|
break;
|
|
case 'p':
|
|
/* Sleep before processing the message, allows for easy
|
|
* debugging by dbx
|
|
*/
|
|
sleepmode++;
|
|
break;
|
|
case 'b':
|
|
/* Run in standalone mode */
|
|
standalone_mode++;
|
|
break;
|
|
case 'o':
|
|
/* Options file specified for dhcp specific options */
|
|
if(!ProclaimServer)
|
|
break;
|
|
opts_file = strdup(optarg);
|
|
rc = process_dhcp_opts(opts_file);
|
|
break;
|
|
default:
|
|
rc = 2;
|
|
}
|
|
}
|
|
|
|
(void) signal(SIGUSR1, setIncrDbgFlg);
|
|
(void) signal(SIGUSR2, setNoDbgFlg);
|
|
#ifdef EDHCP_LDAP
|
|
(void) signal(SIGALRM, setRereadConfFlg);
|
|
#endif
|
|
(void) signal(SIGHUP, setRereadConfFlg);
|
|
|
|
time(&TimeNow);
|
|
openlog("bootp", LOG_PID|LOG_CONS, LOG_DAEMON);
|
|
syslog(LOG_INFO, "%s starting at %s", VERS, ctime(&TimeNow));
|
|
switch(rc) {
|
|
case 1:
|
|
syslog(LOG_ERR, "Cannot parse the DHCP options file %s",opts_file);
|
|
exit(1);
|
|
case 2:
|
|
syslog(LOG_ERR, "Illegal command line option.");
|
|
exit(2);
|
|
}
|
|
|
|
#ifdef EDHCP_LDAP
|
|
/* read the ldap configuration file */
|
|
if (ldap_conf_file) {
|
|
rc = parse_ldap_dhcp_config(ldap_conf_file);
|
|
if (rc) {
|
|
exit(3);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
rfc1533ptr = get_rfc1533_config();
|
|
|
|
/*
|
|
* It is not necessary to create a socket for input.
|
|
* The inet master daemon passes the required socket
|
|
* to this process as fd 0. Bootp takes control of
|
|
* the socket for a while and gives it back if it ever
|
|
* remains idle for the timeout limit.
|
|
*/
|
|
s = 0;
|
|
if(standalone_mode) {
|
|
s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
if (s < 0) {
|
|
syslog(LOG_ERR,"Opening DGRAM stream socket(%m)");
|
|
exit(1);
|
|
}
|
|
dup2(s, 0);
|
|
bzero((char *)&sin, sizeof(sin));
|
|
sin.sin_family = AF_INET;
|
|
sin.sin_addr.s_addr = INADDR_ANY;
|
|
sin.sin_port = htons(IPPORT_BOOTPS);
|
|
if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
|
|
syslog(LOG_ERR,"Binding DGRAM stream socket(%m)");
|
|
exit(1);
|
|
}
|
|
}
|
|
/*
|
|
* initialize ping socket
|
|
*/
|
|
maxfd = s;
|
|
#ifdef EDHCP
|
|
if ((ping_blocking_check) || (ping_nonblocking_check)) {
|
|
ping_sd = init_ping();
|
|
if (ping_sd < 0) {
|
|
syslog(LOG_ERR, "Error in opening ping socket");
|
|
exit(1);
|
|
}
|
|
maxfd = (ping_sd > maxfd)? ping_sd: maxfd;
|
|
ident = htons(getpid()) & 0xFFFF;
|
|
}
|
|
#endif
|
|
/*
|
|
* Save the name of the executing host
|
|
*/
|
|
makenamelist();
|
|
|
|
ifcount = sr_initialize_nets();
|
|
if(MULTIHMDHCP) {
|
|
rsockout_err = sr_initialize_broadcast_sockets();
|
|
}
|
|
else if(ProclaimServer) {
|
|
rsockout_err = sr_initialize_single_broadcast_socket();
|
|
}
|
|
|
|
db = log_dbmopen(EtherIpFile);
|
|
dblock = open(lockEtherIpFile, O_RDONLY|O_CREAT, 0604);
|
|
if (dblock < 0) {
|
|
syslog(LOG_ERR, "Could not open %s (%m)", lockEtherIpFile);
|
|
log_dbmclose(db);
|
|
exit(1);
|
|
}
|
|
|
|
if(ProclaimServer) {
|
|
mynetmask = nets[0].netmask;
|
|
if(alt_config_dir)
|
|
useConfigDir = alt_config_dir;
|
|
else
|
|
useConfigDir = DHCP_CONFIG_DIR;
|
|
if(rc = cf0_initialize_config()) {
|
|
syslog(LOG_ERR, "unable to read DHCP Master Configuration %d", rc);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
if(MULTIHMDHCP) {
|
|
if(setsockopt(s, SOL_SOCKET, SO_PASSIFNAME, &on, sizeof(on)) < 0) {
|
|
syslog(LOG_ERR, "Cannot set socket option SO_PASSIFNAME:(%m)");
|
|
exit(1);
|
|
}
|
|
}
|
|
else if (ifcount > 1) {
|
|
if(setsockopt(s, SOL_SOCKET, SO_PASSIFNAME, &off, sizeof(off)) < 0) {
|
|
syslog(LOG_ERR, "Cannot set socket option SO_PASSIFNAME:(%m)");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
if (ifcount < 2 && forwarding) {
|
|
if (debug)
|
|
syslog(LOG_DEBUG, "less than two interfaces, -f flag ignored");
|
|
forwarding = 0;
|
|
}
|
|
if(debug && (ifcount>1 ^ forwarding))
|
|
syslog(LOG_DEBUG, "%d network interface(s), forwarding is %s",
|
|
ifcount, forwarding ? "ENABLED" : "DISABLED");
|
|
len = sizeof (sin);
|
|
if (getsockname(s, &sin, &len) < 0) {
|
|
syslog(LOG_ERR, "getsockname failed (%m)");
|
|
exit(1);
|
|
}
|
|
|
|
#ifdef EDHCP
|
|
if (dhcp_dnsupd_secure) {
|
|
rc = parse_ddns_dhcp_config(ddns_conf_file);
|
|
if (rc)
|
|
exit(4);
|
|
}
|
|
#endif
|
|
|
|
if ((!ProclaimServer) || (!dhcp_timer_set)) {
|
|
dh0_set_timeval(&tmout, TIMEOUT);
|
|
dhcp_timeout = TIMEOUT;
|
|
}
|
|
else
|
|
dh0_set_timeval(&tmout, dhcp_timeout);
|
|
|
|
dh0_addto_timelist(0, 0, 0, TimeNow+dhcp_timeout, 0);
|
|
|
|
#ifdef EDHCP
|
|
/* read the mac filter file
|
|
*/
|
|
read_dtab();
|
|
#endif
|
|
|
|
for (;;) {
|
|
|
|
FD_ZERO(&readfds);
|
|
FD_SET(s, &readfds);
|
|
#ifdef EDHCP
|
|
if (ping_nonblocking_check) {
|
|
FD_SET(ping_sd, &readfds);
|
|
}
|
|
#endif
|
|
if (dhcp_timer_set && (dhcp_timeout == 0) && (dh0_timeouts_cnt == 1))
|
|
ptmout = (struct timeval*)0;
|
|
else
|
|
ptmout = &tmout;
|
|
switch (select(maxfd + 1, &readfds, (fd_set *)0,
|
|
(fd_set *)0, ptmout)) {
|
|
case 0:
|
|
/* Timeout happened : process timeouts */
|
|
if(dh0_timeouts_cnt == 1) {/* idle timeout happened */
|
|
|
|
#ifdef PERFORMANCE
|
|
if (discover_ctr > 0){
|
|
syslog(LOG_DEBUG,"The total time taken to process %lu DISCOVER messages = %lu usec.", discover_ctr, discover_time);
|
|
syslog(LOG_DEBUG,"The average time to process %lu DISCOVER messages = %lu usec.", discover_ctr, discover_time/discover_ctr);
|
|
}
|
|
if (request_ctr > 0){
|
|
syslog(LOG_DEBUG,"The total time taken to process %lu REQUESTS = %lu usec", request_ctr, request_time);
|
|
syslog(LOG_DEBUG,"The average time to process %lu REQUESTS = %lu usec", request_ctr, request_time/request_ctr);
|
|
}
|
|
#endif
|
|
|
|
log_dbmclose(db);
|
|
cleanup_atexit();
|
|
exit(0);
|
|
_identString = _identString; /*no complaints for ID string*/
|
|
}
|
|
|
|
|
|
/* Remove the First timeout and clean up, also set the next timeout
|
|
* from the list. Do not update the idle timeout.
|
|
*/
|
|
tm_entry = dh0_pop_timelist_timer();
|
|
if(tm_entry) {
|
|
#ifdef EDHCP
|
|
/* if this was an entry for which
|
|
* an ping was sent then process that */
|
|
if (tm_entry->tm_dhcp_rq)
|
|
dhcp_rq = tm_entry->tm_dhcp_rq;
|
|
else
|
|
dhcp_rq = dh0_find_dhcp_rqlist_entry(tm_entry->cid_ptr,
|
|
tm_entry->cid_length);
|
|
if (dhcp_rq && (dhcp_rq->ping_status == PING_SENT)) {
|
|
ping_send_DHCPOFFER(dhcp_rq);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
dh0_remove_from_dhcp_reqlist(tm_entry->cid_ptr,
|
|
tm_entry->cid_length);
|
|
|
|
loc0_remove_entry(tm_entry->tm_eaddr, 0,
|
|
tm_entry->cid_ptr,
|
|
tm_entry->cid_length, db);
|
|
}
|
|
/* prevent major memory leak */
|
|
dh0_free_timelist_entry(tm_entry);
|
|
}
|
|
else {
|
|
syslog(LOG_ERR, "No timer entry found for processing.");
|
|
}
|
|
time(&TimeNow);
|
|
next_tm = dh0_update_and_get_next_timeout(TimeNow);
|
|
dh0_set_timeval(&tmout, next_tm);
|
|
break;
|
|
case -1:
|
|
time(&TimeNow);
|
|
dhcp_tmlist_last->tm_value = TimeNow+dhcp_timeout;
|
|
next_tm = dh0_update_and_get_next_timeout(TimeNow);
|
|
dh0_set_timeval(&tmout, next_tm);
|
|
if (errno == EINTR)
|
|
continue;
|
|
syslog(LOG_ERR, "select error (%m)");
|
|
break;
|
|
default:
|
|
#ifdef EDHCP
|
|
if (FD_ISSET(ping_sd, &readfds)) {
|
|
handle_ping_nonblocking_reply(ping_sd);
|
|
}
|
|
if (ping_number_pending > 0)
|
|
process_expired_ping_send();
|
|
#endif
|
|
if (FD_ISSET(s, &readfds)) {
|
|
fromlen = sizeof (from);
|
|
n = recvfrom(s, buf, sizeof buf, 0, (caddr_t)&from,
|
|
&fromlen);
|
|
if ( (n <= 0) || (n < BOOTPKTSZ) )
|
|
continue;
|
|
|
|
if(MULTIHMDHCP) {
|
|
bzero(sifname, IFNAMSIZ);
|
|
np_recv = 0;
|
|
strncpy(sifname, (char *)buf, IFNAMSIZ);
|
|
if(*sifname == '\0')
|
|
continue;
|
|
for (i = 0; i < ifcount; i++) {
|
|
if(strcmp(nets[i].ifname, sifname) == 0) {
|
|
np_recv = &nets[i];
|
|
break;
|
|
}
|
|
}
|
|
/* The socket option SO_PASSIFNAME is not set for the first
|
|
packet beacuse inetd receives it before option was set*/
|
|
if (ignore_first_packet) {
|
|
ignore_first_packet = 0;
|
|
continue;
|
|
}
|
|
if(!np_recv) {
|
|
syslog(LOG_ERR, "Cannot find interface %s", sifname);
|
|
continue;
|
|
}
|
|
bp = (struct bootp *)&buf[IFNAMSIZ];
|
|
}
|
|
else {
|
|
bp = (struct bootp *)buf;
|
|
}
|
|
|
|
time(&TimeNow);
|
|
dhcp_tmlist_last->tm_value = TimeNow+dhcp_timeout;
|
|
|
|
if (time(&msg_recv_time) == (time_t)-1)
|
|
syslog(LOG_ERR, "Error in time(): (%m)");
|
|
|
|
readtab(); /* (re)read bootptab */
|
|
if (rereadConfig) {
|
|
check_options_config();
|
|
if (ping_sd > 0)
|
|
maxfd = (ping_sd > maxfd)? ping_sd: maxfd;
|
|
else
|
|
maxfd = s;
|
|
}
|
|
#ifdef EDHCP_LDAP
|
|
if (rereadLdapConf) {
|
|
check_ldap_subnet_refresh();
|
|
rereadLdapConf = 0;
|
|
}
|
|
#endif
|
|
|
|
switch (bp->bp_op) {
|
|
case BOOTREQUEST:
|
|
if(MULTIHMDHCP && np_recv) {
|
|
ipn.s_addr = np_recv->netmask;
|
|
reg_net = inet_ntoa(ipn);
|
|
strcpy(reg_netmask, reg_net);
|
|
reg_net = inet_ntoa(np_recv->myaddr);
|
|
}
|
|
else {
|
|
ipn.s_addr = nets[0].netmask;
|
|
reg_net = inet_ntoa(ipn);
|
|
strcpy(reg_netmask, reg_net);
|
|
reg_net = inet_ntoa(nets[0].myaddr);
|
|
}
|
|
strcpy(reg_hostnet, reg_net);
|
|
request(bp);
|
|
break;
|
|
case BOOTREPLY:
|
|
reply(bp);
|
|
break;
|
|
}
|
|
}
|
|
time(&TimeNow);
|
|
next_tm = dh0_update_and_get_next_timeout(TimeNow);
|
|
dh0_set_timeval(&tmout, next_tm);
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
process_rfc1533_bootp_message(struct bootp *rp)
|
|
{
|
|
int rval;
|
|
|
|
if(rfc1533ptr == 0)
|
|
return 1;
|
|
rval = cf0_encode_rfc1533_opts(rp->dh_opts);
|
|
return rval;
|
|
}
|
|
|
|
/*
|
|
* Check the passed name against the current host's addresses.
|
|
*
|
|
* Return value
|
|
* TRUE if match
|
|
* FALSE if no match
|
|
*/
|
|
int
|
|
matchhost(char *name)
|
|
{
|
|
register struct hostent *hp;
|
|
int i;
|
|
u_int requested_addr;
|
|
|
|
if (hp = gethostbyname(name)) {
|
|
requested_addr = *(u_long *)(hp->h_addr_list[0]);
|
|
for (i = 0; i < numaddrs; i++)
|
|
if (requested_addr == myaddrs[i])
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Check whether two passed IP addresses are on the same wire.
|
|
* The first argument is the IP address of the requestor, so
|
|
* it must be on one of the wires to which the server is
|
|
* attached.
|
|
*
|
|
* Return value
|
|
* TRUE if on same wire
|
|
* FALSE if not on same wire
|
|
*/
|
|
int
|
|
samewire(register iaddr_t *src, register iaddr_t *dest)
|
|
{
|
|
register struct netaddr *np;
|
|
|
|
/*
|
|
* It may well happen that src is zero, since the datagram
|
|
* comes from a PROM that may not yet know its own IP address.
|
|
* In that case the socket layer doesnt know what to fill in
|
|
* as the from address. In that case, we have to assume that
|
|
* the source and dest are on different wires. This means
|
|
* we will be doing forwarding sometimes when it isnt really
|
|
* necessary.
|
|
*/
|
|
if (src->s_addr == 0 || dest->s_addr == 0)
|
|
return 0;
|
|
|
|
/*
|
|
* In order to take subnetworking into account, one must
|
|
* use the netmask to tell how much of the IP address
|
|
* actually corresponds to the real network number.
|
|
*
|
|
* Search the table of nets to which the server is connected
|
|
* for the net containing the source address.
|
|
*/
|
|
for (np = nets; np->netmask != 0; np++)
|
|
if ((src->s_addr & np->netmask) == np->net)
|
|
return ((dest->s_addr & np->netmask) == np->net);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int execute_script(void)
|
|
{
|
|
int i;
|
|
char scriptbuf[256];
|
|
char *ethc;
|
|
state_change_t *p_stch;
|
|
char str[MAXCIDLEN];
|
|
|
|
for (i = 0; i < script_state_change_index; i++) {
|
|
p_stch = &script_state_changes[i];
|
|
mk_str(1, p_stch->cid, p_stch->cid_length, str);
|
|
ethc = ether_ntoa(&p_stch->mac);
|
|
sprintf(scriptbuf, "%s '-c %s -m %s -i %s -h %s -l %d -o %d -t %d' &",
|
|
script_file_to_run, str, ethc, p_stch->ipc, p_stch->hostname,
|
|
p_stch->lease, p_stch->op, script_msgtype);
|
|
if (debug)
|
|
syslog(LOG_DEBUG, "Script: %s", scriptbuf);
|
|
if (system(scriptbuf) == -1) {
|
|
syslog(LOG_ERR, "Could not execute %s (%m)",
|
|
scriptbuf);
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static long
|
|
find_lease_to_offer(u_long newaddr, u_int dhcp_lease,
|
|
dhcpConfig* newConfigPtr, DBM *db)
|
|
{
|
|
long offered_lease;
|
|
|
|
offered_lease = loc0_get_lease((u_long)newaddr, db);
|
|
if ( (dhcp_lease == -1) && ( (offered_lease == INFINITE_LEASE) ||
|
|
(offered_lease == STATIC_LEASE) ) )
|
|
offered_lease = -1;
|
|
else {
|
|
if ( (dhcp_lease > 0) && (dhcp_lease < newConfigPtr->p_lease) )
|
|
offered_lease = (long)dhcp_lease;
|
|
else
|
|
offered_lease = (long)newConfigPtr->p_lease;
|
|
}
|
|
return offered_lease;
|
|
}
|
|
|
|
int
|
|
process_dhcp_message(struct bootp *rq, struct bootp *rp)
|
|
{
|
|
char *dh0_getNisDomain(void);
|
|
char *dh0_getDnsDomain(void);
|
|
struct dhcp_timelist *dh0_find_timelist_entry(char *, int);
|
|
struct dhcp_request_list *dh0_find_dhcp_rqlist_entry(char *,int);
|
|
|
|
char npath[4];
|
|
char hostname1[MAXHOSTNAMELEN];
|
|
int has_sname; /* does rq have nonempty bp_sname? */
|
|
struct hostent *hostentp;
|
|
iaddr_t fromaddr, ipn;
|
|
|
|
int dhcp_msg_type;
|
|
struct getParams dhcp_reqParams;
|
|
u_long dhcp_ipaddr, dhcp_server;
|
|
char *dhcp_hsname, *dhcp_msg;
|
|
u_int dhcp_lease;
|
|
char *resolv_name;
|
|
dhcpConfig *newConfigPtr;
|
|
|
|
u_long newaddr, netaddr;
|
|
int nameaddr_err;
|
|
int rval, aval;
|
|
struct dhcp_request_list *req;
|
|
struct dhcp_timelist *tmq;
|
|
|
|
char *rhsname;
|
|
u_long mltaddr;
|
|
int typ;
|
|
int brk_flag;
|
|
|
|
char *client_domain;
|
|
char *cid_ptr = 0; /*CHECK*/
|
|
int cid_flag = 0;
|
|
int cid_length;
|
|
char str[1024];
|
|
int sgi_resolv_name = 0; /* not sgis proprietary resolv name */
|
|
ping_status_t ping_status = PING_NONE;
|
|
int timeout;
|
|
#ifdef EDHCP
|
|
int ret;
|
|
#endif
|
|
struct dhcp_client_info dci_data;
|
|
long offered_lease; /* length of lease offered */
|
|
|
|
offered_lease = 0;
|
|
bzero(str, 1024);
|
|
newaddr = 0;
|
|
resolv_name = (char *)0;
|
|
dhcp_hsname = dhcp_msg = (char *)0;
|
|
dhcp_ipaddr = dhcp_server = 0;
|
|
bzero(&dhcp_reqParams, sizeof(struct getParams));
|
|
dhcp_lease = 0;
|
|
bzero(&dci_data, sizeof(struct dhcp_client_info));
|
|
|
|
npath[0] = '\0';
|
|
strcpy((char *)rp->bp_file, npath);
|
|
has_sname = rq->bp_sname[0] != '\0';
|
|
if (!has_sname)
|
|
strncpy((char *)rp->bp_sname, myname, sizeof(rp->bp_sname));
|
|
else if (!matchhost((char *)rq->bp_sname)) {
|
|
iaddr_t destaddr;
|
|
/* Not for us. */
|
|
if (!forwarding)
|
|
return 0;
|
|
fromaddr = rq->bp_ciaddr.s_addr ? rq->bp_ciaddr : rp->bp_yiaddr;
|
|
/* The client should not be asking for a new IP address from
|
|
* a specific server.
|
|
*/
|
|
if(fromaddr.s_addr == 0)
|
|
return 0;
|
|
/*
|
|
* Look up the host by name and decide whether
|
|
* we should forward the message to him.
|
|
*/
|
|
if ((hostentp = gethostbyname((const char *)rq->bp_sname)) == 0) {
|
|
syslog(LOG_INFO, "request for unknown server %s", rq->bp_sname);
|
|
return 0;
|
|
}
|
|
destaddr.s_addr = *(u_long *)(hostentp->h_addr_list[0]);
|
|
/*
|
|
* If the other server is on a different cable from the
|
|
* requestor, then forward the request. If on the same
|
|
* wire, there is no point in forwarding. Note that in
|
|
* the case that we don't yet know the IP address of the
|
|
* client, there is no way to tell whether the client and
|
|
* server are actually on the same wire. In that case
|
|
* we forward regardless. It's redundant, but there's
|
|
* no way to get around it.
|
|
*/
|
|
if (!samewire(&fromaddr, &destaddr)) {
|
|
/*
|
|
* If we were able to compute the client's Internet
|
|
* address, pass that information along in case the
|
|
* other server doesn't have the info.
|
|
*/
|
|
rq->bp_yiaddr = rp->bp_yiaddr;
|
|
forward(rq, &fromaddr, &destaddr);
|
|
}
|
|
return 0;
|
|
}
|
|
#ifdef EDHCP
|
|
/* if a matching mac address is found take appropriate actions */
|
|
if (lookup_dtab(rq->bp_hlen, rq->bp_htype, rq->bp_chaddr))
|
|
return 0; /* drop packet */
|
|
#endif
|
|
|
|
dhcp_msg_type = dh0_decode_dhcp_client_packet(rq->dh_opts, &dhcp_reqParams,
|
|
&dhcp_ipaddr, &dhcp_lease,
|
|
&dhcp_server, &resolv_name,
|
|
&cid_ptr, &cid_flag, &cid_length,
|
|
&sgi_resolv_name,
|
|
&dci_data);
|
|
|
|
|
|
if (!cid_flag){
|
|
cid_length = sizeof(EtherAddr);
|
|
/*Assuming that the rq->bp_chaddr is not going to be longer than
|
|
sizeof(EtherAddr) i.e. 6 bytes(true for Ethernet & FDDI)*/
|
|
cid_ptr = (char *)malloc(cid_length);
|
|
bzero(cid_ptr,cid_length);
|
|
bcopy(rq->bp_chaddr, cid_ptr, cid_length);
|
|
}
|
|
|
|
mk_str(cid_flag, cid_ptr, cid_length, str);
|
|
/*The 'str'(ing) is being used in displaying in the SYSLOG only. The key
|
|
used for the ndbm file in case of cid_flag=0 is just the cid (without the
|
|
h/w type) whereas when cid_flag=1 the key used includes the h/w type and
|
|
the cid.
|
|
*/
|
|
|
|
script_state_change_index = 0; /* signifies no changes as yet */
|
|
|
|
switch(dhcp_msg_type){
|
|
case DHCPDISCOVER:
|
|
#ifdef PERFORMANCE
|
|
perf_flag = DHCPDISCOVER;
|
|
gettimeofday(&perf_time_start,&perf_tz);
|
|
#endif
|
|
script_msgtype = DHCPOFFER;
|
|
if (debug >= 3) {
|
|
syslog(LOG_DEBUG,
|
|
"DHCPDISCOVER: cid (%s)",str);
|
|
}
|
|
|
|
if(rq->bp_ciaddr.s_addr == 0) {
|
|
/* client doesn't know his IP address */
|
|
if(dhcp_ipaddr) {
|
|
/* client is requesting a specific address */
|
|
if(loc0_is_ipaddress_for_cid((EtherAddr *)rq->bp_chaddr,
|
|
dhcp_ipaddr, cid_ptr,
|
|
cid_length, db)== 0) {
|
|
newConfigPtr = cf0_get_config(dhcp_ipaddr, 1);
|
|
if (newConfigPtr) {
|
|
mltaddr = MULTIHMDHCP ? np_recv->myaddr.s_addr : myhostaddr.s_addr;
|
|
netaddr = rq->bp_giaddr.s_addr ? rq->bp_giaddr.s_addr : mltaddr;
|
|
if (cf0_is_req_addr_valid(dhcp_ipaddr, netaddr)== 0)
|
|
newaddr = dhcp_ipaddr;
|
|
}
|
|
}
|
|
if (debug) {
|
|
ipn.s_addr = dhcp_ipaddr;
|
|
syslog(LOG_DEBUG,
|
|
"DHCPDISCOVER: cid (%s) request address (%s)",str,
|
|
inet_ntoa(ipn));
|
|
}
|
|
|
|
}
|
|
retry_inconsistent:
|
|
if(newaddr == 0) {
|
|
/* The local mapping will also get created here as a new */
|
|
/* IP address and hostname is generated */
|
|
if(rq->bp_giaddr.s_addr) {
|
|
if (debug >= 2)
|
|
syslog(LOG_DEBUG,
|
|
"DHCPDISCOVER: cid (%s) thru gateway (%s)",
|
|
str, inet_ntoa(rq->bp_giaddr));
|
|
/* This is a forwarded message, use the gateway addr */
|
|
if(cf0_get_new_ipaddress((EtherAddr *)rq->bp_chaddr,
|
|
rq->bp_giaddr.s_addr,
|
|
dhcp_ipaddr,
|
|
&newaddr, &newConfigPtr,
|
|
cid_ptr, cid_length, db,
|
|
resolv_name))
|
|
return 1;
|
|
}
|
|
else {
|
|
/* Generate using my IP Address */
|
|
mltaddr =
|
|
MULTIHMDHCP ? np_recv->myaddr.s_addr : myhostaddr.s_addr;
|
|
if(cf0_get_new_ipaddress((EtherAddr *)rq->bp_chaddr,
|
|
mltaddr, dhcp_ipaddr,
|
|
&newaddr,&newConfigPtr,
|
|
cid_ptr, cid_length, db,
|
|
resolv_name))
|
|
return 1;
|
|
if (debug) {
|
|
ipn.s_addr = newaddr;
|
|
syslog(LOG_DEBUG,
|
|
"DHCPDISCOVER: cid (%s) new address (%s)", str,
|
|
inet_ntoa(ipn));
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
/* same address is being assigned as was earlier */
|
|
/* change lease to zero so that if this lease is expired it is
|
|
* not assigned to anyone else. In case pings are sent it is
|
|
* possible to erroneously give out the same ip to two
|
|
* clients unless the lease is zeroed in the database
|
|
*/
|
|
if (loc0_zero_expired_lease((EtherAddr *)rq->bp_chaddr,
|
|
cid_ptr, cid_length, db)) {
|
|
syslog(LOG_DEBUG,
|
|
"DHCPDISCOVER: cid (%s) Record must exist",str);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (debug)
|
|
syslog(LOG_DEBUG,
|
|
"DHCPDISCOVER: cid (%s) ciaddr is non zero",str);
|
|
/* ciaddr MUST be 0 in a DHCPDISCOVER message */
|
|
/* newaddr = rq->bp_ciaddr.s_addr; */
|
|
/* newConfigPtr = cf0_get_config(newaddr); */
|
|
return 1;
|
|
}
|
|
if (resolv_name) {
|
|
free(resolv_name);
|
|
resolv_name = (char*)0;
|
|
}
|
|
/* gets here if dhcp_ipaddr set to a valid mapping */
|
|
dhcp_hsname =
|
|
loc0_get_hostname_from_cid(cid_ptr, cid_length,
|
|
(EtherAddr *)rq->bp_chaddr, db);
|
|
if (debug >= 2)
|
|
syslog(LOG_DEBUG, "DHCPDISCOVER assigning hostname (%s)",dhcp_hsname);
|
|
if(dhcp_hsname == (char *)0) {
|
|
return 1;
|
|
}
|
|
|
|
/* additional consistency check here for the case where a requested
|
|
* address is being given out */
|
|
if (debug) {
|
|
if (dhcp_ipaddr &&
|
|
loc0_is_etherToIP_inconsistent(cid_ptr, cid_length,
|
|
(EtherAddr *)rq->bp_chaddr,
|
|
newaddr, dhcp_hsname, db)){
|
|
/* inconsistency found - offer another address */
|
|
dhcp_ipaddr = 0;/* new address */
|
|
newaddr = 0;
|
|
syslog(LOG_DEBUG, "DHCPDISCOVER retry inconsistent client(%s)",
|
|
str);
|
|
goto retry_inconsistent;
|
|
}
|
|
}
|
|
/* send only host part */
|
|
dhcp_hsname = strtok(dhcp_hsname, " .");
|
|
#ifdef EDHCP
|
|
/* if ping checking is on then prepare to send the ICMP request
|
|
*/
|
|
if (ping_nonblocking_check &&
|
|
(ping_number_pending <= DHCPPING_MAXOUT)) {
|
|
ipn.s_addr = newaddr;
|
|
ret = send_ping( ping_sd, newaddr, ++ntransmitted, ident);
|
|
if ( (ret < 0) && (errno != EWOULDBLOCK) ) {
|
|
syslog(LOG_ERR, "Error in send_ping - nonblocking errno=%d",
|
|
errno);
|
|
return(-1);
|
|
}
|
|
if (debug)
|
|
syslog(LOG_DEBUG,"PING_SENT:cid (%s),ip (%s), name (%s)",
|
|
str,inet_ntoa(ipn), dhcp_hsname);
|
|
ping_status = PING_SENT;
|
|
timeout = ping_timeout;
|
|
}
|
|
#endif
|
|
#ifdef EDHCP_LDAP
|
|
/* get a specific config if one is available
|
|
* this updates the configPtr if there was one in the ldap database */
|
|
newConfigPtr = cf0_get_config((u_long)newaddr, 1);
|
|
#endif
|
|
/* compute the lease to offer */
|
|
offered_lease = find_lease_to_offer((u_long)newaddr, dhcp_lease,
|
|
newConfigPtr, db);
|
|
|
|
if ((ping_status == PING_NONE) || (ping_status == PING_RECV)) {
|
|
if(newConfigPtr->p_choose_name)
|
|
dh0_encode_dhcp_server_packet(rp->dh_opts,
|
|
DHCPOFFER,
|
|
&dhcp_reqParams, offered_lease,
|
|
dhcp_msg,
|
|
dhcp_hsname, dhcp_hsname, newConfigPtr, &dci_data);
|
|
else
|
|
dh0_encode_dhcp_server_packet(rp->dh_opts,
|
|
DHCPOFFER,
|
|
&dhcp_reqParams, offered_lease,
|
|
dhcp_msg,
|
|
dhcp_hsname, 0, newConfigPtr, &dci_data);
|
|
}
|
|
rp->bp_yiaddr.s_addr = (u_long)newaddr;
|
|
|
|
req = dh0_add_to_dhcp_reqlist(cid_ptr, cid_length,
|
|
(EtherAddr *)rq->bp_chaddr, newaddr,
|
|
dhcp_hsname, &dhcp_reqParams, dhcp_server,
|
|
dhcp_lease, newConfigPtr,
|
|
ping_status, ident, ntransmitted,
|
|
dhcp_msg, has_sname, rp, rq, &dci_data);
|
|
|
|
|
|
if ((ping_status == PING_NONE) || (ping_status == PING_RECV)) {
|
|
if (debug)
|
|
syslog(LOG_DEBUG,"DHCPOFFER:cid (%s),ip (%s), name (%s)",
|
|
str,inet_ntoa(rp->bp_yiaddr), dhcp_hsname);
|
|
if(rp->bp_flags & BROADCAST_BIT)
|
|
sendreply(rp, 0, has_sname, 1, DHCP_REPLY);
|
|
else
|
|
sendreply(rp, 0, has_sname, 0, DHCP_REPLY);
|
|
timeout = DHCPOFFER_TIMEOUT;
|
|
}
|
|
time(&TimeNow);
|
|
dh0_addto_timelist(cid_ptr, cid_length,
|
|
(EtherAddr *)rq->bp_chaddr,
|
|
TimeNow+timeout, req);
|
|
break;
|
|
case DHCPREQUEST:
|
|
#ifdef PERFORMANCE
|
|
perf_flag = DHCPREQUEST;
|
|
gettimeofday(&perf_time_start,&perf_tz);
|
|
#endif
|
|
script_msgtype = DHCPACK;
|
|
if (debug >= 3) {
|
|
syslog(LOG_DEBUG,
|
|
"DHCPREQUEST: cid (%s)",str);
|
|
}
|
|
mltaddr = MULTIHMDHCP ? np_recv->myaddr.s_addr : myhostaddr.s_addr;
|
|
ipn.s_addr = dhcp_server;
|
|
if( (dhcp_server == mltaddr) || matchhost(inet_ntoa(ipn)) ) {
|
|
if (debug) {
|
|
syslog(LOG_DEBUG, "DHCPREQUEST:cid (%s) for server (%s)",
|
|
str,inet_ntoa(ipn));
|
|
}
|
|
/* This message is from a client responding to a DHCPOFFER
|
|
* message from us.
|
|
*/
|
|
req = dh0_find_dhcp_rqlist_entry(cid_ptr, cid_length);
|
|
if (req == 0) {
|
|
loc0_remove_entry((EtherAddr *)rq->bp_chaddr, 0, cid_ptr,
|
|
cid_length, db);
|
|
break;
|
|
}
|
|
nameaddr_err = 0;
|
|
typ = 0;
|
|
brk_flag = 0;
|
|
if(resolv_name && sgi_resolv_name) {
|
|
script_msgtype = DHCPOFFER;
|
|
if(resolv_name[1] == ':') {
|
|
rhsname = &resolv_name[2];
|
|
resolv_name[1] = '\0';
|
|
typ = atoi(resolv_name);
|
|
}
|
|
else {
|
|
rhsname = resolv_name;
|
|
}
|
|
switch(typ) {
|
|
case 0:
|
|
case SELECTED_NAME:
|
|
/* While adding the check for ip address
|
|
* have tried to retain the old logic
|
|
* even the part that interacts with
|
|
* alt_naming_flag! which is suspicious */
|
|
nameaddr_err = check_selected_name_address
|
|
(rhsname, &dhcp_msg, 0, rq, req, cid_ptr,
|
|
cid_length);
|
|
|
|
if (alt_naming_flag) {
|
|
if (nameaddr_err == 2)
|
|
break;
|
|
}
|
|
|
|
if(nameaddr_err) {
|
|
/* compute the lease to offer */
|
|
offered_lease = find_lease_to_offer(req->dh_ipaddr,
|
|
req->dh_reqLease,
|
|
req->dh_configPtr,
|
|
db);
|
|
|
|
dh0_encode_dhcp_server_packet(rp->dh_opts,
|
|
DHCPOFFER, &(req->dh_reqParams),
|
|
offered_lease, dhcp_msg,
|
|
req->dh_hsname, req->dh_hsname,
|
|
req->dh_configPtr, &dci_data);
|
|
rp->bp_yiaddr.s_addr = (u_long)req->dh_ipaddr;
|
|
|
|
if(rp->bp_flags & BROADCAST_BIT)
|
|
sendreply(rp, 0, has_sname, 1, DHCP_REPLY);
|
|
else
|
|
sendreply(rp, 0, has_sname, 0, DHCP_REPLY);
|
|
tmq = dh0_find_timelist_entry(cid_ptr, cid_length);
|
|
time(&TimeNow);
|
|
tmq->tm_value = TimeNow+DHCPOFFER_TIMEOUT;
|
|
brk_flag++;
|
|
break;
|
|
}
|
|
else {
|
|
free(req->dh_hsname);
|
|
req->dh_hsname = strdup(rhsname);
|
|
loc0_remove_entry((EtherAddr *)rq->bp_chaddr,0,
|
|
cid_ptr, cid_length, db);
|
|
if(dhcp_msg) {
|
|
free(dhcp_msg);
|
|
dhcp_msg = (char *)0;
|
|
}
|
|
}
|
|
break;
|
|
case KEEP_OLD_NAMEADDR:
|
|
case NEW_NAMEADDR:
|
|
req->dh_ipaddr = dhcp_ipaddr;
|
|
/* Fall through */
|
|
case KEEP_OLD_NAMEONLY:
|
|
free(req->dh_hsname);
|
|
req->dh_hsname = strdup(rhsname);
|
|
nameaddr_err = check_selected_name_address
|
|
(rhsname, &dhcp_msg, dhcp_ipaddr, rq, req,
|
|
cid_ptr, cid_length);
|
|
if (alt_naming_flag) {
|
|
if (nameaddr_err == 2)
|
|
break;
|
|
}
|
|
if (nameaddr_err == 3) {
|
|
u_long mltaddr1;
|
|
dhcp_ipaddr = 0;
|
|
/* must get a new address */
|
|
if(rq->bp_giaddr.s_addr)
|
|
mltaddr1 = rq->bp_giaddr.s_addr;
|
|
else
|
|
mltaddr1 = mltaddr;
|
|
if(cf0_get_new_ipaddress((EtherAddr *)rq->bp_chaddr,
|
|
mltaddr1, dhcp_ipaddr,
|
|
&newaddr, &newConfigPtr,
|
|
cid_ptr, cid_length, db,
|
|
(char*)0))
|
|
return 1;
|
|
req->dh_ipaddr = newaddr;
|
|
}
|
|
if (nameaddr_err) {
|
|
/* compute the lease to offer */
|
|
offered_lease = find_lease_to_offer(req->dh_ipaddr,
|
|
req->dh_reqLease,
|
|
req->dh_configPtr,
|
|
db);
|
|
|
|
dh0_encode_dhcp_server_packet(rp->dh_opts,
|
|
DHCPOFFER, &(req->dh_reqParams),
|
|
offered_lease, dhcp_msg,
|
|
req->dh_hsname, req->dh_hsname,
|
|
req->dh_configPtr, &dci_data);
|
|
rp->bp_yiaddr.s_addr = (u_long)req->dh_ipaddr;
|
|
|
|
if(rp->bp_flags & BROADCAST_BIT)
|
|
sendreply(rp, 0, has_sname, 1, DHCP_REPLY);
|
|
else
|
|
sendreply(rp, 0, has_sname, 0, DHCP_REPLY);
|
|
tmq = dh0_find_timelist_entry(cid_ptr, cid_length);
|
|
time(&TimeNow);
|
|
tmq->tm_value = TimeNow+DHCPOFFER_TIMEOUT;
|
|
brk_flag++;
|
|
break;
|
|
}
|
|
|
|
loc0_remove_entry((EtherAddr *)rq->bp_chaddr, 0,
|
|
cid_ptr, cid_length,db);
|
|
/* Fall through */
|
|
if(dhcp_msg) {
|
|
free(dhcp_msg);
|
|
dhcp_msg = (char *)0;
|
|
}
|
|
break;
|
|
case OFFERED_NAME:
|
|
default:
|
|
syslog(LOG_ERR,
|
|
"Internal Error: Invalid type returned (%d)",
|
|
typ );
|
|
/* Fall through */
|
|
case NO_DHCP:
|
|
loc0_remove_entry((EtherAddr *)rq->bp_chaddr, 0,
|
|
cid_ptr, cid_length, db);
|
|
dh0_remove_from_dhcp_reqlist(cid_ptr, cid_length);
|
|
dh0_remove_from_timelist(cid_ptr, cid_length);
|
|
brk_flag++;
|
|
break;
|
|
}
|
|
if(brk_flag)
|
|
break;
|
|
}
|
|
/* Client has accepted the offer */
|
|
if(req) {
|
|
script_msgtype = DHCPACK;
|
|
if (debug) {
|
|
ipn.s_addr = req->dh_ipaddr;
|
|
syslog(LOG_DEBUG, "DHCPACK:cid (%s),ip (%s) name (%s)",
|
|
str, inet_ntoa(ipn), req->dh_hsname);
|
|
}
|
|
if (req->dh_configPtr->p_dnsdomain) {
|
|
client_domain = req->dh_configPtr->p_dnsdomain;
|
|
}
|
|
else if (req->dh_configPtr->p_nisdomain){
|
|
client_domain = req->dh_configPtr->p_nisdomain;
|
|
}
|
|
else {
|
|
client_domain = mydomain;
|
|
}
|
|
dh0_upd_dhcp_rq_entry(req, &dhcp_reqParams, dhcp_lease);
|
|
dh0_remove_system_mapping(cid_ptr, (EtherAddr *)rq->bp_chaddr, cid_length, db);
|
|
dh0_create_system_mapping(req->dh_ipaddr,
|
|
(EtherAddr *)rq->bp_chaddr,
|
|
req->dh_hsname,
|
|
client_domain);
|
|
/* compute the lease to offer */
|
|
offered_lease = find_lease_to_offer(req->dh_ipaddr,
|
|
req->dh_reqLease,
|
|
req->dh_configPtr,
|
|
db);
|
|
dh0_encode_dhcp_server_packet(rp->dh_opts,
|
|
DHCPACK, &(req->dh_reqParams),
|
|
offered_lease, dhcp_msg,
|
|
req->dh_hsname, 0,
|
|
req->dh_configPtr, &dci_data);
|
|
|
|
rp->bp_yiaddr.s_addr = (u_long)req->dh_ipaddr;
|
|
#ifdef EDHCP
|
|
/* do dns update and encode client_fqdn */
|
|
if ( (1 == dhcp_dnsupd_on) && (alt_naming_flag == 0) &&
|
|
( (req->dh_reqParams.GET_CLIENT_FQDN == 1) ||
|
|
(1 == dhcp_dnsupd_always) ) ) {
|
|
dhcp_dnsadd(0, req->dh_ipaddr, req->dh_hsname,
|
|
client_domain, req->dh_reqLease,
|
|
dci_data.client_fqdn,
|
|
req->dh_configPtr->ddns_conf);
|
|
dh0_append_client_fqdn(rp->dh_opts, dci_data.client_fqdn);
|
|
}
|
|
#endif
|
|
if(rp->bp_flags & BROADCAST_BIT)
|
|
sendreply(rp, 0, has_sname, 1, DHCP_REPLY);
|
|
else
|
|
sendreply(rp, 0, has_sname, 0, DHCP_REPLY);
|
|
|
|
#ifdef EDHCP
|
|
/* do dns update and encode client_fqdn */
|
|
if ( (1 == dhcp_dnsupd_on) && (dhcp_dnsupd_beforeACK == 0) &&
|
|
(0 == alt_naming_flag) &&
|
|
( (req->dh_reqParams.GET_CLIENT_FQDN == 1) ||
|
|
(1 == dhcp_dnsupd_always) ) ) {
|
|
dhcp_dnsadd(1, req->dh_ipaddr, req->dh_hsname,
|
|
client_domain, req->dh_reqLease,
|
|
dci_data.client_fqdn,
|
|
req->dh_configPtr->ddns_conf);
|
|
}
|
|
#endif
|
|
|
|
if (client_domain) {
|
|
sprintf(hostname1,"%s.%s", req->dh_hsname,
|
|
client_domain);
|
|
} else {
|
|
strcpy(hostname1, req->dh_hsname);
|
|
}
|
|
loc0_create_update_entry(cid_ptr, cid_length,
|
|
(EtherAddr *)rq->bp_chaddr,
|
|
req->dh_ipaddr,
|
|
hostname1, msg_recv_time,
|
|
offered_lease,
|
|
db);
|
|
dh0_remove_from_timelist(cid_ptr, cid_length);
|
|
dh0_remove_from_dhcp_reqlist(cid_ptr,cid_length);
|
|
}
|
|
}
|
|
else if(dhcp_server == 0) {
|
|
/* Client is renewing or rebinding the lease, OR
|
|
* it is from a client verifying its IP address
|
|
* in the case of INIT-REBOOT.
|
|
*/
|
|
if( (dhcp_ipaddr) && (rq->bp_ciaddr.s_addr == 0) ) {
|
|
if (debug) {
|
|
ipn.s_addr = dhcp_ipaddr;
|
|
syslog(LOG_DEBUG,
|
|
"DHCPREQUEST: cid(%s) Init-Reboot from client (%s)",
|
|
str, inet_ntoa(ipn));
|
|
}
|
|
/* INIT-REBOOT State */
|
|
mltaddr =
|
|
MULTIHMDHCP ? np_recv->myaddr.s_addr : myhostaddr.s_addr;
|
|
netaddr =
|
|
rq->bp_giaddr.s_addr ? rq->bp_giaddr.s_addr : mltaddr;
|
|
|
|
if((aval = cf0_is_req_addr_valid(dhcp_ipaddr, netaddr)) == 0) {
|
|
rval = loc0_is_ipaddress_for_cid((EtherAddr *)rq->
|
|
bp_chaddr,dhcp_ipaddr,
|
|
cid_ptr, cid_length, db);
|
|
if(rval == 0) {
|
|
newConfigPtr = cf0_get_config(dhcp_ipaddr, 1);
|
|
if(newConfigPtr == 0)
|
|
return 1;
|
|
offered_lease = find_lease_to_offer(dhcp_ipaddr,
|
|
dhcp_lease,
|
|
newConfigPtr,
|
|
db);
|
|
dh0_encode_dhcp_server_packet(rp->dh_opts,
|
|
DHCPACK, &dhcp_reqParams,
|
|
offered_lease, dhcp_msg,
|
|
0, 0, newConfigPtr,
|
|
&dci_data);
|
|
rp->bp_yiaddr.s_addr = dhcp_ipaddr;
|
|
if (loc0_update_lease((EtherAddr *)rq->bp_chaddr,
|
|
cid_ptr, cid_length,
|
|
msg_recv_time,
|
|
offered_lease,
|
|
db)!=0){
|
|
/* there was a DHCPNAK here that was unlikely
|
|
* to take place. Instead just place a error
|
|
* message and remain silent */
|
|
ipn.s_addr = dhcp_ipaddr;
|
|
syslog(LOG_ERR,
|
|
"Init-Reboot from client (%s)"
|
|
"- Lease update error.",
|
|
inet_ntoa(ipn));
|
|
return 0;
|
|
}
|
|
else if (debug)
|
|
syslog(LOG_DEBUG,
|
|
"DHCPACK to Init-Reboot from client (%s)",
|
|
inet_ntoa(rp->bp_yiaddr));
|
|
}
|
|
else if(rval == 2) { /* Incorrect mapping */
|
|
/* we should NAK only if this ipaddress is
|
|
* assigned to someone else otherwise keep quiet
|
|
*/
|
|
char tmp_cid[MAXCIDLEN];
|
|
int tmp_cidlen;
|
|
if (getCidByIpAddr(tmp_cid, &tmp_cidlen,
|
|
&dhcp_ipaddr, db)) {
|
|
dh0_encode_dhcp_server_packet(rp->dh_opts,
|
|
DHCPNAK, 0, 0,
|
|
INCORRECT_MAPPING_MSG,
|
|
0, 0, 0, &dci_data);
|
|
script_msgtype = DHCPNAK;
|
|
if (debug) {
|
|
ipn.s_addr = dhcp_ipaddr;
|
|
syslog(LOG_DEBUG,
|
|
"DHCPNAK to Init-Reboot from client (%s) - Incorrect Mapping.",
|
|
inet_ntoa(ipn));
|
|
}
|
|
/* rp->bp_yiaddr.s_addr = dhcp_ipaddr; */
|
|
}
|
|
else {
|
|
if (debug) {
|
|
ipn.s_addr = dhcp_ipaddr;
|
|
syslog(LOG_DEBUG,
|
|
"No reply to Init-Reboot from client (%s).",
|
|
inet_ntoa(ipn));
|
|
}
|
|
return 0; /* remain silent
|
|
* we don't have a binding for this ip
|
|
* although there is a binding for
|
|
* this cid */
|
|
}
|
|
}
|
|
else {
|
|
return 0; /* Remain Silent */
|
|
}
|
|
}
|
|
else { /* Address is invalid: on wrong subnet */
|
|
if (aval != 2) {
|
|
dh0_encode_dhcp_server_packet(rp->dh_opts,
|
|
DHCPNAK, 0, 0,
|
|
WRONG_SUBNET_MSG,
|
|
0, 0, 0, &dci_data);
|
|
if (debug) {
|
|
ipn.s_addr = dhcp_ipaddr;
|
|
syslog(LOG_DEBUG,
|
|
"DHCPNAK to Init-Reboot from client (%s) - Incorrect Subnet.",
|
|
inet_ntoa(ipn));
|
|
}
|
|
script_msgtype = DHCPNAK;
|
|
}
|
|
else {
|
|
if (debug) {
|
|
ipn.s_addr = dhcp_ipaddr;
|
|
syslog(LOG_DEBUG, "No Reply to Init-Reboot from client (%s).",
|
|
inet_ntoa(ipn));
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
else if( (dhcp_ipaddr == 0) && (rq->bp_ciaddr.s_addr) ) {
|
|
/* Lease Renewal/Rebind message from the client */
|
|
if (debug)
|
|
syslog(LOG_DEBUG,
|
|
"DHCPREQUEST: Renew/Rebind cid (%s) ciaddr (%s)",
|
|
str, inet_ntoa(rq->bp_ciaddr));
|
|
rp->bp_yiaddr.s_addr = rq->bp_ciaddr.s_addr;
|
|
|
|
if ((rval=loc0_is_ipaddress_for_cid((EtherAddr *)rq->
|
|
bp_chaddr,
|
|
rq->bp_ciaddr.s_addr,
|
|
cid_ptr, cid_length,
|
|
db)==0)) {
|
|
newConfigPtr = cf0_get_config(rq->bp_ciaddr.s_addr, 1);
|
|
if(newConfigPtr == 0)
|
|
return 1;
|
|
if ((aval = cf0_is_req_addr_valid(rq->bp_ciaddr.s_addr,
|
|
newConfigPtr->p_netnum)) != 0) {
|
|
/* the old address valid check with giaddr
|
|
would not work but this one will work */
|
|
dh0_encode_dhcp_server_packet(rp->dh_opts,
|
|
DHCPNAK, 0, 0,
|
|
INCORRECT_MAPPING_MSG,
|
|
0, 0, 0, &dci_data);
|
|
script_msgtype = DHCPNAK;
|
|
if (debug) {
|
|
syslog(LOG_DEBUG,
|
|
"DHCPNAK to Renew/Rebind from client (%s)"
|
|
"- Incorrect Range or Subnet.",
|
|
inet_ntoa(rq->bp_ciaddr));
|
|
}
|
|
}
|
|
else {
|
|
offered_lease =
|
|
find_lease_to_offer(rq->bp_ciaddr.s_addr,
|
|
dhcp_lease,
|
|
newConfigPtr,
|
|
db);
|
|
dh0_encode_dhcp_server_packet(rp->dh_opts,
|
|
DHCPACK,
|
|
&dhcp_reqParams,
|
|
offered_lease,
|
|
dhcp_msg,
|
|
0, 0,
|
|
newConfigPtr,
|
|
&dci_data);
|
|
if (loc0_update_lease((EtherAddr *)rq->bp_chaddr,cid_ptr, cid_length,
|
|
msg_recv_time,
|
|
offered_lease,
|
|
db) != 0) {
|
|
/* there was a DHCPNAK coded here that was unlikely
|
|
* to take place. Instead just place a debug
|
|
* message and remain silent */
|
|
|
|
syslog(LOG_ERR,
|
|
"Renew/Rebind from client (%s)"
|
|
"- Lease Update Error.",
|
|
inet_ntoa(rp->bp_yiaddr));
|
|
}
|
|
else if (debug)
|
|
syslog(LOG_DEBUG,
|
|
"DHCPACK to Renew/Rebind from client (%s)",
|
|
inet_ntoa(rp->bp_yiaddr));
|
|
}
|
|
}
|
|
else {
|
|
/* I am not the orignal server who assigned this lease but
|
|
* need to check if I can give the requested ip address
|
|
* a new lease
|
|
* NAK only in case we have a different mapping - else
|
|
* if no mapping be silent
|
|
*/
|
|
if (rval == 2) {
|
|
char tmp_cid[MAXCIDLEN];
|
|
int tmp_cidlen;
|
|
if (getCidByIpAddr(tmp_cid, &tmp_cidlen,
|
|
(ulong*)&rq->bp_ciaddr.s_addr, db)) {
|
|
dh0_encode_dhcp_server_packet(rp->dh_opts,
|
|
DHCPNAK, 0, 0,
|
|
WRONG_SERVER_MSG,
|
|
0, 0, 0, &dci_data);
|
|
script_msgtype = DHCPNAK;
|
|
if (debug) {
|
|
syslog(LOG_DEBUG,
|
|
"DHCPNAK to Renew/Rebind from client (%s)"
|
|
"- Incorrect Server.",
|
|
inet_ntoa(rq->bp_ciaddr));
|
|
}
|
|
}
|
|
else {
|
|
if (debug) {
|
|
syslog(LOG_DEBUG,
|
|
"No Reply to Renew/Rebind from client (%s)",
|
|
inet_ntoa(rq->bp_ciaddr));
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
else
|
|
return 1;
|
|
}
|
|
} /* CHANGE added else when could not understand message -
|
|
earlier the received message was just sent back */
|
|
else { /* remain quiet */
|
|
return 0;
|
|
}
|
|
if(rp->bp_flags & BROADCAST_BIT)
|
|
sendreply(rp, 0, has_sname, 1, DHCP_REPLY);
|
|
else
|
|
sendreply(rp, 0, has_sname, 0, DHCP_REPLY);
|
|
}
|
|
else if(dhcp_server != 0) {
|
|
/* Client has selected someone else */
|
|
dh0_remove_from_timelist(cid_ptr, cid_length);
|
|
dh0_remove_from_dhcp_reqlist(cid_ptr, cid_length);
|
|
loc0_remove_entry((EtherAddr *)rq->bp_chaddr, 0, cid_ptr,
|
|
cid_length, db);
|
|
}
|
|
break;
|
|
case DHCPDECLINE:
|
|
{
|
|
iaddr_t ipn, ipn1;
|
|
char tmp_cid[MAXCIDLEN];
|
|
u_long res_node;
|
|
char tmp_ipn[64];
|
|
|
|
ipn.s_addr = loc0_get_ipaddr_from_cid((EtherAddr *)rq->bp_chaddr,
|
|
cid_ptr, cid_length, db);
|
|
if ( ipn.s_addr != dhcp_ipaddr ) {
|
|
ipn1.s_addr = dhcp_ipaddr;
|
|
strcpy(tmp_ipn, inet_ntoa(ipn1));
|
|
syslog(LOG_DEBUG,
|
|
"DHCPDECLINE: MISMATCH!Req IP is %s and IP in db is %s",
|
|
tmp_ipn, inet_ntoa(ipn));
|
|
/* ipn = Zero means no record in the database */
|
|
}
|
|
syslog(LOG_WARNING,
|
|
"DHCPDECLINE: cid (%s) marking stolen %s",str,
|
|
dhcp_ipaddr);
|
|
/* instead of removing the lease mark the address as stolen
|
|
* so that it is not given out again */
|
|
/* the original record for this ipaddress must be removed */
|
|
loc0_remove_entry((EtherAddr *)rq->bp_chaddr, 3,
|
|
cid_ptr, cid_length, db);
|
|
res_node = dhcp_ipaddr;
|
|
sprintf(tmp_cid, "STOLEN-%d", ++msg_recv_time);
|
|
loc0_create_update_entry(tmp_cid, strlen(tmp_cid),
|
|
(EtherAddr*)&res_node, res_node,
|
|
tmp_cid, 0, STOLEN_LEASE, db);
|
|
script_msgtype = DHCPDECLINE;
|
|
}
|
|
break;
|
|
case DHCPRELEASE:
|
|
if (debug)
|
|
syslog(LOG_DEBUG, "DHCPRELEASE: cid (%s)", str);
|
|
script_msgtype = DHCPRELEASE;
|
|
#ifdef EDHCP
|
|
if ( (dhcp_dnsupd_on == 1) && (0 == alt_naming_flag) ) {
|
|
/* this should check if the
|
|
record was added before deleting */
|
|
u_long addrfromCid;
|
|
dhcpConfig *cfgPtr;
|
|
|
|
addrfromCid = loc0_get_ipaddr_from_cid((EtherAddr *)rq->bp_chaddr,
|
|
cid_ptr, cid_length, db);
|
|
if (addrfromCid != 0) {
|
|
cfgPtr = cf0_get_config(addrfromCid, 0);
|
|
dhcp_dnsdel(addrfromCid, cfgPtr->ddns_conf);
|
|
}
|
|
}
|
|
#endif
|
|
dh0_remove_system_mapping(cid_ptr, (EtherAddr *)rq->bp_chaddr, cid_length, db);
|
|
/*
|
|
* Instead of removing the record just set the lease to expire
|
|
* some clients do an init-reboot after release (ISC clients)
|
|
*/
|
|
loc0_update_lease((EtherAddr *)rq->bp_chaddr, cid_ptr, cid_length,
|
|
msg_recv_time, 0, db);
|
|
break;
|
|
|
|
case DHCPINFORM:
|
|
if (debug)
|
|
syslog(LOG_DEBUG, "DHCPINFORM: cid (%s)", str);
|
|
/* respond with a DHCPACK as if a DHCPDISCOVER was received
|
|
* except don't send a lease and address
|
|
*/
|
|
if(rq->bp_ciaddr.s_addr != 0) {
|
|
dhcp_hsname =
|
|
loc0_get_hostname_from_cid(cid_ptr, cid_length,
|
|
(EtherAddr *)rq->bp_chaddr, db);
|
|
dhcp_hsname = strtok(dhcp_hsname, " .");/*send only host part*/
|
|
|
|
if(dhcp_hsname == (char *)0) {
|
|
return 1;
|
|
}
|
|
newConfigPtr = cf0_get_config(rq->bp_ciaddr.s_addr, 1);
|
|
if(newConfigPtr == 0)
|
|
return 1;
|
|
/* note that we are passing a negative value for lease time
|
|
* to inform the routine not to fill up lease time
|
|
*/
|
|
if(newConfigPtr->p_choose_name)
|
|
dh0_encode_dhcp_server_packet(rp->dh_opts,
|
|
DHCPACK, &dhcp_reqParams, 0,
|
|
dhcp_msg, dhcp_hsname,
|
|
dhcp_hsname, newConfigPtr,
|
|
&dci_data);
|
|
else
|
|
dh0_encode_dhcp_server_packet(rp->dh_opts,
|
|
DHCPACK, &dhcp_reqParams, 0,
|
|
dhcp_msg, dhcp_hsname, 0,
|
|
newConfigPtr, &dci_data);
|
|
/* should we create system_mapping */
|
|
if(rp->bp_flags & BROADCAST_BIT)
|
|
sendreply(rp, 0, has_sname, 1, DHCP_REPLY);
|
|
else
|
|
sendreply(rp, 0, has_sname, 0, DHCP_REPLY);
|
|
}
|
|
break;
|
|
/* this is not part of the standard yet but this functionality has
|
|
* been added to support polling of the server to verify whether the
|
|
* server is running and serving addresses
|
|
*/
|
|
case DHCPPOLL:
|
|
if (debug)
|
|
syslog(LOG_DEBUG, "DHCPPOLL: cid (%s)", str);
|
|
rp->bp_yiaddr.s_addr = rq->bp_ciaddr.s_addr;
|
|
|
|
dh0_encode_dhcp_server_packet(rp->dh_opts,
|
|
DHCPPRPL, &dhcp_reqParams,
|
|
0, 0,
|
|
0, 0, 0, &dci_data);
|
|
if(rp->bp_flags & BROADCAST_BIT)
|
|
sendreply(rp, 0, has_sname, 1, DHCP_REPLY);
|
|
else
|
|
sendreply(rp, 0, has_sname, 0, DHCP_REPLY);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (script_file_to_run && (script_state_change_index > 0)) {
|
|
execute_script();
|
|
}
|
|
if(dhcp_hsname) {
|
|
free(dhcp_hsname);
|
|
dhcp_hsname = (char *)0;
|
|
}
|
|
if (resolv_name)
|
|
free(resolv_name);
|
|
free(cid_ptr);/*CHECK*/
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Perform Reverse ARP lookup using /etc/ether and /etc/hosts (or
|
|
* their NIS equivalents)
|
|
*
|
|
* Returns 1 on success, 0 on failure.
|
|
*/
|
|
int
|
|
reverse_arp(struct ether_addr *eap, iaddr_t *iap)
|
|
{
|
|
register struct hostent *hp;
|
|
char host[512];
|
|
|
|
/*
|
|
* Call routine to access /etc/ethers or its NIS equivalent
|
|
*/
|
|
if (ether_ntohost(host, eap))
|
|
return (0);
|
|
|
|
/*
|
|
* Now access the hostname database.
|
|
*/
|
|
if ((hp = gethostbyname(host)) == 0) {
|
|
syslog(LOG_ERR, "gethostbyname(%s) failed: %s", host, hstrerror(h_errno));
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Return primary Internet address
|
|
*/
|
|
iap->s_addr = *(u_long *)(hp->h_addr_list[0]);
|
|
return (1);
|
|
}
|
|
|
|
char hostmap[] = "hosts.byname";
|
|
/*
|
|
* A new diskless workstation/autoreg is asking for
|
|
* initial boot/IPADDR
|
|
*/
|
|
int
|
|
handle_diskless(struct bootp *rq, struct bootp *rp)
|
|
{
|
|
char *t_host, *domain, *ypmaster, *str_match, *t_domain;
|
|
char hostname[256], alias[256];
|
|
int err = 0;
|
|
|
|
if (yp_get_default_domain(&domain)) {
|
|
syslog(LOG_ERR, "Autoreg: can't get domain");
|
|
return(-1);
|
|
}
|
|
|
|
t_host = (char *)rq->vd_clntname;
|
|
if (debug)
|
|
syslog(LOG_DEBUG, "Autoreg: domain=%s, host=%s", domain, t_host);
|
|
|
|
gethostname(hostname, sizeof(hostname));
|
|
if (gethostbyname(t_host) != NULL) {
|
|
/* play safe */
|
|
rp->vd_flags = VF_RET_IPADDR;
|
|
return(0);
|
|
}
|
|
|
|
if (!reg_netmask) {
|
|
if (str_match = strrchr(reg_hostnet, '.'))
|
|
*str_match = 0;
|
|
}
|
|
|
|
/* get ypmaster ipaddr */
|
|
if (yp_master(domain, hostmap, &ypmaster)) {
|
|
if (debug)
|
|
syslog(LOG_DEBUG, "no \"%s\" YPmaster found for \"%s\" domain",
|
|
hostmap, domain);
|
|
return(-1);
|
|
}
|
|
|
|
/* check and prevent broadcast storm */
|
|
if (!matchhost(ypmaster)) {
|
|
if (!forwarding) {
|
|
if (debug)
|
|
syslog(LOG_DEBUG, "Autoreg: I am neither YPMASTER nor GATEWAY");
|
|
return(-1);
|
|
}
|
|
if (debug)
|
|
syslog(LOG_DEBUG, "Autoreg: I am GATEWAY");
|
|
/* XXX
|
|
* finding out if the requestor and YPMASTER are
|
|
* at same sub-net is impossible. So let's
|
|
* allow gateway issue an extra broadcast msg.
|
|
*/
|
|
} else if (debug)
|
|
syslog(LOG_DEBUG, "Autoreg: I am YPMASTER");
|
|
|
|
/* check/create full host name */
|
|
if ((t_domain = strchr((const char *)rq->vd_clntname, '.')) != NULL) {
|
|
/* if it not for the domain i belong, then ignore */
|
|
if (strcmp(domain, t_domain+1)) {
|
|
if (debug)
|
|
syslog( LOG_DEBUG, "Autoreg: req_domain(%s)<>domain(%s)",
|
|
t_domain+1, domain);
|
|
return(-1);
|
|
}
|
|
strcpy(hostname, t_host);
|
|
*t_domain = 0;
|
|
strcpy(alias, t_host);
|
|
*t_domain = '.';
|
|
} else {
|
|
strcpy(hostname, t_host);
|
|
strcat(hostname, ".");
|
|
strcat(hostname, domain);
|
|
strcpy(alias, t_host);
|
|
}
|
|
|
|
/* can't create, other bootp may be creating at the same time
|
|
or, there is no NIS */
|
|
err = registerinethost(hostname,reg_hostnet,reg_netmask,0,alias);
|
|
if ((u_long)err <= YPERR_BUSY) {
|
|
syslog(LOG_ERR, "Autoreg: REGISTER failed(0x%x)", err);
|
|
return(-1);
|
|
}
|
|
|
|
rp->vd_flags = VF_NEW_IPADDR;
|
|
rp->bp_yiaddr.s_addr = (u_long)(err);
|
|
|
|
if (debug)
|
|
syslog( LOG_DEBUG, "Autoreg: %s REGISTERED as %s",
|
|
hostname, inet_ntoa(*(struct in_addr *)&err));
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* Process BOOTREQUEST packet.
|
|
*
|
|
* <SGI CHANGES>
|
|
*
|
|
* Our version does implement the hostname processing
|
|
* specified in RFC 951. If the client specifies a hostname, then
|
|
* only that hostname will respond. We do one additional thing
|
|
* that the RFC does not specify: if the server name is not specified,
|
|
* then we fill it in, so that the client knows the name as well
|
|
* as the IP address of the server who responded.
|
|
*
|
|
* Our version also implements the forwarding algorithm specified
|
|
* in RFC 951, but not used in the Stanford version of this code.
|
|
* If a request is received that specifies a host other than this
|
|
* one, check the network address of that host and forward the
|
|
* request to him if he lives on a different wire than the one
|
|
* from which the request was received.
|
|
*
|
|
* Another change from the RFC and the original version of the
|
|
* code is that the BOOTP server will respond to the client even
|
|
* if the client is not listed in the BOOTP configuration file,
|
|
* provided that the client already knows his IP address. The
|
|
* reason for this change is to supply a bit more ease of use.
|
|
* If there is a file out there that you want to boot, it seems
|
|
* a shame to have to edit bootptab on the server before
|
|
* you can boot the lousy file. All the more so because if you
|
|
* have already configured the IP address of your system, then
|
|
* the bootptab information is redundant. (This will make the
|
|
* transition from XNS network boot to IP network boot a little
|
|
* less painful).
|
|
*
|
|
* <END SGI CHANGES>
|
|
*/
|
|
void
|
|
request(struct bootp *rq)
|
|
{
|
|
/* register struct bootp *rq = (struct bootp *)buf; */
|
|
struct bootp rp;
|
|
char path[MAXPATHLEN], file[128];
|
|
iaddr_t fromaddr;
|
|
register struct hosts *hp;
|
|
register struct hostent *hostentp;
|
|
register n;
|
|
int has_sname; /* does rq have nonempty bp_sname? */
|
|
|
|
if(sleepmode) /* allow debugging */
|
|
sleep(10);
|
|
rp = *rq; /* copy request into reply */
|
|
rp.bp_op = BOOTREPLY;
|
|
|
|
/* Let's first check if it is a DHCP message */
|
|
if(rq->dh_magic == VM_DHCP) {
|
|
if(dh0_get_dhcp_msg_type(rq->dh_opts) == -1) {
|
|
/* This is a RFC1533 Only message */
|
|
process_rfc1533_bootp_message(&rp);
|
|
/* Fall thru to process the bootp stuff */
|
|
}
|
|
else {
|
|
/* This is a DHCP Message */
|
|
if(ProclaimServer) {
|
|
process_dhcp_message(rq, &rp);
|
|
return;
|
|
}
|
|
else {
|
|
if (debug)
|
|
syslog(LOG_DEBUG,
|
|
"DHCP request from client (%s). DHCP server not enabled.",
|
|
ether_ntoa((struct ether_addr *)rq->bp_chaddr));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* prevent possible buffer overrun */
|
|
rq->bp_file[sizeof(rq->bp_file)-1] = '\0';
|
|
|
|
/* This is a non dhcp, regular bootp packet */
|
|
/*
|
|
* Let's resolve client's ipaddr first.
|
|
*/
|
|
if (rq->bp_yiaddr.s_addr != 0 && rq->bp_giaddr.s_addr != 0) {
|
|
/*
|
|
* yiaddr has already been filled in by forwarding bootp
|
|
*/
|
|
hp = (struct hosts *) 0;
|
|
} else if (rq->bp_ciaddr.s_addr == 0) {
|
|
/*
|
|
* client doesnt know his IP address,
|
|
* search by hardware address.
|
|
*/
|
|
hp = lookup_btabm(rq->bp_htype, rq->bp_chaddr);
|
|
if (hp == (struct hosts *)0) {
|
|
/*
|
|
* The requestor isn't listed in bootptab.
|
|
*/
|
|
if( (rp.vd_magic == VM_SGI) && (rp.vd_flags == VF_GET_IPADDR) ) {
|
|
if((hostentp = gethostbyname((const char *)rp.vd_clntname)) == 0) {
|
|
rp.bp_yiaddr.s_addr = 0;
|
|
} else {
|
|
rp.bp_yiaddr.s_addr= *(u_long *)(hostentp->h_addr_list[0]);
|
|
rp.vd_flags = VF_RET_IPADDR;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Try Reverse ARP using /etc/ethers or NIS before
|
|
* giving up.
|
|
*/
|
|
if (!reverse_arp((struct ether_addr *)rq->bp_chaddr, &rp.bp_yiaddr)) {
|
|
/*
|
|
* Don't trash the request at this point,
|
|
* since it may be for another server with
|
|
* better tables than we have.
|
|
*/
|
|
if (debug)
|
|
syslog(LOG_DEBUG,
|
|
"No IP address for %s found in /etc/ethers or ethers map, can't reply",
|
|
ether_ntoa((struct ether_addr *)rq->bp_chaddr));
|
|
/* Play it safe */
|
|
rp.bp_yiaddr.s_addr = 0;
|
|
}
|
|
} else {
|
|
rp.bp_yiaddr = hp->iaddr;
|
|
}
|
|
} else {
|
|
/* search by IP address */
|
|
hp = lookup_btabi(rq->bp_ciaddr);
|
|
}
|
|
|
|
fromaddr = rq->bp_ciaddr.s_addr ? rq->bp_ciaddr : rp.bp_yiaddr;
|
|
|
|
/*
|
|
* Check whether the requestor specified a particular server.
|
|
* If not, fill in the name of the current host. If a
|
|
* particular server was requested, then don't answer the
|
|
* request unless the name matches our name or one of our
|
|
* aliases.
|
|
*/
|
|
has_sname = rq->bp_sname[0] != '\0';
|
|
if (!has_sname)
|
|
strncpy((char *)rp.bp_sname, myname, sizeof(rp.bp_sname));
|
|
else if (!matchhost((char *)rq->bp_sname)) {
|
|
iaddr_t destaddr;
|
|
|
|
if (debug)
|
|
syslog(LOG_DEBUG, "%s: request for server %s",
|
|
iaddr_ntoa(fromaddr), rq->bp_sname);
|
|
/*
|
|
* Not for us.
|
|
*/
|
|
if (!forwarding)
|
|
return;
|
|
/*
|
|
* Look up the host by name and decide whether
|
|
* we should forward the message to him.
|
|
*/
|
|
if ((hostentp = gethostbyname((const char *)rq->bp_sname)) == 0) {
|
|
syslog(LOG_INFO, "%s: request for unknown server %s",
|
|
iaddr_ntoa(fromaddr), rq->bp_sname);
|
|
return;
|
|
}
|
|
destaddr.s_addr = *(u_long *)(hostentp->h_addr_list[0]);
|
|
/*
|
|
* If the other server is on a different cable from the
|
|
* requestor, then forward the request. If on the same
|
|
* wire, there is no point in forwarding. Note that in
|
|
* the case that we don't yet know the IP address of the
|
|
* client, there is no way to tell whether the client and
|
|
* server are actually on the same wire. In that case
|
|
* we forward regardless. It's redundant, but there's
|
|
* no way to get around it.
|
|
*/
|
|
if (!samewire(&fromaddr, &destaddr)) {
|
|
/*
|
|
* If we were able to compute the client's Internet
|
|
* address, pass that information along in case the
|
|
* other server doesn't have the info.
|
|
*/
|
|
rq->bp_yiaddr = rp.bp_yiaddr;
|
|
forward(rq, &fromaddr, &destaddr);
|
|
}
|
|
return;
|
|
}
|
|
if ( (fromaddr.s_addr == 0) && (rq->vd_magic == VM_AUTOREG) &&
|
|
rq->vd_clntname && rq->vd_clntname[0] ) {
|
|
if (debug)
|
|
syslog(LOG_DEBUG, "Autoreg starts");
|
|
if (handle_diskless(rq, &rp))
|
|
return;
|
|
fromaddr = rp.bp_yiaddr;
|
|
}
|
|
/*
|
|
* If we get here and the 'from' address is still zero, that
|
|
* means we don't recognize the client and we can't pass the buck.
|
|
* So we have to give up.
|
|
*/
|
|
if (fromaddr.s_addr == 0)
|
|
return;
|
|
|
|
if (rq->bp_file[0] == 0) {
|
|
/* client didnt specify file */
|
|
if (hp == (struct hosts *)0 || hp->bootfile[0] == 0)
|
|
strcpy(file, defaultboot);
|
|
else
|
|
strcpy(file, hp->bootfile);
|
|
} else {
|
|
/* client did specify file */
|
|
strcpy(file, (const char *)rq->bp_file);
|
|
}
|
|
if (file[0] == '/') /* if absolute pathname */
|
|
strcpy(path, file);
|
|
else { /* else look in boot directory */
|
|
strcpy(path, homedir);
|
|
strcat(path, "/");
|
|
strcat(path, file);
|
|
}
|
|
/* try first to find the file with a ".host" suffix */
|
|
n = strlen(path);
|
|
if (hp != (struct hosts *)0 && hp->host[0] != 0) {
|
|
strcat(path, ".");
|
|
strcat(path, hp->host);
|
|
}
|
|
if (access(path, R_OK) < 0) {
|
|
path[n] = 0; /* try it without the suffix */
|
|
if (access(path, R_OK) < 0) {
|
|
/*
|
|
* We don't have the file. Don't respond unless
|
|
* the client asked for us by name, in case some
|
|
* other server does have the file. If he asked
|
|
* for us by name, send him back a null pathname
|
|
* so that he knows we don't have his boot file.
|
|
*/
|
|
if (rq->bp_sname[0] == 0) {
|
|
if (debug) /* Bug #767501 */
|
|
syslog(LOG_DEBUG,
|
|
"%s requested boot file %s: access failed (%m)",
|
|
iaddr_ntoa(fromaddr), path);
|
|
|
|
return; /* didnt ask for us */
|
|
}
|
|
syslog(LOG_ERR,
|
|
"%s requested boot file %s: access failed (%m)",
|
|
iaddr_ntoa(fromaddr), path);
|
|
path[0] = '\0';
|
|
}
|
|
}
|
|
if (path[0] != '\0') {
|
|
if (strlen(path) > sizeof(rp.bp_file)-1) {
|
|
syslog(LOG_ERR, "%s: reply boot file name %s too long",
|
|
iaddr_ntoa(fromaddr), path);
|
|
path[0] = '\0';
|
|
} else
|
|
syslog(LOG_INFO, "reply to %s: boot file %s",
|
|
iaddr_ntoa(fromaddr), path);
|
|
}
|
|
strcpy((char *)rp.bp_file, path);
|
|
sendreply(&rp, 0, has_sname, 0, BOOTSTRAP_REPLY);
|
|
}
|
|
|
|
/*
|
|
* Select the address of the interface on this server that is
|
|
* on the same net as the 'from' address.
|
|
*
|
|
* Return value
|
|
* TRUE if match
|
|
* FALSE if no match
|
|
*/
|
|
int
|
|
bestaddr(iaddr_t *from, iaddr_t *answ)
|
|
{
|
|
register struct netaddr *np;
|
|
int match = 0;
|
|
int i;
|
|
|
|
if (from->s_addr == 0) {
|
|
answ->s_addr = myhostaddr.s_addr;
|
|
} else {
|
|
/*
|
|
* Search the table of nets to which the server is connected
|
|
* for the net containing the source address.
|
|
*/
|
|
for (np = nets; np->netmask != 0; np++)
|
|
if ((from->s_addr & np->netmask) == np->net) {
|
|
answ->s_addr = np->myaddr.s_addr;
|
|
match = 1;
|
|
break;
|
|
}
|
|
else {
|
|
if ((np->first_alias != 0) && (np->last_alias != 0)) {
|
|
for (i = np->first_alias; i <= np->last_alias; i++) {
|
|
if ((from->s_addr & np->netmask) ==
|
|
(myaddrs[i] & np->netmask)) {
|
|
answ->s_addr = np->myaddr.s_addr; /* ?? alias ?? */
|
|
match = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (match == 1)
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If no match in table, default to our 'primary' address
|
|
*/
|
|
if (np->netmask == 0)
|
|
answ->s_addr = myhostaddr.s_addr;
|
|
}
|
|
return match;
|
|
}
|
|
|
|
|
|
/*
|
|
* Process BOOTREPLY packet (something is using us as a gateway).
|
|
*/
|
|
void
|
|
reply(struct bootp *bp)
|
|
{
|
|
/* struct bootp *bp = (struct bootp *)buf; */
|
|
iaddr_t dst, gate;
|
|
|
|
dst = bp->bp_yiaddr.s_addr ? bp->bp_yiaddr : bp->bp_ciaddr;
|
|
|
|
if (debug)
|
|
syslog(LOG_DEBUG, "forwarding BOOTP reply from %s to %s",
|
|
bp->bp_sname, iaddr_ntoa(dst));
|
|
|
|
/*
|
|
* Try to compute a better giaddr in case we didn't know the
|
|
* client's IP address when we forwarded the original request.
|
|
* The remote server would not be returning the request unless
|
|
* it contained the client's Internet address, so this time
|
|
* we should get the right answer.
|
|
*/
|
|
if (bestaddr(&dst, &gate))
|
|
bp->bp_giaddr = gate;
|
|
else
|
|
syslog(LOG_ERR, "reply: can't find net for %s", iaddr_ntoa(dst));
|
|
|
|
sendreply(bp, 1, 0, 0, BOOTSTRAP_REPLY);
|
|
}
|
|
|
|
/*
|
|
* Forward a BOOTREQUEST packet to another server.
|
|
*
|
|
* RFC 951 (7.3) implies no-forward in case
|
|
* bp_ciaddr = 0.
|
|
*/
|
|
void
|
|
forward(struct bootp *bp, iaddr_t *from, iaddr_t *dest)
|
|
{
|
|
struct sockaddr_in to;
|
|
|
|
/*
|
|
* If hop >= 3, just discard(RFC951 says so).
|
|
*/
|
|
if (bp->bp_hops >= 3)
|
|
return;
|
|
bp->bp_hops++;
|
|
|
|
/*
|
|
* If giaddr is 0, then I am the immediate gateway.
|
|
* So, set myaddr for successing forwarders to send
|
|
* reply to me directly.
|
|
*/
|
|
if (!bp->bp_giaddr.s_addr) {
|
|
(void) bestaddr(from, &bp->bp_giaddr);
|
|
}
|
|
|
|
to.sin_family = AF_INET;
|
|
to.sin_port = htons(IPPORT_BOOTPS);
|
|
to.sin_addr.s_addr = dest->s_addr; /* already in network order */
|
|
|
|
if (debug)
|
|
syslog(LOG_DEBUG, "forwarding BOOTP request to %s(%s)",
|
|
bp->bp_sname, iaddr_ntoa(*dest));
|
|
|
|
if (sendto(s, (caddr_t)bp, sizeof *bp, 0, &to, sizeof to) < 0)
|
|
syslog(LOG_ERR, "forwarding to %s failed (%m)",
|
|
iaddr_ntoa(to.sin_addr));
|
|
}
|
|
|
|
/*
|
|
* Send a reply packet to the client. 'forward' flag is set if we are
|
|
* not the originator of this reply packet.
|
|
*/
|
|
|
|
/* The performance metrics calculations are done at 3 different places
|
|
for precision purposes. It could be simply done once at the top but then
|
|
the time calculations wouldn't be precise (9/24/97)
|
|
*/
|
|
|
|
|
|
void
|
|
sendreply(struct bootp *bp, int forward,
|
|
int has_sname, int broadcast_it, int reply_type)
|
|
{
|
|
iaddr_t dst;
|
|
struct sockaddr_in to;
|
|
char *bufp;
|
|
int buflen;
|
|
int itype;
|
|
|
|
to = sin;
|
|
to.sin_port = htons(IPPORT_BOOTPC);
|
|
/*
|
|
* If the client IP address is specified, use that
|
|
* else if gateway IP address is specified, use that
|
|
* else make a temporary arp cache entry for the client's NEW
|
|
* IP/hardware address and use that.
|
|
*/
|
|
if (bp->bp_ciaddr.s_addr) {
|
|
dst = bp->bp_ciaddr;
|
|
if (debug >= 2)
|
|
syslog(LOG_DEBUG, "reply to client (ciaddr) %s", inet_ntoa(dst));
|
|
|
|
} else if (bp->bp_giaddr.s_addr && forward == 0) {
|
|
dst = bp->bp_giaddr;
|
|
to.sin_port = htons(IPPORT_BOOTPS);
|
|
if (debug >= 2)
|
|
syslog(LOG_DEBUG, "reply via gateway (giaddr) %s", inet_ntoa(dst));
|
|
} else {
|
|
dst = bp->bp_yiaddr;
|
|
if (debug >= 2)
|
|
syslog(LOG_DEBUG, "reply that IP address (yiaddr) is %s", inet_ntoa(dst));
|
|
if (dst.s_addr != 0)
|
|
setarp(&dst, bp->bp_chaddr, bp->bp_hlen);
|
|
}
|
|
|
|
if( (forward == 0) && (reply_type == BOOTSTRAP_REPLY) ) {
|
|
/*
|
|
* If we are originating this reply, we
|
|
* need to find our own interface address to
|
|
* put in the bp_siaddr field of the reply.
|
|
* If this server is multi-homed, pick the
|
|
* 'best' interface (the one on the same net
|
|
* as the client).
|
|
*/
|
|
int gotmatch = 0;
|
|
gotmatch = bestaddr(&dst, &bp->bp_siaddr);
|
|
if (bp->bp_giaddr.s_addr == 0) {
|
|
if (gotmatch == 0) {
|
|
syslog(LOG_ERR, "can't reply to %s (unknown net)", inet_ntoa(dst));
|
|
return;
|
|
}
|
|
bp->bp_giaddr.s_addr = bp->bp_siaddr.s_addr;
|
|
} else if (!gotmatch && has_sname) {
|
|
struct hostent *hp;
|
|
|
|
/*
|
|
* Use the address corresponding to the name
|
|
* specified by the client.
|
|
*/
|
|
if ((hp = gethostbyname((const char *)bp->bp_sname))) {
|
|
bp->bp_siaddr = *(struct in_addr *)hp->h_addr;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!(bp->bp_giaddr.s_addr && (forward == 0))) {
|
|
if(ProclaimServer && (broadcast_it || (dst.s_addr == 0))) {
|
|
if(rsockout_err)
|
|
return;
|
|
if(MULTIHMDHCP) {
|
|
np_send =
|
|
forward ? sr_match_address(bp->bp_giaddr.s_addr): np_recv;
|
|
if(np_send) {
|
|
itype = sr_get_interface_type(np_send->ifname);
|
|
if(itype == TYPE_UNSUPPORT) {
|
|
syslog(LOG_ERR, "Unsupported interface `%s`",
|
|
np_send->ifname);
|
|
return;
|
|
}
|
|
sr_create_ip_send_packet(&bufp, &buflen, bp, itype);
|
|
#ifdef PERFORMANCE
|
|
if (perf_flag == DHCPDISCOVER){
|
|
discover_ctr ++;
|
|
gettimeofday(&perf_time_end, &perf_tz);
|
|
discover_time += (perf_time_end.tv_usec - perf_time_start.tv_usec) + (MIL * (perf_time_end.tv_sec - perf_time_start.tv_sec));
|
|
if (!(discover_ctr % 100))
|
|
syslog(LOG_DEBUG,"The average time to process %lu DISCOVER messages = %lu usec.",discover_ctr, discover_time/discover_ctr);
|
|
}
|
|
else if(perf_flag == DHCPREQUEST){
|
|
request_ctr ++;
|
|
gettimeofday(&perf_time_end, &perf_tz);
|
|
request_time += (perf_time_end.tv_usec - perf_time_start.tv_usec) + (MIL * (perf_time_end.tv_sec - perf_time_start.tv_sec));
|
|
if (!(request_ctr % 100))
|
|
syslog(LOG_DEBUG,"The average time to process %lu REQUESTS = %lu usec",request_ctr, request_time/request_ctr);
|
|
}
|
|
#endif
|
|
|
|
if ( (sendto(np_send->rsockout, bufp, buflen, 0,
|
|
&braw_addr, sizeof (SCIN))) != buflen ) {
|
|
syslog(LOG_ERR, "Unable to broadcast packet (%m)");
|
|
}
|
|
}
|
|
else {
|
|
syslog(LOG_ERR, "Cannot send broadcast (interface?)");
|
|
}
|
|
}
|
|
else {
|
|
itype = sr_get_interface_type(nets[0].ifname);
|
|
if(itype == TYPE_UNSUPPORT) {
|
|
syslog(LOG_ERR, "Unsupported interface `%s`",
|
|
nets[0].ifname);
|
|
free(bufp);
|
|
return;
|
|
}
|
|
sr_create_ip_send_packet(&bufp, &buflen, bp, itype);
|
|
|
|
#ifdef PERFORMANCE
|
|
if (perf_flag == DHCPDISCOVER){
|
|
discover_ctr ++;
|
|
gettimeofday(&perf_time_end, &perf_tz);
|
|
discover_time += (perf_time_end.tv_usec - perf_time_start.tv_usec)+ (MIL * (perf_time_end.tv_sec - perf_time_start.tv_sec));
|
|
if (!(discover_ctr % 100))
|
|
syslog(LOG_DEBUG,"The average time to process %lu DISCOVER messages = %lu usec.",discover_ctr, discover_time/discover_ctr);
|
|
}
|
|
else if(perf_flag == DHCPREQUEST){
|
|
request_ctr ++;
|
|
gettimeofday(&perf_time_end, &perf_tz);
|
|
request_time += (perf_time_end.tv_usec - perf_time_start.tv_usec)+
|
|
(MIL * (perf_time_end.tv_sec - perf_time_start.tv_sec));
|
|
if (!(request_ctr % 100))
|
|
syslog(LOG_DEBUG,"The average time to process %lu REQUESTS = %lu usec",request_ctr, request_time/request_ctr);
|
|
}
|
|
#endif
|
|
|
|
if ( (sendto(rsockout, bufp, buflen, 0, &braw_addr,
|
|
sizeof (SCIN))) != buflen ) {
|
|
syslog(LOG_ERR, "Cannot send broadcast packet (%m)");
|
|
}
|
|
}
|
|
free(bufp);
|
|
return;
|
|
}
|
|
}
|
|
|
|
to.sin_addr = dst;
|
|
#ifdef PERFORMANCE
|
|
if (perf_flag == DHCPDISCOVER){
|
|
discover_ctr ++;
|
|
gettimeofday(&perf_time_end, &perf_tz);
|
|
discover_time += (perf_time_end.tv_usec - perf_time_start.tv_usec) +
|
|
(MIL * (perf_time_end.tv_sec - perf_time_start.tv_sec));
|
|
if (!(discover_ctr % 100))
|
|
syslog(LOG_DEBUG,"The average time to process %lu DISCOVER messages = %lu usec.",discover_ctr, discover_time/discover_ctr);
|
|
}
|
|
else if(perf_flag == DHCPREQUEST){
|
|
request_ctr ++;
|
|
gettimeofday(&perf_time_end, &perf_tz);
|
|
request_time += (perf_time_end.tv_usec - perf_time_start.tv_usec) +
|
|
(MIL * (perf_time_end.tv_sec - perf_time_start.tv_sec));
|
|
if (!(request_ctr % 100))
|
|
syslog(LOG_DEBUG,"The average time to process %lu REQUESTS = %lu usec",request_ctr, request_time/request_ctr);
|
|
}
|
|
#endif
|
|
|
|
if (sendto(s, (caddr_t)bp, sizeof *bp, 0, &to, sizeof to) < 0)
|
|
syslog(LOG_ERR, "send to %s failed (%m)", iaddr_ntoa(to.sin_addr));
|
|
}
|
|
|
|
|
|
/*
|
|
* Setup the arp cache so that IP address 'ia' will be temporarily
|
|
* bound to hardware address 'ha' of length 'len'.
|
|
*/
|
|
void
|
|
setarp(iaddr_t *ia, u_char *ha, int len)
|
|
{
|
|
struct sockaddr_in *si;
|
|
|
|
bzero(&arpreq, sizeof(struct arpreq));
|
|
arpreq.arp_pa.sa_family = AF_INET;
|
|
si = (struct sockaddr_in *)&arpreq.arp_pa;
|
|
si->sin_addr = *ia;
|
|
bcopy(ha, arpreq.arp_ha.sa_data, len);
|
|
if (ioctl(s, SIOCSARP, (caddr_t)&arpreq) < 0)
|
|
syslog(LOG_ERR, "set arp ioctl failed (%m)");
|
|
}
|
|
|
|
/*
|
|
* get an arp entry
|
|
* this is used to check whether the reply to a ping was from the same
|
|
* mac address as the one for which the address was intended
|
|
*/
|
|
int
|
|
getarp(struct in_addr *ia, u_char *ha, int len)
|
|
{
|
|
struct sockaddr_in *si;
|
|
|
|
bzero(&arpreq, sizeof(struct arpreq));
|
|
arpreq.arp_pa.sa_family = AF_INET;
|
|
si = (struct sockaddr_in *)&arpreq.arp_pa;
|
|
si->sin_addr = *ia;
|
|
if (ioctl(s, SIOCGARP, (caddr_t)&arpreq) < 0) {
|
|
syslog(LOG_ERR, "get arp ioctl failed (%m)");
|
|
return -1;
|
|
}
|
|
bcopy(arpreq.arp_ha.sa_data, ha, len);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Read bootptab database file. Avoid rereading the file if the
|
|
* write date hasnt changed since the last time we read it.
|
|
*/
|
|
void
|
|
readtab(void)
|
|
{
|
|
struct stat st;
|
|
register char *cp;
|
|
int v;
|
|
register u_long i;
|
|
char temp[64], tempcpy[64];
|
|
register struct hosts *hp;
|
|
int skiptopercent;
|
|
struct hosts hosts_ent;
|
|
|
|
if (fp == 0) {
|
|
if ((fp = log_fopen(bootptab, "r")) == NULL) {
|
|
syslog(LOG_ERR, "can't open bootptab %s (%m)", bootptab);
|
|
exit(1);
|
|
}
|
|
}
|
|
fstat(fileno(fp), &st);
|
|
if (st.st_mtime == modtime && st.st_nlink)
|
|
return; /* hasnt been modified or deleted yet */
|
|
fclose(fp);
|
|
if ((fp = log_fopen(bootptab, "r")) == NULL) {
|
|
syslog(LOG_ERR, "can't reopen bootptab %s (%m)", bootptab);
|
|
exit(1);
|
|
}
|
|
fstat(fileno(fp), &st);
|
|
if (debug)
|
|
syslog(LOG_DEBUG, "(re)reading %s", bootptab);
|
|
modtime = st.st_mtime;
|
|
homedir[0] = defaultboot[0] = 0;
|
|
hp = &hosts_ent;
|
|
nhosts = 0;
|
|
cleanup_btabm();
|
|
cleanup_btabi();
|
|
linenum = 0;
|
|
skiptopercent = 1;
|
|
|
|
/*
|
|
* read and parse each line in the file.
|
|
*/
|
|
for (;;) {
|
|
if (fgets(line, sizeof line, fp) == NULL)
|
|
break; /* done */
|
|
if ( (i = strlen(line)) && (line[i-1] == '\n') )
|
|
line[i-1] = 0; /* remove trailing newline */
|
|
linep = line;
|
|
linenum++;
|
|
if (line[0] == '#' || line[0] == 0 || line[0] == ' ')
|
|
continue; /* skip comment lines */
|
|
/* fill in fixed leading fields */
|
|
if (homedir[0] == 0) {
|
|
getfield(homedir, sizeof homedir);
|
|
continue;
|
|
}
|
|
if (defaultboot[0] == 0) {
|
|
getfield(defaultboot, sizeof defaultboot);
|
|
continue;
|
|
}
|
|
if (skiptopercent) { /* allow for future leading fields */
|
|
if (line[0] != '%')
|
|
continue;
|
|
skiptopercent = 0;
|
|
continue;
|
|
}
|
|
/* fill in host table */
|
|
getfield(hp->host, sizeof hp->host);
|
|
getfield(temp, sizeof temp);
|
|
sscanf(temp, "%d", &v);
|
|
hp->htype = v;
|
|
getfield(temp, sizeof temp);
|
|
strcpy(tempcpy, temp);
|
|
cp = tempcpy;
|
|
/* parse hardware address */
|
|
for (i = 0 ; i < sizeof hp->haddr ; i++) {
|
|
char *cpold;
|
|
char c;
|
|
cpold = cp;
|
|
while (*cp != '.' && *cp != ':' && *cp != 0)
|
|
cp++;
|
|
c = *cp; /* save original terminator */
|
|
*cp = 0;
|
|
cp++;
|
|
if (sscanf(cpold, "%x", &v) != 1)
|
|
goto badhex;
|
|
hp->haddr[i] = v;
|
|
if (c == 0)
|
|
break;
|
|
}
|
|
if (hp->htype == 1 && i != 5) {
|
|
badhex: syslog(LOG_ERR, "bad hex address: %s at line %d of bootptab",
|
|
temp, linenum);
|
|
continue;
|
|
}
|
|
getfield(temp, sizeof temp);
|
|
i = inet_addr(temp);
|
|
if (i == -1) {
|
|
register struct hostent *hep;
|
|
hep = gethostbyname(temp);
|
|
if (hep != 0 && hep->h_addrtype == AF_INET)
|
|
i = *(int *)(hep->h_addr_list[0]);
|
|
}
|
|
if (i == -1 || i == 0) {
|
|
syslog(LOG_ERR, "bad internet address: %s at line %d of bootptab",
|
|
temp, linenum);
|
|
continue;
|
|
}
|
|
hp->iaddr.s_addr = i;
|
|
getfield(hp->bootfile, sizeof hp->bootfile);
|
|
insert_btab(hp);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Get next field from 'line' buffer into 'str'. 'linep' is the
|
|
* pointer to current position.
|
|
*/
|
|
void
|
|
getfield(char *str, int len)
|
|
{
|
|
register char *cp = str;
|
|
|
|
for ( ; *linep && (*linep == ' ' || *linep == '\t') ; linep++)
|
|
; /* skip spaces/tabs */
|
|
if (*linep == 0) {
|
|
*cp = 0;
|
|
return;
|
|
}
|
|
len--; /* save a spot for a null */
|
|
for ( ; *linep && *linep != ' ' && *linep != '\t' ; linep++) {
|
|
*cp++ = *linep;
|
|
if (--len <= 0) {
|
|
*cp = 0;
|
|
syslog(LOG_ERR, "string truncated: %s, on line %d of bootptab",
|
|
str, linenum);
|
|
return;
|
|
}
|
|
}
|
|
*cp = 0;
|
|
}
|
|
|
|
/*
|
|
* Build a list of all the names and aliases by which
|
|
* the current host is known on all of its interfaces.
|
|
*/
|
|
void
|
|
makenamelist(void)
|
|
{
|
|
register struct hostent *hp;
|
|
char name[64];
|
|
|
|
numaddrs = 0;
|
|
myaddrs = (u_int *)malloc(max_addr_alloc*sizeof(u_int));
|
|
|
|
/*
|
|
* Get name of host as told to the kernel and look that
|
|
* up in the hostname database.
|
|
*/
|
|
gethostname(name, sizeof(name));
|
|
if ((hp = gethostbyname(name)) == 0) {
|
|
syslog(LOG_ERR, "gethostbyname(%s) failed: %s", name, hstrerror(h_errno));
|
|
exit(1);
|
|
}
|
|
strcpy(myname,name);
|
|
|
|
/*
|
|
** Find domain name.
|
|
*/
|
|
mydomain = strchr(myname, '.');
|
|
if (mydomain) {
|
|
mydomain++;
|
|
}
|
|
|
|
/*
|
|
* Remember primary Internet address
|
|
*/
|
|
myhostaddr.s_addr = *(u_long *)(hp->h_addr_list[0]);
|
|
}
|
|
|
|
|
|
static char *
|
|
iaddr_ntoa(iaddr_t addr)
|
|
{
|
|
struct hostent *hp;
|
|
|
|
if (hp = gethostbyaddr(&addr, sizeof(addr), AF_INET)) {
|
|
return hp->h_name;
|
|
} else {
|
|
return inet_ntoa(addr);
|
|
}
|
|
}
|
|
|
|
static int
|
|
check_selected_name_address(char *rhsname, char **dhcp_msg,
|
|
u_long dhcp_ipaddr,
|
|
struct bootp *rq, struct dhcp_request_list *req, char *cid_ptr, int cid_length)
|
|
{
|
|
/* 1=invalid/inuse 2=alt_naming_flg error */
|
|
|
|
u_long mltaddr;
|
|
u_long netaddr;
|
|
char *client_domain;
|
|
char hostname1[MAXHOSTNAMELEN];
|
|
|
|
if (req->dh_configPtr->p_dnsdomain) {
|
|
client_domain = req->dh_configPtr->p_dnsdomain;
|
|
}
|
|
else if (req->dh_configPtr->p_nisdomain){
|
|
client_domain = req->dh_configPtr->p_nisdomain;
|
|
}
|
|
else {
|
|
client_domain = mydomain;
|
|
}
|
|
if (strchr(rhsname, '.') == NULL)
|
|
sprintf(hostname1,"%s.%s", rhsname, client_domain);
|
|
else
|
|
sprintf(hostname1,"%s", rhsname);
|
|
mltaddr =
|
|
MULTIHMDHCP ? np_recv->myaddr.s_addr : myhostaddr.s_addr;
|
|
netaddr =
|
|
rq->bp_giaddr.s_addr ? rq->bp_giaddr.s_addr : mltaddr;
|
|
|
|
if(sys0_hostnameIsValid(rhsname) == 0) {
|
|
*dhcp_msg =
|
|
sys0_name_errmsg(rhsname, NAME_INVALID);
|
|
return 1;
|
|
}
|
|
else if(loc0_is_hostname_assigned(hostname1, db)){
|
|
char *enm;
|
|
/* change - if name is assigned to
|
|
* same ether then it is okay
|
|
*/
|
|
enm = loc0_get_hostname_from_cid(cid_ptr, cid_length, (EtherAddr*)rq->bp_chaddr, db);
|
|
enm = strtok(enm, "."); /* hostname part */
|
|
if (enm && *enm && strcmp(rhsname, enm)) {
|
|
*dhcp_msg =sys0_name_errmsg(rhsname, NAME_INUSE);
|
|
return 1;
|
|
}
|
|
}
|
|
if(dhcp_ipaddr) {
|
|
dhcpConfig *newConfigPtr;
|
|
/* client is requesting a specific address */
|
|
newConfigPtr = cf0_get_config(dhcp_ipaddr, 1);
|
|
if (!newConfigPtr) {
|
|
*dhcp_msg =
|
|
sys0_addr_errmsg(dhcp_ipaddr, ADDR_INVALID);
|
|
return 3;
|
|
}
|
|
if(loc0_is_ipaddress_for_cid((EtherAddr *)rq->bp_chaddr,
|
|
dhcp_ipaddr, cid_ptr, cid_length, db) == 0){
|
|
/* 0 = same binding, 1 = ether not found, 2 = Found but diff ip*/
|
|
if (cf0_is_req_addr_valid(dhcp_ipaddr, netaddr) != 0) {
|
|
*dhcp_msg = sys0_addr_errmsg(dhcp_ipaddr, ADDR_INVALID);
|
|
return 3;
|
|
}
|
|
/* ok to give address */
|
|
}
|
|
else {
|
|
/* means ethernet not found or has diff binding */
|
|
if (loc0_is_ipaddress_assigned(dhcp_ipaddr, db)) {
|
|
*dhcp_msg =
|
|
sys0_addr_errmsg(dhcp_ipaddr, ADDR_INUSE);
|
|
return 3;
|
|
}
|
|
else { /* not assigned */
|
|
if (cf0_is_req_addr_valid(dhcp_ipaddr, netaddr) != 0) {
|
|
*dhcp_msg =
|
|
sys0_addr_errmsg(dhcp_ipaddr, ADDR_INVALID);
|
|
return 3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (alt_naming_flag) {
|
|
struct hostent *he;
|
|
int i;
|
|
|
|
free(req->dh_hsname);
|
|
req->dh_hsname = strdup(rhsname);
|
|
|
|
he = gethostbyname(hostname1);
|
|
if (he) {
|
|
for (i = 0; he->h_addr_list[i]; i++) {
|
|
if (rq->bp_giaddr.s_addr) {
|
|
if (cf0_is_req_addr_valid
|
|
(*(u_long *)he->h_addr_list[i],
|
|
rq->bp_giaddr.s_addr)) {
|
|
req->dh_ipaddr =
|
|
*(u_long *)(he->h_addr_list[i]);
|
|
return 2;
|
|
}
|
|
} else {
|
|
if (cf0_is_req_addr_valid
|
|
(*(u_long *)he->h_addr_list[i],mltaddr)) {
|
|
req->dh_ipaddr = *(u_long *)(he->h_addr_list[i]);
|
|
return 2;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#ifdef EDHCP
|
|
/*
|
|
* when a reply is received on the ping socket we process it to check if this
|
|
* is in reply to an ICMP echo send by us
|
|
*/
|
|
int
|
|
handle_ping_nonblocking_reply(int sd)
|
|
{
|
|
int res_seq;
|
|
int res_ident;
|
|
u_long res_node;
|
|
u_char respack[2048];
|
|
struct icmp *icmp_pack;
|
|
struct dhcp_request_list * dhcp_rq;
|
|
iaddr_t ipn;
|
|
struct timeval tmout;
|
|
long next_tm;
|
|
int ret = 0;
|
|
char tmp_cid[MAXCIDLEN];
|
|
struct ether_addr haa;
|
|
|
|
while (icmp_pack = recv_icmp_msg( sd, &res_node, respack,
|
|
sizeof(respack))) {
|
|
if (icmp_pack == (struct icmp *)NULL) {
|
|
return(-1);
|
|
}
|
|
|
|
if (ping_number_pending <= 0)
|
|
continue;
|
|
|
|
dhcp_rq = dh0_find_dhcp_rqlist_entry_by_ipaddr(res_node);
|
|
if (dhcp_rq == NULL) {
|
|
/* we may have already mistakenly given out this address
|
|
because the echo reply arrived late */
|
|
if (getRecByIpAddr(0, &res_node, db) != NULL) {
|
|
ipn.s_addr = res_node;
|
|
syslog(LOG_WARNING, "IPaddress %s served may be STOLEN",
|
|
inet_ntoa(ipn));
|
|
}
|
|
}
|
|
else {
|
|
ret = recv_echo_reply(&res_seq, &res_ident, icmp_pack );
|
|
if (ret == 0) {
|
|
if ( (res_ident != dhcp_rq->ping_ident) ||
|
|
(res_seq != dhcp_rq->ping_seqnum) ||
|
|
(res_node != dhcp_rq->dh_ipaddr) ) {
|
|
/* this reply does not match - do nothing */
|
|
syslog(LOG_DEBUG, "ping reply did not match with rqlist but address did match");
|
|
}
|
|
else {
|
|
/* every thing matched */
|
|
/* check if the mac address of the replying client is
|
|
* the same as the one from which we got the DISCOVER
|
|
* This is possible only if we didn't get the DISCOVER
|
|
* via a relay agent (because we use an arp call) */
|
|
ipn.s_addr = res_node;
|
|
if (!dhcp_rq->ping_rq.bp_giaddr.s_addr) {
|
|
if (getarp(&ipn, &haa.ea_addr[0], 6) == 0) {
|
|
if (bcmp(dhcp_rq->dh_eaddr, &haa.ea_addr[0],
|
|
sizeof(haa)) == 0) {
|
|
/* its the same mac so its not stolen */
|
|
return ret;
|
|
}
|
|
syslog(LOG_WARNING, "Address %s appears to be STOLEN by %s",
|
|
inet_ntoa(ipn), ether_ntoa(&haa));
|
|
}
|
|
}
|
|
else
|
|
syslog(LOG_WARNING, "Address %s appears to be STOLEN",
|
|
inet_ntoa(ipn));
|
|
dh0_remove_from_timelist(dhcp_rq->cid_ptr, dhcp_rq->cid_length);
|
|
/* don't remove from the reqlist - it will get updated */
|
|
/* try to get another address */
|
|
loc0_remove_entry(dhcp_rq->dh_eaddr, 3,
|
|
dhcp_rq->cid_ptr, dhcp_rq->cid_length, db);
|
|
/* mark the stolen address as bad */
|
|
sprintf(tmp_cid, "STOLEN-%d", ++msg_recv_time);
|
|
loc0_create_update_entry(tmp_cid, strlen(tmp_cid),
|
|
&haa, res_node,
|
|
tmp_cid, 0, STOLEN_LEASE, db);
|
|
request(&dhcp_rq->ping_rq);
|
|
time(&TimeNow);
|
|
next_tm = dh0_update_and_get_next_timeout(TimeNow);
|
|
dh0_set_timeval(&tmout, next_tm);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* after timeout occurs for an address which was pinged
|
|
* this function sends an OFFER to that host
|
|
*/
|
|
int
|
|
ping_send_DHCPOFFER(struct dhcp_request_list* dhcp_rq)
|
|
{
|
|
char str[MAXCIDLEN];
|
|
long offered_lease;
|
|
|
|
/* compute the lease to offer */
|
|
offered_lease = find_lease_to_offer(dhcp_rq->dh_ipaddr,
|
|
dhcp_rq->dh_reqLease,
|
|
dhcp_rq->dh_configPtr,
|
|
db);
|
|
|
|
if(dhcp_rq->dh_configPtr->p_choose_name)
|
|
dh0_encode_dhcp_server_packet(dhcp_rq->ping_rp.dh_opts,
|
|
DHCPOFFER,
|
|
&dhcp_rq->dh_reqParams,
|
|
offered_lease, dhcp_rq->ping_dh_msg,
|
|
dhcp_rq->dh_hsname, dhcp_rq->dh_hsname,
|
|
dhcp_rq->dh_configPtr,
|
|
&dhcp_rq->dci_data);
|
|
else
|
|
dh0_encode_dhcp_server_packet(dhcp_rq->ping_rp.dh_opts,
|
|
DHCPOFFER,
|
|
&dhcp_rq->dh_reqParams,
|
|
offered_lease, dhcp_rq->ping_dh_msg,
|
|
dhcp_rq->dh_hsname, 0,
|
|
dhcp_rq->dh_configPtr,
|
|
&dhcp_rq->dci_data);
|
|
|
|
dhcp_rq->ping_rp.bp_yiaddr.s_addr = dhcp_rq->dh_ipaddr;
|
|
dhcp_rq->ping_status = PING_RECV;
|
|
|
|
if (debug) {
|
|
mk_str(1, dhcp_rq->cid_ptr, dhcp_rq->cid_length, str);
|
|
syslog(LOG_DEBUG,"DHCPOFFER:cid (%s),ip (%s), name (%s)",
|
|
str,inet_ntoa(dhcp_rq->ping_rp.bp_yiaddr), dhcp_rq->dh_hsname);
|
|
}
|
|
if(dhcp_rq->ping_rp.bp_flags & BROADCAST_BIT)
|
|
sendreply(&dhcp_rq->ping_rp, 0, dhcp_rq->ping_has_sname, 1, DHCP_REPLY);
|
|
else
|
|
sendreply(&dhcp_rq->ping_rp, 0, dhcp_rq->ping_has_sname, 0, DHCP_REPLY);
|
|
|
|
time(&TimeNow);
|
|
dh0_addto_timelist(dhcp_rq->cid_ptr, dhcp_rq->cid_length,
|
|
dhcp_rq->dh_eaddr,
|
|
TimeNow+DHCPOFFER_TIMEOUT, 0);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* processing when a ping has been sent and the timeout has expired
|
|
* for each entry on the time list for which the time has expired
|
|
* an OFFER is sent
|
|
*/
|
|
|
|
int process_expired_ping_send(void)
|
|
{
|
|
struct dhcp_request_list* dhcp_rq;
|
|
time_t timenow;
|
|
struct dhcp_timelist *tq;
|
|
int done;
|
|
|
|
done = 0;
|
|
while (!done) {
|
|
if (dh0_timeouts_cnt <= 1)
|
|
return 0;
|
|
tq = dhcp_tmlist_first;
|
|
if(tq == 0)
|
|
return 0;
|
|
while(tq) {
|
|
time(&timenow);
|
|
if ((tq->tm_value - timenow) < 0) {
|
|
if (tq->tm_dhcp_rq)
|
|
dhcp_rq = tq->tm_dhcp_rq;
|
|
else
|
|
dhcp_rq = dh0_find_dhcp_rqlist_entry(tq->cid_ptr,
|
|
tq->cid_length);
|
|
if (dhcp_rq && dhcp_rq->ping_status == PING_SENT) {
|
|
/* this has expired */
|
|
dh0_remove_from_timelist(dhcp_rq->cid_ptr,
|
|
dhcp_rq->cid_length);
|
|
ping_send_DHCPOFFER(dhcp_rq);
|
|
done = 1; /* don't want to finish actually */
|
|
break;
|
|
}
|
|
}
|
|
tq = tq->tm_next;
|
|
}
|
|
done = (done == 1)? 0 : 1; /* finish if done is 0 */
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|