dhcps.c
上传用户:nvosite88
上传日期:2007-01-17
资源大小:4983k
文件大小:127k
源码类别:

VxWorks

开发平台:

C/C++

  1. /* dhcps.c - WIDE project DHCP server routines */
  2. /* Copyright 1984 - 2001 Wind River Systems, Inc. */
  3. #include "copyright_wrs.h"
  4. /*
  5. modification history
  6. --------------------
  7. 01n,23apr02,wap  use dhcpTime() instead of time() (SPR #68900), also use
  8.                  BPF_WORDALIGN() when traversing multiple packets in BPF
  9.                  buffers (SPR #74215)
  10. 01m,08mar02,wap  Return sane lease renewal times for clients that request
  11.                  leases without specifying their own lease times (SPR #73243)
  12. 01l,31oct01,vvv  allow low-priority tasks to run while server waits for 
  13.  ICMP reply (SPR #33132)
  14. 01k,12oct01,rae  merge from truestack ver 01s, base 01h
  15.                  SPRs 70184, 69547, 34799, 66808
  16. 01j,24oct00,spm  fixed modification history after tor3_x merge; fixed invalid
  17.                  socket reference (SPR #27246)
  18. 01i,23oct00,niq  merged from version 01j of tor3_x branch (base version 01h)
  19. 01h,01mar99,spm  corrected checksum calculation for ICMP requests (SPR #24745)
  20. 01g,06oct97,spm  removed reference to deleted endDriver global; replaced with
  21.                  support for dynamic driver type detection; split interface
  22.                  name into device name and unit number
  23. 01f,26aug97,spm  rearranged functions to consolidate lease selection routines
  24. 01e,02jun97,spm  changed DHCP option tags to prevent name conflicts (SPR #8667)
  25.                  and updated man pages
  26. 01d,06may97,spm  changed memory access to align IP header on four byte 
  27.                  boundary and corrected format of lease record
  28. 01c,28apr97,spm  allowed user to change DHCP_MAX_HOPS setting
  29. 01b,18apr97,spm  added conditional include DHCPS_DEBUG for displayed output
  30. 01a,07apr97,spm  created by modifying WIDE project DHCP implementation
  31. */
  32. /*
  33. DESCRIPTION
  34. This library implements the server side of the Dynamic Host Configuration
  35. Protocol (DHCP). DHCP is an extension of BOOTP. Like BOOTP, it allows a
  36. target to configure itself dynamically by obtaining its IP address, a
  37. boot file name, and the DHCP server's address over the network. Additionally,
  38. DHCP provides for automatic reuse of network addresses by specifying
  39. individual leases as well as many additional options. The compatible
  40. message format allows DHCP participants to interoperate with BOOTP
  41. participants.
  42. INCLUDE FILES: dhcpsLib.h
  43. SEE ALSO: RFC 1541, RFC 1533
  44. */
  45. /* includes */
  46. #include "dhcp/copyright_dhcp.h"
  47. #include "vxWorks.h"
  48. #include "net/bpf.h"
  49. #include "logLib.h"
  50. #include "vxLib.h"  /* checksum() declaration */
  51. #include "inetLib.h"
  52. #include "sockLib.h"
  53. #include "ioLib.h"
  54. #include "wdLib.h"
  55. #include "taskLib.h"
  56. #include "sysLib.h"
  57. #include "muxLib.h"
  58. #include "netinet/ip.h"
  59. #include "netinet/in_systm.h"
  60. #include "netinet/ip_icmp.h" 
  61. #include <time.h>
  62. #include <stdio.h>
  63. #include <stdlib.h>
  64. #include "dhcp/dhcp.h"
  65. #include "dhcp/common.h"
  66. #include "dhcp/hash.h"
  67. #include "dhcp/dhcps.h"
  68. #include "dhcpsLib.h"
  69. #include "dhcp/common_subr.h"
  70. /* defines */
  71. #define MEMORIZE 90  /* Seconds of delay before re-using offered lease. */
  72. #define E_NOMORE -2  /* Error code: no more space in options field. */
  73. #ifndef VIRTUAL_STACK
  74. /* globals */
  75. int nbind;  /* Number of active or pending lease records. */
  76. struct msg dhcpsMsgIn; /* Pointers to components of incoming message. */
  77. struct msg dhcpsMsgOut; /* Pointers to outgoing message parts. */
  78. char *dhcpsSendBuf;     /* Transmit buffer for outgoing messages. */
  79. char *dhcpsOverflowBuf; /* Extra space (for larger messages) starts here. */
  80. IMPORT struct if_info *dhcpsIntfaceList;  /* Interfaces to monitor. */
  81. struct iovec sbufvec[2];  /* Socket access to outgoing message. 
  82.                                  * sbufvec[0] is standard message. 
  83.                                  * sbufvec[1] contains message extensions if
  84.                                  * client accepts longer messages. 
  85.                                  */
  86. IMPORT int dhcpsMaxSize; /* Size of transmit buffer. */
  87. IMPORT int dhcpsBufSize; /* Size of receive buffer. */
  88. IMPORT u_short dhcps_port;
  89. IMPORT u_short dhcpc_port;
  90. /* locals */
  91. LOCAL unsigned char dhcpCookie[] = RFC1048_MAGIC; /* DHCP message indicator. */
  92. LOCAL int rdhcplen; /* Size of received DHCP message. */
  93. LOCAL int overload;  /* Options in sname or file fields? */
  94. LOCAL int off_options;  /* Index into options field. */
  95. LOCAL int off_extopt;  /* Index into any options in sbufvec[1]. */
  96. LOCAL int maxoptlen; /* Space available for options. */
  97. LOCAL int off_file;  /* Index into any options within file field. */
  98. LOCAL int off_sname;  /* Index into any options in sname field. */
  99. #else
  100. #include "netinet/vsLib.h"
  101. #include "netinet/vsDhcps.h"
  102. #endif /* VIRTUAL_STACK */
  103. /* forward declarations */
  104. LOCAL int icmp_check (int, struct in_addr *);
  105. IMPORT STATUS dhcpsSend (struct ifnet *, char *, int,
  106.                          struct sockaddr_in *, char *, int, BOOL);
  107. IMPORT void dhcpServerRelay (struct if_info *);
  108. IMPORT void dhcpClientRelay (struct if_info *, int, char *);
  109. IMPORT void delarp (struct in_addr *, int);
  110. /*******************************************************************************
  111. *
  112. * dhcpsStart - execute the DHCP server
  113. *
  114. * This routine receives DHCP requests from clients and generates replies. If
  115. * configured to do so, it may also act as a relay agent and forward these 
  116. * requests to other servers or transfer replies to clients. It is the entry 
  117. * point for the DHCP server task and should only be called internally.
  118. *
  119. * RETURNS: N/A
  120. *
  121. * ERRNO: N/A
  122. *
  123. * NOMANUAL
  124. */
  125. int dhcpsStart
  126.     (
  127. #ifndef VIRTUAL_STACK
  128.     void
  129. #else
  130.     int stackNum
  131. #endif /* VIRTUAL_STACK */
  132.     )
  133.     {
  134.     struct if_info *ifp;  /* pointer to receiving interface descriptor */
  135.     int n = 0;  /* number of bytes received */
  136.     int msgtype;  /* DHCP message type, or BOOTP indicator */
  137.     char *option = NULL;  /* pointer to access message options */
  138.     struct bpf_hdr *    pMsgHdr;
  139.     char *              pMsgData;
  140.     int                 msglen;
  141.     int                 curlen;
  142.     int                 totlen;         /* Amount of data in BPF buffer. */
  143. #ifdef VIRTUAL_STACK
  144.     if (virtualStackNumTaskIdSet (stackNum) == ERROR)
  145.         return (ERROR);
  146. #endif /* VIRTUAL_STACK */
  147.     /* Main loop - read and process incoming messages. */
  148.     FOREVER 
  149.         { 
  150.         garbage_collect ();      /* remove expired leases from hash table. */
  151.         /* select and read from interfaces */
  152.         ifp = read_interfaces (dhcpsIntfaceList, &n, dhcpsBufSize);
  153.         if (ifp == NULL)
  154.             continue;
  155.         /* Divide each DHCP message in buffer into protocol sections. */
  156.         msglen = curlen = 0;
  157.         totlen = n;
  158.         pMsgHdr = (struct bpf_hdr *)ifp->buf;
  159.         pMsgData = ifp->buf;
  160.         while (curlen < totlen)
  161.             {
  162.             msglen = BPF_WORDALIGN(pMsgHdr->bh_hdrlen + pMsgHdr->bh_caplen);
  163.             curlen += msglen;
  164.             /* Set the IP pointer to skip the BPF and link level headers. */
  165.             dhcpsMsgIn.ip = (struct ip *) (pMsgData + pMsgHdr->bh_hdrlen +
  166.                                            pMsgHdr->bh_linklen);
  167.             if ( (dhcpsMsgIn.ip->ip_dst.s_addr == 0xffffffff ||
  168.                    dhcpsMsgIn.ip->ip_dst.s_addr == ifp->ipaddr.s_addr) &&
  169.                   check_ipsum (dhcpsMsgIn.ip))
  170.                 dhcpsMsgIn.udp =
  171.                 (struct udphdr *)( (UCHAR *)dhcpsMsgIn.ip +
  172.                                               dhcpsMsgIn.ip->ip_hl * WORD);
  173.             else
  174.                 {
  175.                 /* Invalid IP header: ignore. */
  176.                 pMsgData = pMsgData + msglen;
  177.                 pMsgHdr = (struct bpf_hdr *)pMsgData;
  178.                 continue;
  179.                 }
  180.             if (check_udpsum (dhcpsMsgIn.ip, dhcpsMsgIn.udp))
  181.                 dhcpsMsgIn.dhcp =
  182.                 (struct dhcp *)( (UCHAR *)dhcpsMsgIn.udp + UDPHL);
  183.             else
  184.                 {
  185.                 /* Invalid UDP header: ignore. */
  186.                 pMsgData = pMsgData + msglen;
  187.                 pMsgHdr = (struct bpf_hdr *)pMsgData;
  188.                 continue;
  189.                 }
  190.             /*
  191.              * Perform function of relay agent for received server replies.
  192.              * (Changes contents of input message IP and UDP headers while
  193.              * sending to client).
  194.              */
  195.             dhcpMsgIn.ip = dhcpsMsgIn.ip;
  196.             dhcpMsgIn.udp = dhcpsMsgIn.udp;
  197.             dhcpMsgIn.dhcp = dhcpsMsgIn.dhcp;
  198.             if (dhcpMsgIn.dhcp->op == BOOTREPLY)
  199.                 dhcpClientRelay (dhcpsIntfaceList, DHCPLEN (dhcpsMsgIn.udp),
  200.                                  dhcpsSendBuf);
  201.             if (dhcpsMsgIn.dhcp->op != BOOTREQUEST)
  202.                 {
  203.                 pMsgData = pMsgData + msglen;
  204.                 pMsgHdr = (struct bpf_hdr *)pMsgData;
  205.                 continue;
  206.                 }
  207.             /* Process DHCP client message received at server port. */
  208.             rdhcplen = DHCPLEN (dhcpsMsgIn.udp);
  209.             msgtype = BOOTP;
  210.             option = pickup_opt (dhcpsMsgIn.dhcp, rdhcplen, _DHCP_MSGTYPE_TAG);
  211.             if (option != NULL)
  212.                 msgtype = (int) *OPTBODY(option);
  213. #ifdef DHCPS_DEBUG
  214.             if (msgtype < BOOTP || msgtype > DHCPRELEASE)
  215.                 logMsg ("dhcps: unknown message received.n",
  216.                          0, 0, 0, 0, 0, 0);
  217. #endif
  218.             if (msgtype >= BOOTP && msgtype <= DHCPRELEASE)
  219.                 if ( (process_msg [msgtype]) != NULL)
  220.                     (*process_msg [msgtype]) (ifp);
  221.             /*
  222.              * Relay received client messages if target addresses available.
  223.              * (Changes contents of input DHCP message when sending to
  224.              * server or relay agent - must process first).
  225.              */
  226.             dhcpMsgIn.udp = dhcpsMsgIn.udp;
  227.             dhcpMsgIn.dhcp = dhcpsMsgIn.dhcp;
  228.             if (pDhcpTargetList != NULL)
  229.                 dhcpServerRelay (ifp);
  230.             pMsgData = pMsgData + msglen;
  231.             pMsgHdr = (struct bpf_hdr *)pMsgData;
  232.             }
  233.         }
  234.     }
  235. /*******************************************************************************
  236. *
  237. * haddrtos - convert hardware address to cache format
  238. *
  239. * This routine converts the given hardware address to the <type>:<value> pair
  240. * used when adding lease record entries to permanent storage.
  241. *
  242. * RETURNS: Pointer to converted string
  243. *
  244. * ERRNO: N/A
  245. *
  246. * NOMANUAL
  247. */
  248. char * haddrtos
  249.     (
  250.     struct chaddr *haddr  /* pointer to parsed hardware address */
  251.     )
  252.     {
  253.     int i;
  254.     int fin;
  255.     char tmp[3];
  256.     static char result [MAX_HLEN * 2 + 8];     /* it seems enough */
  257.     bzero (result, sizeof (result));
  258.     fin = haddr->hlen;
  259.     if (fin > MAX_HLEN)
  260.         fin = MAX_HLEN;
  261.     sprintf (result, "%d:0x", haddr->htype);
  262.     for (i = 0; i < fin; i++) 
  263.         {
  264.         sprintf(tmp, "%.2x", haddr->haddr[i] & 0xff);
  265.         strcat(result, tmp);
  266.         }
  267.     return (result);
  268.     }
  269. /*******************************************************************************
  270. *
  271. * cidtos - convert client identifier to cache format
  272. *
  273. * This routine converts the given client identifier to the <type>:<value> pair
  274. * used when adding lease record entries to permanent storage. It also
  275. * adds the current subnet number so that the server can deny inaccurate 
  276. * leases caused by a client changing subnets. When used for user output, the
  277. * subnet is not included in the converted string.
  278. *
  279. * RETURNS: Pointer to converted string
  280. *
  281. * ERRNO: N/A
  282. *
  283. * NOMANUAL
  284. */
  285. char * cidtos
  286.     (
  287.     struct client_id *cid, /* pointer to parsed client ID */
  288.     int withsubnet  /* flag for adding subnet to output string */
  289.     )
  290.     {
  291.     int i = 0;
  292.     static char result [MAXOPT * 2 + sizeof ("255.255.255.255") + 3];
  293.     char tmp [sizeof ("255.255.255.255") + 1];
  294.     sprintf (result, "%d:0x", cid->idtype);
  295.     for (i = 0; i < cid->idlen; i++) 
  296.         {
  297.         sprintf (tmp, "%.2x", cid->id[i] & 0xff);
  298.         strcat (result, tmp);
  299.         }
  300.     if (withsubnet) 
  301.         {
  302.         inet_ntoa_b (cid->subnet, tmp);
  303.         strcat (result, ":");
  304.         strcat (result, tmp);
  305.         }
  306.     return (result);
  307.     }
  308. /*******************************************************************************
  309. *
  310. * get_reqlease - Retrieve requested lease value 
  311. *
  312. * This routine extracts the desired lease duration from the options field of 
  313. * the DHCP client request and converts it to host byte order.
  314. *
  315. * RETURNS: Requested lease duration, or 0 if none.
  316. *
  317. * ERRNO: N/A
  318. *
  319. * NOMANUAL
  320. */
  321.   
  322. static u_long get_reqlease
  323.     (
  324.     struct dhcp *msg,  /* pointer to incoming message */
  325.     int length  /* length of incoming message */
  326.     )
  327.     {
  328.     char *option = NULL;
  329.     u_long retval = 0;
  330.     if ( (option = pickup_opt (msg, length, _DHCP_LEASE_TIME_TAG)) != NULL) 
  331.         retval = GETHL (OPTBODY (option));
  332.     return (retval);
  333.     }
  334. /*******************************************************************************
  335. *
  336. * get_cid - Retrieve client identifier
  337. *
  338. * This routine extracts the client identifier from the options field of the
  339. * DHCP client request and stores the <type>:<value> pair in the given 
  340. * structure. If no explicit client ID is given, the routine uses the hardware
  341. * address, as specified by RFC 1541.
  342. *
  343. * RETURNS: N/A
  344. *
  345. * ERRNO: N/A
  346. *
  347. * NOMANUAL
  348. */
  349. static void get_cid
  350.     (
  351.     struct dhcp *msg,  /* pointer to incoming message */
  352.     int length,  /* length of incoming message */
  353.     struct client_id *cid  /* pointer to storage for parsed data */
  354.     )
  355.     {
  356.     char *option = NULL;
  357.     option = pickup_opt (msg, length, _DHCP_CLIENT_ID_TAG);
  358.     if (option != NULL) 
  359.         {
  360.         cid->idlen = ( (int)DHCPOPTLEN (option)) - 1; /* -1 for ID type */
  361.         bcopy (OPTBODY (option) + 1, cid->id, cid->idlen);
  362.         cid->idtype = *OPTBODY (option);
  363.         } 
  364.     else 
  365.         { 
  366.         /* haddr is used to substitute for client identifier */
  367.         cid->idlen = msg->hlen;
  368.         cid->idtype = msg->htype;
  369.         bcopy (msg->chaddr, cid->id, msg->hlen);
  370.         }
  371.     return;
  372.     }
  373. /*******************************************************************************
  374. *
  375. * get_maxoptlen - Calculate size of options field
  376. *
  377. * This routine determines the number of bytes available for DHCP options
  378. * without overloading. For standard length messages of 548 bytes, it returns 
  379. * the default length of 312 bytes. For longer messages, it returns 312 bytes 
  380. * plus the excess bytes (beyond 548), unless the DHCP message length exceeds
  381. * the MTU size. In that case, it returns the number of bytes in the MTU not
  382. * needed for the IP header, UDP header, and the fixed-length portion of the
  383. * DHCP messages, which require 264 bytes total.
  384. *
  385. * RETURNS: Size of variable-length options field
  386. *
  387. * ERRNO: N/A
  388. *
  389. * NOMANUAL
  390. */
  391. static int get_maxoptlen
  392.     (
  393.     struct dhcp *msg,  /* pointer to incoming message */
  394.     int length  /* length of incoming message */
  395.     )
  396.     {
  397.     char *option = NULL;
  398.     int retval = DFLTOPTLEN;
  399.     /* Calculate length of options field from maximum message size. */
  400.     if ( (option = pickup_opt (msg, length, _DHCP_MAXMSGSIZE_TAG)) != NULL)
  401.         retval = GETHS (OPTBODY (option)) - IPHL - UDPHL - DFLTDHCPLEN 
  402.                         + DFLTOPTLEN;
  403.     /* 
  404.      * If requested maximum size exceeds largest supported value, return
  405.      * value equal to portion of buffer not required for message headers
  406.      * or fixed-size portion of DHCP message. 
  407.      */
  408.     if (retval - DFLTOPTLEN + DFLTDHCPLEN + UDPHL + IPHL > dhcpsMaxSize)
  409.         retval = dhcpsMaxSize - IPHL - UDPHL - DFLTDHCPLEN + DFLTOPTLEN;
  410.     return (retval);
  411.     }
  412. /*******************************************************************************
  413. *
  414. * get_subnet - Retrieve subnet number
  415. *
  416. * This routine determines the subnet number of the requesting client and
  417. * stores it in the given structure. This value is determined using the
  418. * subnet mask specified by the client, if present. Otherwise, it is formed
  419. * from the last known subnet mask, if available, or the subnet mask of the
  420. * receiving interface. The server will only issue leases for IP addresses 
  421. * with the same subnet number as the requesting client.
  422. *
  423. * RETURNS: 0 if subnet number determined, or -1 otherwise.
  424. *
  425. * ERRNO: N/A
  426. *
  427. * NOMANUAL
  428. */
  429. static int get_subnet
  430.     (
  431.     struct dhcp *msg,  /* pointer to incoming message */
  432.     int length,  /* length of incoming message */
  433.     struct in_addr *subn,  /* pointer to storage for parsed data */
  434.     struct if_info *ifp  /* pointer to receiving interface descriptor */
  435.     )
  436.     {
  437.     char *option = NULL;
  438.     struct relay_acl *acl = NULL;
  439.     struct dhcp_resource *res = NULL;
  440. #ifdef DHCPS_DEBUG
  441.     char output [INET_ADDR_LEN];
  442. #endif
  443.     if (msg->ciaddr.s_addr != 0)
  444.         {
  445.         if ( (option = pickup_opt (msg, length, _DHCP_SUBNET_MASK_TAG))
  446.               != NULL) 
  447.             {
  448.             subn->s_addr = 
  449.                          msg->ciaddr.s_addr & htonl (GETHL (OPTBODY (option)));
  450.             return (0);
  451.             }
  452.         else 
  453.             {
  454.             res = (struct dhcp_resource *)
  455.           hash_find (&iphashtable, (char *)&msg->ciaddr.s_addr,
  456.      sizeof (u_long), resipcmp, &msg->ciaddr);
  457. #ifdef DHCPS_DEBUG
  458.             if (res == NULL)
  459.                 logMsg ("get_subnet can't find IP address in hash table.n",
  460.                          0, 0, 0, 0, 0, 0);
  461. #endif
  462.             if (res != NULL) 
  463.                 {
  464.                 subn->s_addr = msg->ciaddr.s_addr & res->subnet_mask.s_addr;
  465.                 return (0);
  466.                 }
  467.             }
  468.         }
  469.     if (msg->giaddr.s_addr != 0) 
  470.         {
  471.         if ( (option = pickup_opt (msg, length, _DHCP_SUBNET_MASK_TAG)) 
  472.               != NULL)
  473.             {
  474.             subn->s_addr = 
  475.                          msg->giaddr.s_addr & htonl (GETHL (OPTBODY (option)));
  476.             return (0);
  477.             }
  478.         else if ( (acl = (struct relay_acl *)
  479.                  hash_find (&relayhashtable, (char *)&msg->giaddr,
  480.                             sizeof (struct in_addr), relayipcmp,
  481.                             &msg->giaddr)) == NULL) 
  482.             {
  483. #ifdef DHCPS_DEBUG
  484.             inet_ntoa_b (msg->giaddr, output);          
  485.             logMsg ("DHCP message sent from invalid relay agent(%s).n", 
  486.                      output, 0, 0, 0, 0, 0);
  487. #endif
  488.             return (-1);
  489.             } 
  490.         else 
  491.             {
  492.             subn->s_addr = (acl->relay.s_addr & acl->subnet_mask.s_addr);
  493.             return (0);
  494.             }
  495.         }
  496.     /* Client doesn't have IP address - form from received interface. */
  497.     subn->s_addr = ifp->ipaddr.s_addr & ifp->subnetmask.s_addr;
  498.     return (0);
  499.     }
  500. /*******************************************************************************
  501. *
  502. * get_snmk - Retrieve subnet mask
  503. *
  504. * This routine determines the subnet mask for the requesting client and
  505. * stores it in the given structure. This value is determined from the 
  506. * value specified for the relay agent, if the message was forwarded, or
  507. * the subnet mask of the receiving interface. 
  508. *
  509. * RETURNS: N/A
  510. *
  511. * ERRNO: N/A
  512. *
  513. * NOMANUAL
  514. */
  515. static int get_snmk
  516.     (
  517.     struct dhcp *msg,  /* pointer to incoming message */
  518.     int length,  /* length of incoming message */
  519.     struct in_addr *subn,  /* pointer to storage for parsed data */
  520.     struct if_info *ifp  /* pointer to interface descriptor */
  521.     )
  522.     {
  523.     struct relay_acl *acl = NULL;
  524. #ifdef DHCPS_DEBUG
  525.     char output [INET_ADDR_LEN];
  526. #endif
  527.     if (msg->giaddr.s_addr != 0) 
  528.         {
  529.         acl = (struct relay_acl *)hash_find(&relayhashtable, 
  530.                                             (char *) &msg->giaddr,
  531.                             sizeof (struct in_addr),
  532.                                             relayipcmp, &msg->giaddr);
  533.         if (acl == NULL) 
  534.             {
  535. #ifdef DHCPS_DEBUG
  536.             inet_ntoa_b (msg->giaddr, output);
  537.             logMsg ("packet received from invalid relay agent(%s).n", output, 
  538.                      0, 0, 0, 0, 0);
  539. #endif
  540.             return (-1);
  541.             } 
  542.         else 
  543.             {
  544.             subn->s_addr = acl->subnet_mask.s_addr;
  545.             return (0);
  546.             }
  547.         }
  548.     subn->s_addr = ifp->subnetmask.s_addr;
  549.     return (0);
  550.     }
  551. /*******************************************************************************
  552. *
  553. * available_res - Check resource availability
  554. *
  555. * This routine determines if the resource selected by the server may
  556. * be offered to the client. The resource is available if it has not been
  557. * assigned or offered to any client (res->binding == NULL), or the lease has 
  558. * expired (expire_epoch < curr_epoch), or an outstanding offer was not 
  559. * acknowledged within the time allotted (temp_epoch < curr_epoch). The
  560. * resource is also available if it was  manually assigned to the given 
  561. * client (binding->cid matches given client ID).
  562. *
  563. * RETURNS: TRUE if resource available, or FALSE otherwise. 
  564. *
  565. * ERRNO: N/A
  566. *
  567. * NOMANUAL
  568. */
  569. static int available_res 
  570.     (
  571.     struct dhcp_resource *res,  /* pointer to lease descriptor */
  572.     struct client_id *cid,  /* pointer to client ID */
  573.     time_t curr_epoch  /* current time, in seconds */ 
  574.     )
  575.     {
  576.     return (res->binding == NULL ||
  577.     (res->binding->expire_epoch != 0xffffffff &&
  578.      res->binding->expire_epoch < curr_epoch &&
  579.      res->binding->temp_epoch < curr_epoch) ||
  580.     cidcmp (&res->binding->cid, cid));
  581.     }
  582. /*******************************************************************************
  583. *
  584. * cidcopy - copy client identifier
  585. *
  586. * This routine copies the <type>:<value> client identifier pair from the
  587. * source structure to the destination.
  588. *
  589. * RETURNS: 0, always.
  590. *
  591. * ERRNO: N/A
  592. *
  593. * NOMANUAL
  594. */
  595. static int cidcopy
  596.     (
  597.     struct client_id *src,  /* source client identifier */
  598.     struct client_id *dst  /* destiniation client identifier */
  599.     )
  600.     {
  601.     dst->subnet.s_addr = src->subnet.s_addr;
  602.     dst->idtype = src->idtype;
  603.     dst->idlen = src->idlen;
  604.     bzero (dst->id, src->idlen);
  605.     bcopy (src->id, dst->id, src->idlen);
  606.     return (0);
  607.     }
  608. /*******************************************************************************
  609. *
  610. * choose_lease - determine lease duration
  611. *
  612. * This routine selects the lease duration for the offer to the client. If
  613. * the resource is client-specific, the lease duration is infinite. Otherwise,
  614. * the server provides the duration requested by the client, if available, or 
  615. * the maximum available lease, whichever is less. If the client does not 
  616. * request a lease duration, and has no active lease, the default lease value 
  617. * is returned. For lease renewals or rebinding attempts, or if the lease
  618. * has expired, the default lease value is also returned.
  619. *
  620. * RETURNS: Selected lease duration.
  621. *
  622. * ERRNO: N/A
  623. *
  624. * NOMANUAL
  625. */
  626. static int choose_lease
  627.     (
  628.     int reqlease,  /* requested lease duration (sec) */
  629.     time_t curr_epoch,  /* current time, in seconds */
  630.     struct dhcp_resource *offer_res  /* pointer to lease descriptor */
  631.     )
  632.     {
  633.     u_long offer_lease = 0;
  634.     /* Manual allocation - give an infinite lease to client. */
  635.     if (ISSET (offer_res->valid, S_CLIENT_ID)) 
  636.         offer_lease = 0xffffffff;
  637.     /* Give requested lease, or maximum lease if request exceeds that value. */
  638.     else if (reqlease != 0) 
  639.         {
  640.         if (reqlease <= offer_res->max_lease)
  641.             offer_lease = reqlease;
  642.         else
  643.             offer_lease = offer_res->max_lease;
  644.         }
  645.     /* Initial request - give default lease. */
  646.     else if (offer_res->binding == NULL) 
  647.         offer_lease = offer_res->default_lease;
  648.     /* Lease renewal or rebinding. */
  649.     else 
  650.         {
  651.         /* "Renew" infinite lease. */
  652.         if (offer_res->binding->expire_epoch == 0xffffffff) 
  653.             offer_lease = 0xffffffff;
  654.         /*
  655.          * Lease expired (or being renewed) - give new lease
  656.          * of default duration.
  657.          */
  658.         else 
  659.             offer_lease = offer_res->default_lease;
  660.         }
  661.     return (offer_lease);
  662.     }
  663. /*******************************************************************************
  664. *
  665. * select_wcid - retrieve manually allocated leases
  666. *
  667. * This routine retrieves the dhcp_resource structure which holds the parameters
  668. * from the address pool database specifically allocated to the client with
  669. * the given client identifier, if any. These lease types have the highest
  670. * priority when the server is selecting a lease for a client in response to
  671. * a DHCP discover message.
  672. *
  673. * RETURNS: Manually allocated resource, or NULL if none or not available.
  674. *
  675. * ERRNO: N/A
  676. *
  677. * NOMANUAL
  678. */
  679. static struct dhcp_resource * select_wcid
  680.     (
  681.     int msgtype,  /* DHCP message type */
  682.     struct client_id *cid,  /* pointer to client ID */
  683.     time_t curr_epoch  /* current time, in seconds */
  684.     )
  685.     {
  686.     struct dhcp_binding *binding = NULL;
  687.     binding = (struct dhcp_binding *)hash_find (&cidhashtable, cid->id, 
  688.                                                 cid->idlen, bindcidcmp, cid);
  689.     if (binding != NULL) 
  690.         {
  691.         /*
  692.          * Is the resource used ?
  693.          */
  694.         if (available_res (binding->res, cid, curr_epoch)) 
  695.             {
  696.             if (icmp_check (msgtype, &binding->res->ip_addr) == GOOD)
  697.                 {
  698.         return (binding->res);
  699.                 } 
  700.             else 
  701.                 { 
  702.                 turnoff_bind (binding); 
  703.                 return (NULL); 
  704.                 } 
  705.             } 
  706.         } 
  707.     return (NULL); 
  708.     }
  709. /*******************************************************************************
  710. *
  711. * select_wreqip - retrieve resource matching IP address
  712. *
  713. * This routine retrieves the dhcp_resource structure which provides the IP
  714. * address requested by the client. The requested IP address must be on the
  715. * same subnet as the requesting client. The client must also provide a
  716. * matching client identifier, if included in the server's database entry.
  717. *
  718. * RETURNS: Matching resource, or NULL if none or not available.
  719. *
  720. * ERRNO: N/A
  721. *
  722. * NOMANUAL
  723. */
  724.  
  725. static struct dhcp_resource * select_wreqip 
  726.     ( 
  727.     int msgtype,  /* DHCP message type */
  728.     struct client_id *cid,  /* pointer to client ID */
  729.     time_t curr_epoch  /* current time, in seconds */
  730.     ) 
  731.     { 
  732.     char *option = NULL;  /* pointer to access options field */
  733.     char tmp [INET_ADDR_LEN];  /* temp IP address storage */
  734.     struct dhcp_resource *res = NULL;  /* access to lease descriptor data */
  735.     struct in_addr reqip;  /* value of desired IP address */
  736.     bzero (tmp, sizeof (tmp));
  737.     bzero ( (char *)&reqip, sizeof (reqip));
  738.     option = pickup_opt (dhcpsMsgIn.dhcp, rdhcplen, _DHCP_REQUEST_IPADDR_TAG);
  739.     if (option != NULL) 
  740.         {
  741.         reqip.s_addr = htonl (GETHL (OPTBODY (option)));
  742.         res = (struct dhcp_resource *)hash_find (&iphashtable, 
  743.                                                  (char *)&reqip.s_addr,
  744.                          sizeof (u_long), resipcmp, 
  745.                                                  &reqip);
  746.         if (res == NULL) 
  747.             {
  748. #ifdef DHCPS_DEBUG
  749.             inet_ntoa_b (reqip, tmp);
  750.             logMsg ("IP address %s is not in address pool", tmp, 
  751.                      0, 0, 0, 0, 0);
  752. #endif
  753.             return (NULL);
  754.             } 
  755.         else 
  756.             {   
  757.             /* check the subnet number */
  758.             if (cid->subnet.s_addr != 
  759.                 (res->ip_addr.s_addr & res->subnet_mask.s_addr)) 
  760.                 {
  761. #ifdef DHCPS_DEBUG
  762.                 inet_ntoa_b (reqip, tmp);
  763.         logMsg (
  764.           "DHCP%s(cid:"%s"): subnet mismatch for requested IP address %s.n",
  765.        (int)((msgtype == DHCPDISCOVER) ? "DISCOVER" : "REQUEST"),
  766.                 (int)cidtos (cid, 1), (int)tmp, 0, 0, 0);
  767. #endif
  768.         return (NULL);
  769.                 }
  770.             /* is it manual allocation ? */
  771.             if (ISSET (res->valid, S_CLIENT_ID)) 
  772.                 {
  773.         /* is there corresponding binding ? */
  774.         if (res->binding == NULL) 
  775.                     {
  776. #ifdef DHCPS_DEBUG
  777.                     inet_ntoa_b (reqip, tmp);
  778.             logMsg (
  779.                          "DHCP%s(cid:"%s"): No corresponding binding for %s",
  780.                     (int)((msgtype == DHCPDISCOVER) ? "DISCOVER" : "REQUEST"),
  781.                             (int)cidtos (cid, 1), (int)tmp, 0, 0, 0);
  782. #endif
  783.                      return (NULL);
  784.                     }
  785.         /* Check for matching client identifiers. */
  786.         if (cidcmp (&res->binding->cid, cid) != TRUE) 
  787.                     {
  788. #ifdef DHCPS_DEBUG
  789.                     inet_ntoa_b (reqip, tmp);
  790.             logMsg (
  791.         "DHCP%s(cid:"%s"): Client ID mismatch for requested IP address %s.n",
  792.                      (int)((msgtype == DHCPDISCOVER) ? "DISCOVER" : "REQUEST"),
  793.                            (int)cidtos (cid, 1), (int)tmp, 0, 0, 0);
  794. #endif
  795.                     return (NULL);
  796.             }
  797.         /* Check if the address is already in use. */
  798.         if (icmp_check (msgtype, &res->ip_addr) == GOOD) 
  799.             return (res);
  800.                 else 
  801.                     {
  802.             turnoff_bind (res->binding);
  803.             return (NULL);
  804.             }
  805.                 }
  806.             /* If not manual allocation, is the requested lease available? */
  807.             else if (available_res (res, cid, curr_epoch)) 
  808.                {
  809.        if (icmp_check (msgtype, &res->ip_addr) == GOOD)
  810.            return (res);
  811.        else
  812.                    { 
  813.            turnoff_bind (res->binding);
  814.            return (NULL);
  815.            }
  816.                }
  817.             }
  818.         }
  819.     return (NULL);    /* No requested IP address present in option list. */
  820.     }
  821. /*******************************************************************************
  822. *
  823. * select_newone - choose an entry from the available resources
  824. *
  825. * This routine retrieves a dhcp_resource structure for a client if no entry
  826. * was found based on the requested IP address, if any, or the client 
  827. * identifier. No addresses which are in use or on a different subnet than 
  828. * the client are considered. For the remaining entries, it uses the following 
  829. * criteria:
  830. *
  831. *      First, select entries which were never used in preference to used ones.
  832. *
  833. *      If not found, select the least recently used entry.
  834. *
  835. *      Next, give preference to entries which are unavailable to BOOTP clients.
  836. *
  837. *      Among unused or equally old entries, select the smallest possible 
  838. *      maximum which exceeds the requested lease length. If not found, 
  839. *      choose the resource with the largest maximum lease length.
  840. *
  841. *      The previous condition excludes resources with an infinite maximum
  842. *      lease whenever possible, unless specifically requested by the client.
  843. *
  844. *      If not found (i.e. - all entries have same maximum lease length), 
  845. *      select the first available entry in the database.
  846. *
  847. * RETURNS: Matching resource, or NULL if none or not available.
  848. *
  849. * ERRNO: N/A
  850. *
  851. * NOMANUAL
  852. */
  853. static struct dhcp_resource * select_newone
  854.     (
  855.     int msgtype,  /* DHCP message type */
  856.     struct client_id *cid,  /* pointer to client ID */
  857.     time_t curr_epoch,  /* current time, in seconds */
  858.     u_long reqlease  /* requested lease duration (seconds) */
  859.     )
  860.     {
  861.     struct dhcp_resource *res = NULL;
  862.     struct dhcp_resource *best = NULL;
  863.     struct hash_member *resptr = NULL;
  864.     /* Examine each resource in list constructed from database. */
  865.     resptr = reslist;
  866.     while (resptr != NULL) 
  867.        {
  868.        res = (struct dhcp_resource *)resptr->data;
  869.        if (res->ip_addr.s_addr == 0)      /* Skip dummy entries. */
  870.            {
  871.            resptr = resptr->next;
  872.            continue;
  873.            }
  874.        /*
  875.         * Check the resource subnet and availability. Skip entries which 
  876.         * are not available or would change the subnet of the client.
  877.         */
  878.        if (cid->subnet.s_addr ==
  879.              (res->ip_addr.s_addr & res->subnet_mask.s_addr) &&
  880.      available_res (res, cid, curr_epoch)) 
  881.            {
  882.            /*
  883.             * choose the best entry from available resources on the same 
  884.             * subnet. The criteria are listed in inverse priority order.
  885.             */
  886.            /* Lowest priority - take first available entry. */
  887.            if (best == NULL) 
  888.                {
  889.        if (icmp_check (msgtype, &res->ip_addr) == GOOD)
  890.            best = res;
  891.                else
  892.            turnoff_bind (res->binding);
  893.        resptr = resptr->next;
  894.        continue;
  895.                }
  896.            /* Select unused entries in preference to used ones. */
  897.            if (best->binding == NULL && res->binding != NULL) 
  898.                {
  899.                resptr = resptr->next;
  900.                continue;
  901.                }
  902.            else if (best->binding != NULL && res->binding == NULL) 
  903.                {
  904.                if (icmp_check (msgtype, &res->ip_addr) == GOOD)
  905.                    best = res;
  906.                else
  907.            turnoff_bind (res->binding);
  908.                resptr = resptr->next;
  909.                continue;
  910.                }
  911.  
  912.            /* Give preference to entries not available to BOOTP clients. */
  913.            if (best->allow_bootp == FALSE && res->allow_bootp == TRUE) 
  914.                {
  915.                resptr = resptr->next;
  916.                continue;
  917.                }
  918.            else if (best->allow_bootp == TRUE && res->allow_bootp == FALSE) 
  919.                {
  920.                if (icmp_check(msgtype, &res->ip_addr) == GOOD)
  921.                    best = res;
  922.                else
  923.            turnoff_bind (res->binding);
  924.                resptr = resptr->next;
  925.                continue;
  926.                }
  927.            /*
  928.             * Tiebreaker conditionals for preferred entries (either both 
  929.             * unused or which both qualify as least recently used). 
  930.             * Overall, these conditionals select the resource whose maximum
  931.             * lease exceeds the threshold of the requested lease length by 
  932.             * the minimum amount. If there is no resource whose maximum lease
  933.             * exceeds the requested lease length, the resource with the
  934.             * largest maximum lease is chosen.
  935.             *
  936.             * NOTE: These conditionals also implement a preference for resource
  937.             *       entries which do not provide an infinite lease, unless
  938.             *       specifically requested by the client.
  939.             */
  940.            if ( (best->binding == NULL && res->binding == NULL) ||
  941.                   (best->binding != NULL && res->binding != NULL &&
  942.                    best->binding->expire_epoch == res->binding->expire_epoch)) 
  943.                {
  944.                /*
  945.                 * Select resource with larger maximum lease,
  946.                 * even if shorter than the requested lease length.
  947.                 * Combined with the third conditional, the maximum value
  948.                 * of the maximum lease will be selected, if the lease length
  949.                 * threshold is not exceeded.
  950.                 */
  951.                if (reqlease >= res->max_lease && 
  952.                    res->max_lease > best->max_lease) 
  953.                    {
  954.                    if (icmp_check (msgtype, &res->ip_addr) == GOOD)
  955.                        best = res;
  956.                    else
  957.                turnoff_bind (res->binding);
  958.                    resptr = resptr->next;
  959.                    continue;
  960.                    }
  961.                /*
  962.                 * Among resources whose maximum lease exceeds
  963.                 * the requested value, select the minimum.
  964.                 * This condition is never true until either the 
  965.                 * previous or next condition evaluated to true.
  966.                 */
  967.                if (reqlease != INFINITY && reqlease <= res->max_lease &&
  968.                    res->max_lease < best->max_lease) 
  969.                    {
  970.                    if (icmp_check (msgtype, &res->ip_addr) == GOOD)
  971.                        best = res;
  972.                    else
  973.                turnoff_bind (res->binding);
  974.                    resptr = resptr->next;
  975.                    continue;
  976.                    }
  977.                 /*
  978.                  * Accept entries longer than both the requested value 
  979.                  * and the current maximum. This condition evaluates to
  980.                  * true at most once. Once it does, neither it or the
  981.                  * first conditional will ever evaluate to true again.
  982.                  */
  983.                if (reqlease != INFINITY && res->max_lease >= reqlease &&
  984.                    reqlease > best->max_lease) 
  985.                    {
  986.                    if (icmp_check (msgtype, &res->ip_addr) == GOOD)
  987.                        best = res;
  988.                    else
  989.                turnoff_bind (res->binding);
  990.                    resptr = resptr->next;
  991.                    continue;
  992.                    }
  993.                resptr = resptr->next;
  994.                continue;
  995.                }
  996.  
  997.            /*
  998.             * Among previously used entries, select those which expired 
  999.             * earlier. (In the aggregate, implements a LRU algorithm).
  1000.             */
  1001.            if (best->binding != NULL && res->binding != NULL &&
  1002.                best->binding->expire_epoch > res->binding->expire_epoch) 
  1003.                {
  1004.                if (icmp_check (msgtype, &res->ip_addr) == GOOD)
  1005.                    best = res;
  1006.                else
  1007.                    turnoff_bind (res->binding);
  1008.                resptr = resptr->next;
  1009.                continue;
  1010.                } 
  1011.            else
  1012.                {
  1013.        resptr = resptr->next;
  1014.        continue;
  1015.                }
  1016.            }
  1017.         resptr = resptr->next;
  1018.         }
  1019.     return (best);
  1020.     }
  1021. /*******************************************************************************
  1022. *
  1023. * choose_res - select a resource for DHCP client
  1024. *
  1025. * This routine chooses a resource in response to an incoming DHCP discover 
  1026. * message. If the server database contains a manual entry matching the
  1027. * client identifier, the corresponding resource is returned. Otherwise,
  1028. * the database entry which provides the requested IP address, if any, is 
  1029. * chosen. If the discover message does not require a specific entry, the
  1030. * server selects a new entry using its own internal criteria, or NULL if
  1031. * no entry is available.
  1032. *
  1033. * RETURNS: Matching resource, or NULL if none available.
  1034. *
  1035. * ERRNO: N/A
  1036. *
  1037. * NOMANUAL
  1038. */
  1039. static struct dhcp_resource * choose_res
  1040.     (
  1041.     struct client_id *cid,  /* pointer to client ID */
  1042.     time_t curr_epoch,  /* current time, in seconds */
  1043.     u_long reqlease  /* requested lease duration (seconds) */
  1044.     )
  1045.     {
  1046.     struct dhcp_resource *res = NULL;
  1047.     /* 1. select with client identifier, if  found. */
  1048.     if ( (res = select_wcid (DHCPDISCOVER, cid, curr_epoch)) != NULL)
  1049.         return (res);
  1050.     /* 2. select with requested IP address, if any. */
  1051.     if ( (res = select_wreqip (DHCPDISCOVER, cid, curr_epoch)) != NULL)
  1052.         return (res);
  1053.     /* 3. select an entry using internal criteria. */
  1054.     res = select_newone (DHCPDISCOVER, cid, curr_epoch, reqlease);
  1055.     if (res != NULL)
  1056.         return (res);
  1057. #ifdef DHCPS_DEBUG
  1058.     logMsg ("Warning: DHCPDISCOVER - No available addresses in the pool.n",
  1059.              0, 0, 0, 0, 0, 0);
  1060. #endif
  1061.     return (NULL);
  1062.     }
  1063. /*******************************************************************************
  1064. *
  1065. * update_db - add entries to internal data structures to reflect client state
  1066. *
  1067. * This routine updates the binding list and corresponding hash tables when
  1068. * the server receives a DHCP discover message, or a DHCP or BOOTP request
  1069. * message from a client. The binding entry for the resource is marked
  1070. * unavailable for a short interval (for DHCP discover) or until the expiration
  1071. * of the lease.
  1072. *
  1073. * RETURNS: 0 if update completed, or -1 on error.
  1074. *
  1075. * ERRNO: N/A
  1076. *
  1077. * NOMANUAL
  1078. */
  1079. static int update_db
  1080.     (
  1081.     int msgtype,  /* DHCP message type */
  1082.     struct client_id *cid,  /* pointer to client ID */
  1083.     struct dhcp_resource *res,  /* pointer to lease descriptor */
  1084.     u_long lease,  /* lease duration, in seconds */
  1085.     time_t curr_epoch  /* current time, in seconds */
  1086.     )
  1087.     {
  1088.     struct dhcp_binding *binding = NULL;
  1089.     /*
  1090.      * Ignore lease descriptors already offered (res->binding != NULL)
  1091.      * if also reserved to specific clients (STATIC_ENTRY flag set).
  1092.      */
  1093.     if (res->binding != NULL && (res->binding->flag & STATIC_ENTRY) != 0)
  1094.         return (0);
  1095.     /* Remove old client identifier association from prior lease record. */
  1096.     if (res->binding != NULL) 
  1097.         hash_del (&cidhashtable, res->binding->cid.id, res->binding->cid.idlen,
  1098.           bindcidcmp, &res->binding->cid, free_bind);
  1099.     /* Create and assign new lease record entry. */
  1100.     binding = (struct dhcp_binding *)calloc (1, sizeof (struct dhcp_binding));
  1101.     if (binding == NULL) 
  1102.         {
  1103. #ifdef DHCPS_DEBUG
  1104.         logMsg ("Warning: memory allocation error updating database.n", 
  1105.                  0, 0, 0, 0, 0, 0);
  1106. #endif
  1107.         return (-1);
  1108.         }
  1109.     if (cidcopy (cid, &binding->cid) != 0)
  1110.         return (-1);
  1111.     if (msgtype == DHCPDISCOVER) 
  1112.         binding->temp_epoch = curr_epoch + MEMORIZE;
  1113.     else if (lease == 0xffffffff) 
  1114.         binding->expire_epoch = 0xffffffff;
  1115.     else 
  1116.         binding->expire_epoch = curr_epoch + lease;
  1117.     /* Link lease record and lease descriptor. */
  1118.     binding->res = res;
  1119.     bcopy (res->entryname, binding->res_name, strlen (res->entryname));
  1120.     binding->res_name [strlen (res->entryname)] = '';
  1121.     res->binding = binding;
  1122.     /* Record client hardware address. */
  1123.     binding->haddr.htype = dhcpsMsgIn.dhcp->htype;
  1124.     binding->haddr.hlen = dhcpsMsgIn.dhcp->hlen;
  1125.     if (binding->haddr.hlen > MAX_HLEN)
  1126.         binding->haddr.hlen = MAX_HLEN;
  1127.     bcopy (dhcpsMsgIn.dhcp->chaddr, binding->haddr.haddr, binding->haddr.hlen);
  1128.     /* Add association of lease record and client identifier. */
  1129.     if ( (hash_ins (&cidhashtable, binding->cid.id, binding->cid.idlen, 
  1130.                     bindcidcmp, &binding->cid, binding) < 0)) 
  1131.         {
  1132. #ifdef DHCPS_DEBUG
  1133.         logMsg ("Warning: hash table insertion with client ID failed.n",
  1134.                  0, 0, 0, 0, 0, 0);
  1135. #endif
  1136.         return (-1);
  1137.         }
  1138.     /* Store record of lease. */
  1139.     if (add_bind (binding) != 0)
  1140.         return (-1);
  1141.     return (0);
  1142.     }
  1143. /*******************************************************************************
  1144. *
  1145. * turnoff_bind - mark resource entry as unavailable
  1146. *
  1147. * This routine updates the binding list and corresponding hash tables when
  1148. * the server discovers (through an ICMP check or client decline) that an IP 
  1149. * address is unexpectedly in use. The corresponding resource is marked as an 
  1150. * active lease for the next half-hour.
  1151. *
  1152. * RETURNS: N/A
  1153. *
  1154. * ERRNO: N/A
  1155. *
  1156. * NOMANUAL
  1157. */
  1158. static void turnoff_bind
  1159.     (
  1160.     struct dhcp_binding *binding  /* unavailable lease record */
  1161.     )
  1162.     {
  1163.     time_t curr_epoch = 0;
  1164.     int result;
  1165.     if (binding == NULL)
  1166.         return;
  1167.     if (dhcpTime (&curr_epoch) == -1) 
  1168.         {
  1169. #ifdef DHCPS_DEBUG
  1170.         logMsg ("Warning: turnoff_bind() can't retrieve current time.n",
  1171.                  0, 0, 0, 0, 0, 0);
  1172. #endif
  1173.         return;
  1174.         }
  1175.     /* Remove client ID from hash table entry for address in use. */
  1176.     binding->expire_epoch = binding->temp_epoch = curr_epoch + 1800;
  1177.     hash_del (&cidhashtable, binding->cid.id, binding->cid.idlen, bindcidcmp,
  1178.       &binding->cid, free_fake);
  1179.     bzero (binding->cid.id, binding->cid.idlen);
  1180.     result = hash_ins (&cidhashtable, binding->cid.id, binding->cid.idlen, 
  1181.                        bindcidcmp, &binding->cid, binding);
  1182. #ifdef DHCPS_DEBUG
  1183.     if (result < 0) 
  1184.         logMsg ("Warning: couldn't alter hash table in turnoff_bind()", 
  1185.                  0, 0, 0, 0, 0, 0);
  1186. #endif
  1187.     binding->flag &= ~COMPLETE_ENTRY;
  1188.     return;
  1189.     }
  1190. /*******************************************************************************
  1191. *
  1192. * clean_sbuf - clean the message transmission buffers
  1193. *
  1194. * This routine clears the vectored buffers used to store outgoing DHCP 
  1195. * messages. The first buffer contains the Ethernet, IP and UDP headers, as well
  1196. * as the fixed-length portion of the DHCP message and the options which fit 
  1197. * within the default (312 byte) option field. The second buffer contains 
  1198. * overflow options, if any, for clients capable of receiving DHCP messages 
  1199. * longer than the default 548 bytes.
  1200. *
  1201. * RETURNS: N/A
  1202. *
  1203. * ERRNO: N/A
  1204. *
  1205. * NOMANUAL
  1206. */
  1207. static void clean_sbuf (void)
  1208.     {
  1209.     bzero (sbufvec[0].iov_base, sbufvec[0].iov_len);
  1210.     bzero (sbufvec[1].iov_base, sbufvec[1].iov_len);
  1211.     sbufvec[1].iov_len = 0;
  1212.     return;
  1213.     }
  1214. /*******************************************************************************
  1215. *
  1216. * construct_msg - make an outgoing DHCP message
  1217. *
  1218. * This routine creates all DHCP server responses to client requests, according
  1219. * to the behavior specified in RFC 1541. The message type parameter indicates
  1220. * whether to build a DHCP offer message, a DHCP ACK message, or a NAK reply.
  1221. *
  1222. * RETURNS: N/A
  1223. *
  1224. * ERRNO: N/A
  1225. *
  1226. * NOMANUAL
  1227. */
  1228. static void construct_msg
  1229.     (
  1230.     u_char msgtype,  /* type of DHCP message to construct */
  1231.     struct dhcp_resource *res,  /* lease descriptor describing contents */
  1232.     u_long lease,  /* lease duration, in seconds */
  1233.     struct if_info *ifp  /* descriptor of receiving interface */
  1234.     )
  1235.     {
  1236.     int i = 0;
  1237.     int reqoptlen = 0;
  1238.     u_long tmp = 0;
  1239.     char *reqopt = NULL;
  1240.     char inserted[32];
  1241.     char *option = NULL;
  1242.     struct client_id   paramId;   /* Key for additional parameters. */
  1243.     struct dhcp_resource * params;   /* Client- or class-specific options. */
  1244.     int result;
  1245.     bzero (inserted, sizeof (inserted));
  1246.     clean_sbuf ();            /* Zero out outgoing message buffer. */
  1247.     dhcpsMsgOut.dhcp->op = BOOTREPLY;
  1248.     dhcpsMsgOut.dhcp->htype = dhcpsMsgIn.dhcp->htype;
  1249.     dhcpsMsgOut.dhcp->hlen = dhcpsMsgIn.dhcp->hlen;
  1250.     dhcpsMsgOut.dhcp->hops = 0;
  1251.     dhcpsMsgOut.dhcp->xid = dhcpsMsgIn.dhcp->xid;
  1252.     dhcpsMsgOut.dhcp->secs = 0;
  1253.     dhcpsMsgOut.dhcp->flags = dhcpsMsgIn.dhcp->flags;
  1254.     dhcpsMsgOut.dhcp->giaddr.s_addr = dhcpsMsgIn.dhcp->giaddr.s_addr;
  1255.     bcopy (dhcpsMsgIn.dhcp->chaddr, dhcpsMsgOut.dhcp->chaddr,
  1256.            dhcpsMsgIn.dhcp->hlen);
  1257.     if (msgtype == DHCPACK)   /* ciaddr stays zero for all other types. */
  1258.         dhcpsMsgOut.dhcp->ciaddr.s_addr = dhcpsMsgIn.dhcp->ciaddr.s_addr;
  1259.     if (msgtype != DHCPNAK) 
  1260.         {
  1261.         dhcpsMsgOut.dhcp->yiaddr.s_addr = res->ip_addr.s_addr;
  1262.         if (ISSET (res->valid, S_SIADDR)) 
  1263.             {
  1264.             dhcpsMsgOut.dhcp->siaddr.s_addr = res->siaddr.s_addr;
  1265.             } 
  1266.         else 
  1267.             {
  1268.             dhcpsMsgOut.dhcp->siaddr.s_addr = 0;
  1269.             }
  1270.         overload = BOTH_AREOPT;
  1271.         if (ISSET (res->valid, S_SNAME)) 
  1272.             {
  1273.             strncpy (dhcpsMsgOut.dhcp->sname, res->sname, MAX_SNAME);
  1274.             dhcpsMsgOut.dhcp->sname [MAX_SNAME - 1] = '';
  1275.             overload -= SNAME_ISOPT;
  1276.             }
  1277.         if (ISSET (res->valid, S_FILE)) 
  1278.             {
  1279.             strncpy (dhcpsMsgOut.dhcp->file, res->file, MAX_FILE);
  1280.             dhcpsMsgOut.dhcp->file [MAX_FILE - 1] = '';
  1281.             overload -= FILE_ISOPT;
  1282.             }
  1283.         } 
  1284.     else 
  1285.         {
  1286.         dhcpsMsgOut.dhcp->yiaddr.s_addr = 0;
  1287.         dhcpsMsgOut.dhcp->siaddr.s_addr = 0;
  1288.         /* Refinement for draft RFC. */
  1289.         /* if (dhcpsMsgIn.giaddr.s_addr != 0)
  1290.             SETBRDCAST (dhcpsMsgOut.dhcp->flags); */
  1291.         }
  1292.     /* insert magic cookie */
  1293.     bcopy ((char *)dhcpCookie, dhcpsMsgOut.dhcp->options, MAGIC_LEN);
  1294.     off_options = MAGIC_LEN;
  1295.     off_extopt = 0;
  1296.     /* insert dhcp message type option */
  1297.     dhcpsMsgOut.dhcp->options [off_options++] = _DHCP_MSGTYPE_TAG;
  1298.     dhcpsMsgOut.dhcp->options [off_options++] = 1;
  1299.     dhcpsMsgOut.dhcp->options [off_options++] = msgtype;
  1300.     /* Insert client ID when permitted. (Only allowed under draft RFC). */
  1301.     if (msgtype == DHCPNAK) 
  1302.         {
  1303.         SETBRDCST (dhcpsMsgOut.dhcp->flags);
  1304. /*      option = pickup_opt (dhcpsMsgIn.dhcp, rdhcplen, _DHCP_CLIENT_ID_TAG);
  1305.         if (option != NULL) 
  1306.             {
  1307.             dhcpsMsgOut.dhcp->options [off_options++] = _DHCP_CLIENT_ID_TAG;
  1308.             dhcpsMsgOut.dhcp->options [off_options++] = DHCPOPTLEN(option);
  1309.             bcopy (option, &dhcpsMsgOut.dhcp->options [off_options],
  1310.                    DHCPOPTLEN (option));
  1311.             off_options += DHCPOPTLEN (option);
  1312.             } */
  1313.         return;
  1314.         }
  1315.     /* insert "server identifier" (required). */
  1316.     dhcpsMsgOut.dhcp->options [off_options++] = _DHCP_SERVER_ID_TAG;
  1317.     dhcpsMsgOut.dhcp->options [off_options++] = 4;
  1318.     bcopy ( (char *)&ifp->ipaddr.s_addr, 
  1319.             &dhcpsMsgOut.dhcp->options [off_options], 4);
  1320.     off_options += 4;
  1321.     /* insert "subnet mask" (permitted). */
  1322.     result = insert_opt (res, lease, _DHCP_SUBNET_MASK_TAG, inserted, PASSIVE);
  1323. #ifdef DHCPS_DEBUG
  1324.     if (result == E_NOMORE) 
  1325.         logMsg ("No space left in options field for DHCP%s",
  1326.                  (int)((msgtype == DHCPOFFER) ? "OFFER" : "ACK"), 
  1327.                  0, 0, 0, 0, 0);
  1328. #endif
  1329.     /* insert "lease duration" (required). */
  1330.     tmp = htonl (lease);
  1331.     dhcpsMsgOut.dhcp->options [off_options++] = _DHCP_LEASE_TIME_TAG;
  1332.     dhcpsMsgOut.dhcp->options [off_options++] = 4;
  1333.     bcopy ( (char *)&tmp, &dhcpsMsgOut.dhcp->options [off_options], 
  1334.             sizeof (u_long));
  1335.     off_options += 4;
  1336.     /* Insert "option overload" tag, if needed. */
  1337.     if (overload != 0) 
  1338.         {
  1339.         dhcpsMsgOut.dhcp->options[off_options++] = _DHCP_OPT_OVERLOAD_TAG;
  1340.         dhcpsMsgOut.dhcp->options[off_options++] = 1;
  1341.         dhcpsMsgOut.dhcp->options[off_options++] = overload;
  1342.         }
  1343.     /* insert the requested options */
  1344.     option = pickup_opt(dhcpsMsgIn.dhcp, rdhcplen, _DHCP_REQ_LIST_TAG);
  1345.     if (option != NULL) 
  1346.         {
  1347.         reqopt = OPTBODY (option);
  1348.         reqoptlen = DHCPOPTLEN (option);
  1349.         /* 
  1350.          * Handle requested parameters. The PASSIVE flag only inserts options
  1351.          * explicity configured into the resource entry. (Rule 1 of RFC 1541).
  1352.          * Because the implementation used "tblc=dflt" to force inclusion of
  1353.          * any missing parameters defined in the Host Requirements Document,
  1354.          * the PASSIVE flag will also include those settings if not already 
  1355.          * present. (Rule 2 of RFC 1541).
  1356.          */
  1357.         for (i = 0; i < reqoptlen; i++) 
  1358.             if (ISCLR (inserted, * (reqopt + i))) 
  1359.                 {
  1360.         result = insert_opt (res, lease, * (reqopt + i), inserted, 
  1361.                                      PASSIVE);
  1362.         if (result == E_NOMORE)
  1363.                     {
  1364. #ifdef DHCPS_DEBUG
  1365.             logMsg ("No space left in options field for DHCP%s",
  1366.              (int)((msgtype == DHCPOFFER) ? "OFFER" : "ACK"), 
  1367.                              0, 0, 0, 0, 0);
  1368. #endif
  1369.             break;
  1370.             }
  1371.                 }
  1372.         }
  1373.     /* 
  1374.      * Insert parameters which differ from the Host Requirements RFC defaults.
  1375.      * (The tags for these parameters are preceded by "!" in the server
  1376.      * configuration table).
  1377.      */
  1378.     for (i = 0; i < _DHCP_LAST_OPTION; i++) 
  1379.         if (ISCLR (inserted, i)) 
  1380.             if (insert_opt (res, lease, i, inserted, ACTIVE) == E_NOMORE) 
  1381.                 {
  1382. #ifdef DHCPS_DEBUG
  1383.         logMsg ("No space left in options field for DHCP%s",
  1384.                  (int)((msgtype == DHCPOFFER) ? "OFFER" : "ACK"), 
  1385.                          0, 0, 0, 0, 0);
  1386. #endif
  1387.         break;
  1388.                 }
  1389.     /* Insert any client-specific options. */
  1390.        /* Insert any parameters associated with explicit client identifier. */
  1391.     tmp = 0;
  1392.     option = pickup_opt (dhcpsMsgIn.dhcp, rdhcplen, _DHCP_CLIENT_ID_TAG);
  1393.     if (option != NULL)
  1394.         {
  1395.         paramId.idlen = DHCPOPTLEN (option) - 1;
  1396.         paramId.idtype = *(char *)OPTBODY (option);
  1397.         bcopy (OPTBODY (option) + sizeof (char), paramId.id, paramId.idlen);
  1398.         params = hash_find (&paramhashtable, 
  1399.                             paramId.id, paramId.idlen, paramcidcmp, &paramId);
  1400.         /* Insert options from matching resource entry not already present. */
  1401.         if (params != NULL)
  1402.             {
  1403.             for (i = 0; i < _DHCP_LAST_OPTION; i++)
  1404.                 if (ISCLR (inserted, i))
  1405.                     if (insert_opt (params, lease, i, inserted, PASSIVE) 
  1406.                            == E_NOMORE)
  1407.                         {
  1408. #ifdef DHCPS_DEBUG
  1409.                         logMsg ("No space left in options field for DHCP%s",
  1410.                                (int)((msgtype == DHCPOFFER) ? "OFFER" : "ACK"),
  1411.                                0, 0, 0, 0, 0);
  1412. #endif
  1413.                         break;
  1414.                         }
  1415.             tmp = 1;       /* Client-specific options found. */
  1416.             }
  1417.         }
  1418.     /*
  1419.      * If no client ID included, or no associated options found, check 
  1420.      * hardware address. 
  1421.      */
  1422.     if (tmp == 0)
  1423.         {
  1424.         paramId.idlen = dhcpsMsgIn.dhcp->hlen;
  1425.         paramId.idtype = dhcpsMsgIn.dhcp->htype;
  1426.         bcopy (dhcpsMsgIn.dhcp->chaddr, paramId.id, dhcpsMsgIn.dhcp->hlen);
  1427.         params = hash_find (&paramhashtable,
  1428.                             paramId.id, paramId.idlen, paramcidcmp, &paramId);
  1429.         /* Insert options from matching resource entry not already present. */
  1430.         if (params != NULL)
  1431.             {
  1432.             for (i = 0; i < _DHCP_LAST_OPTION; i++)
  1433.                 if (ISCLR (inserted, i))
  1434.                     if (insert_opt (params, lease, i, inserted, PASSIVE)
  1435.                            == E_NOMORE)
  1436.                         {
  1437. #ifdef DHCPS_DEBUG
  1438.                         logMsg ("No space left in options field for DHCP%s",
  1439.                                (int)((msgtype == DHCPOFFER) ? "OFFER" : "ACK"),
  1440.                                0, 0, 0, 0, 0);
  1441. #endif
  1442.                         break;
  1443.                         }
  1444.             }
  1445.         }
  1446.     /* Insert any class-specific options. */
  1447.     option = pickup_opt (dhcpsMsgIn.dhcp, rdhcplen, _DHCP_CLASS_ID_TAG);
  1448.     if (option != NULL)
  1449.         {
  1450.         paramId.idlen = DHCPOPTLEN (option);
  1451.         paramId.idtype = 0;         /* Unused for class identifiers. */
  1452.         bcopy (OPTBODY (option), paramId.id, paramId.idlen);
  1453.         params = hash_find (&paramhashtable,
  1454.                             paramId.id, paramId.idlen, paramcidcmp, &paramId);
  1455.         /* Insert options from matching resource entry not already present. */
  1456.         if (params != NULL)
  1457.             {
  1458.             for (i = 0; i < _DHCP_LAST_OPTION; i++)
  1459.                 if (ISCLR (inserted, i))
  1460.                     if (insert_opt (params, lease, i, inserted, PASSIVE)
  1461.                            == E_NOMORE)
  1462.                         {
  1463. #ifdef DHCPS_DEBUG
  1464.                         logMsg ("No space left in options field for DHCP%s",
  1465.                                (int)((msgtype == DHCPOFFER) ? "OFFER" : "ACK"),
  1466.                                0, 0, 0, 0, 0);
  1467. #endif
  1468.                         break;
  1469.                         }
  1470.             }
  1471.         }
  1472.     return;
  1473.     }
  1474. /*******************************************************************************
  1475. *
  1476. * select_wciaddr - retrieve resource with matching IP address
  1477. *
  1478. * This routine attempts to find an address pool entry whose IP address matches
  1479. * the value requested by the client. If the matching IP address is part of a
  1480. * manual lease, it also verifies that the client ID matches the required value.
  1481. * Otherwise, it checks if the requesting client received an offer from the
  1482. * server.
  1483. *
  1484. * RETURNS: Matching resource, or NULL if none or not available.
  1485. *
  1486. * ERRNO: N/A
  1487. *
  1488. * NOMANUAL
  1489. */
  1490. /*
  1491.  * choose resource with ciaddr
  1492.  */
  1493. static struct dhcp_resource * select_wciaddr
  1494.     (
  1495.     struct client_id *cid,  /* pointer to identifier of request client */
  1496.     time_t curr_epoch,  /* current time, in seconds */
  1497.     int *nosuchaddr  /* flag indicating if address foun in table */
  1498.     )
  1499.     {
  1500.     struct dhcp_resource *res = NULL;
  1501. #ifdef DHCPS_DEBUG
  1502.     char tmp [INET_ADDR_LEN];
  1503. #endif
  1504.     *nosuchaddr = FALSE;
  1505.     res = (struct dhcp_resource *)hash_find (&iphashtable, 
  1506.                                       (char *)&dhcpsMsgIn.dhcp->ciaddr.s_addr,
  1507.                                              sizeof (u_long), resipcmp, 
  1508.                                              &dhcpsMsgIn.dhcp->ciaddr);
  1509.     if (res == NULL) 
  1510.         {
  1511.         *nosuchaddr = TRUE;   /* Fatal error - expected entry not found. */
  1512.         return (NULL);
  1513.         } 
  1514.     else 
  1515.         {
  1516.         /* Check for subnet match. */
  1517.         if (cid->subnet.s_addr != 
  1518.              (res->ip_addr.s_addr & res->subnet_mask.s_addr)) 
  1519.             {
  1520. #ifdef DHCPS_DEBUG
  1521.             inet_ntoa_b (dhcpsMsgIn.dhcp->ciaddr, tmp);
  1522.             logMsg ("Subnet mismatch for DHCPREQUEST (cid: %s, ciaddr: %s).n",
  1523.                      (int)cidtos (cid, 1), (int)tmp, 0, 0, 0, 0);
  1524. #endif
  1525.             return (NULL);
  1526.             }
  1527.         else if (ISSET(res->valid, S_CLIENT_ID))    /* Manual allocation. */
  1528.             {
  1529.             /* Fatal error if no binding present for manual allocation. */
  1530.             if (res->binding == NULL) 
  1531.                 {
  1532. #ifdef DHCPS_DEBUG
  1533.                 inet_ntoa_b (dhcpsMsgIn.dhcp->ciaddr, tmp);
  1534.         logMsg ("DHCPREQUEST(cid:"%s"): No binding for %s",
  1535.                          (int)cidtos(cid, 1), (int)tmp, 0, 0, 0, 0);
  1536. #endif
  1537.         *nosuchaddr = TRUE;
  1538.         return (NULL);
  1539.                 }
  1540.             /* Check that client ID of binding matches requesting client. */
  1541.             else if (res->binding->cid.idtype != cid->idtype ||
  1542.              res->binding->cid.idlen != cid->idlen ||
  1543.              bcmp (res->binding->cid.id, cid->id, cid->idlen) != 0) 
  1544.                     {
  1545. #ifdef DHCPS_DEBUG
  1546.                     inet_ntoa_b (dhcpsMsgIn.dhcp->ciaddr, tmp);
  1547.             logMsg (
  1548.                   "DHCPREQUEST(cid:"%s", ciaddr:%s) - client ID mismatch.n",
  1549.                                (int)cidtos(cid, 1), (int)tmp, 0, 0, 0, 0);
  1550. #endif
  1551.             return (NULL);
  1552.                     }
  1553.             }
  1554.         /* Dynamic or automatic allocation. Fails if no binding present
  1555.          * (i.e. - unknown request), or lease has expired (i.e. - late
  1556.          * request), or if client ID doesn't match expected value from
  1557.          * the DHCP offer message.
  1558.          */
  1559.         else if (res->binding == NULL ||
  1560.          (res->binding->expire_epoch != 0xffffffff &&
  1561.           res->binding->expire_epoch <= curr_epoch) ||
  1562.          res->binding->cid.idtype != cid->idtype ||
  1563.          res->binding->cid.idlen != cid->idlen ||
  1564.          bcmp (res->binding->cid.id, cid->id, cid->idlen) != 0) 
  1565.                  {
  1566. #ifdef DHCPS_DEBUG
  1567.                  inet_ntoa_b (dhcpsMsgIn.dhcp->ciaddr, tmp);
  1568.                  logMsg ("DHCPREQUEST(cid:"%s",ciaddr:%s): is unavailable.n",
  1569.                   (int)cidtos(cid, 1), (int)tmp, 0, 0, 0, 0);
  1570. #endif
  1571.                  return (NULL);
  1572.                  }
  1573.         }
  1574.     return (res);
  1575.     }
  1576. /*******************************************************************************
  1577. *
  1578. * discover - handle client discover messages
  1579. *
  1580. * This routine examines client discover messages, selects an available 
  1581. * resource (if any), calculates the length of the lease, and sends the 
  1582. * appropriate offer to the client.
  1583. *
  1584. * RETURNS: 0 if processing successful, or -1 on error.
  1585. *
  1586. * ERRNO: N/A
  1587. *
  1588. * NOMANUAL
  1589. */
  1590. static int discover
  1591.     (
  1592.     struct if_info *ifp    /* pointer to descriptor of receiving interface */
  1593.     )
  1594.     {
  1595.     struct dhcp_resource *offer_res = NULL; /* lease descriptor chosen */
  1596.     struct client_id cid;   /* ID of requesting client */
  1597.     u_long offer_lease = 0;  /* offered lease duration */
  1598.     u_long reqlease = 0;  /* requested lease duration */
  1599.     time_t curr_epoch = 0;  /* current time, in seconds */
  1600.     int result;  /* error value, if any */
  1601.     bzero ((char *)&cid, sizeof (cid));
  1602.     if (dhcpTime (&curr_epoch) == -1) 
  1603.         {
  1604. #ifdef DHCPS_DEBUG
  1605.         logMsg ("Warning: discover() couldn't retrieve current time.n",
  1606.                  0, 0, 0, 0, 0, 0);
  1607. #endif
  1608.         return (-1);
  1609.         }
  1610.     /* Extract requested lease from DHCP message. */
  1611.     reqlease = get_reqlease (dhcpsMsgIn.dhcp, rdhcplen);
  1612.     /* Determine maximum length available in DHCP message for options. */
  1613.     maxoptlen = get_maxoptlen (dhcpsMsgIn.dhcp, rdhcplen);
  1614.     /* Set pointers to access client ID within DHCP message options field. */
  1615.     get_cid (dhcpsMsgIn.dhcp, rdhcplen, &cid);
  1616.     /* Critical section with dhcpsLeaseEntryAdd(). */
  1617.     semTake (dhcpsMutexSem, WAIT_FOREVER);
  1618.     /* Retrieve subnet for incoming message. */
  1619.     if (get_subnet (dhcpsMsgIn.dhcp, rdhcplen, &cid.subnet, ifp) != 0)
  1620.         { 
  1621.         semGive (dhcpsMutexSem);
  1622.         return (-1);
  1623.         }
  1624.     /* Select an available lease descriptor. */
  1625.     if ( (offer_res = choose_res (&cid, curr_epoch, reqlease)) == NULL)
  1626.         {
  1627.         semGive (dhcpsMutexSem);
  1628.         return (-1);
  1629.         }
  1630.     /* Select a lease duration. */
  1631.     offer_lease = choose_lease (reqlease, curr_epoch, offer_res);
  1632.     /* Record lease offer in data structures. */
  1633.     result = update_db (DHCPDISCOVER, &cid, offer_res, offer_lease, curr_epoch);
  1634.     semGive (dhcpsMutexSem);
  1635.     if (result != 0)
  1636.         return (-1);
  1637.     /* Create outgoing DHCP offer message. */
  1638.     construct_msg (DHCPOFFER, offer_res, offer_lease, ifp);
  1639.     /*
  1640.      * xxx must be able to handle the fragments, but currently not implemented
  1641.      */
  1642.     /* Transfer message to receiving interface. */
  1643.     send_dhcp (ifp, DHCPOFFER);
  1644.     return (0);
  1645.     }
  1646. /*******************************************************************************
  1647. *
  1648. * request - handle client request messages
  1649. *
  1650. * This routine responds to request messages sent by clients in response
  1651. * to an offer from a DHCP server. If the server generated the offer, it
  1652. * sends the appropriate ACK or NAK message. Otherwise, it updates the 
  1653. * internal data structure to reflect the implicit decline from the client.
  1654. *
  1655. * RETURNS: 0 if processing successful, or -1 on error.
  1656. *
  1657. * ERRNO: N/A
  1658. *
  1659. * NOMANUAL
  1660. */
  1661. static int request
  1662.     (
  1663.     struct if_info *ifp    /* pointer to descriptor of receiving interface */
  1664.     )
  1665.     {
  1666.     BOOL reqforme = FALSE;
  1667.     BOOL nosuchaddr = FALSE;
  1668.     struct dhcp_resource *res = NULL;
  1669.     struct client_id cid;
  1670.     struct in_addr reqip;
  1671.     struct in_addr netmask;
  1672.     unsigned long offer_lease = 0;         /* offering lease */
  1673.     unsigned long reqlease = 0;            /* requested lease duration */
  1674.     char *option = NULL;
  1675.     int response;                 /* Accept request? */
  1676. #define EPOCH      "Thu Jan  1 00:00:00 1970n"
  1677. #define BRDCSTSTR  "255.255.255.255"
  1678.     char datestr [sizeof (EPOCH)];
  1679.     char addrstr [sizeof (BRDCSTSTR)];
  1680.     time_t curr_epoch = 0;                 /* current epoch */
  1681.     bzero ((char *)&cid, sizeof (cid));
  1682.     bcopy (EPOCH, datestr, sizeof (EPOCH));
  1683.     bcopy (BRDCSTSTR, addrstr, sizeof (BRDCSTSTR));
  1684.     response = 2;            /* Response not determined. */
  1685.     if (dhcpTime (&curr_epoch) == -1)
  1686.         {
  1687. #ifdef DHCPS_DEBUG
  1688.         logMsg ("Warning: Couldn't get timestamp when processing request.n",
  1689.                  0, 0, 0, 0, 0, 0);
  1690. #endif
  1691.         return (-1);
  1692.         }
  1693.     reqlease = get_reqlease (dhcpsMsgIn.dhcp, rdhcplen);
  1694.     maxoptlen = get_maxoptlen (dhcpsMsgIn.dhcp, rdhcplen);
  1695.     get_cid (dhcpsMsgIn.dhcp, rdhcplen, &cid);
  1696.     /* Critical section with dhcpsLeaseEntryAdd(). */
  1697.     semTake (dhcpsMutexSem, WAIT_FOREVER);
  1698.     if (get_subnet (dhcpsMsgIn.dhcp, rdhcplen, &cid.subnet, ifp) != 0)
  1699.         { 
  1700.         semGive (dhcpsMutexSem);
  1701.         return (-1);
  1702.         }
  1703.     /*
  1704.      * Check if this DHCP server is the request destination. 
  1705.      * (Option not present unless client is in SELECTING state).  
  1706.      */
  1707.     option = pickup_opt (dhcpsMsgIn.dhcp, rdhcplen, _DHCP_SERVER_ID_TAG);
  1708.     if (option != NULL) 
  1709.         if (htonl (GETHL (OPTBODY (option))) == ifp->ipaddr.s_addr)
  1710.             reqforme = TRUE;
  1711.     /*
  1712.      * Check the previously allocated network address sent by client
  1713.      * (i.e. - client is in RENEWING or REBINDING state). 
  1714.      */ 
  1715.     if (dhcpsMsgIn.dhcp->ciaddr.s_addr != 0) 
  1716.         {
  1717.         /* For client in RENEWING state, no relay agents are used. */
  1718.         
  1719.         if (get_snmk (dhcpsMsgIn.dhcp, rdhcplen, &netmask, ifp) == 0) 
  1720.             if (dhcpsMsgIn.dhcp->giaddr.s_addr != 0) 
  1721.                 /* REBINDING state - check network of client. */
  1722.         if ( (dhcpsMsgIn.dhcp->giaddr.s_addr & netmask.s_addr) !=
  1723.              (dhcpsMsgIn.dhcp->ciaddr.s_addr & netmask.s_addr))
  1724.             /* goto nak;   */               /* Different subnet. */
  1725.                     response = 0;          /* Send NAK to client. */
  1726.         
  1727.         if (response != 0)    /* Response still not determined. */
  1728.             {
  1729.             res = select_wciaddr (&cid, curr_epoch, &nosuchaddr);
  1730.             if (res == NULL) 
  1731.                 {
  1732.                 /* 
  1733.                  * If no entry present or manual entry present without a 
  1734.                  * matching binding the request is meant for another server
  1735.                  * (for renewal request) or cannot be satisfied (for rebinding).
  1736.                  */
  1737.                 if (nosuchaddr == TRUE)
  1738.                     {
  1739.                     semGive (dhcpsMutexSem);
  1740.                     return (-1);
  1741.                     }
  1742.                 /* deny request for subnet mismatch, client ID mismatch,
  1743.                  * expired lease, or missing binding for non-manual allocation.
  1744.                  * (Missing binding means no DISCOVER received for request). 
  1745.                  */
  1746.                  else
  1747.                      response = 0;
  1748.                 } 
  1749.             else 
  1750.                 /* goto ack; */
  1751.                 response = 1;             /* Send ACK to client. */
  1752.             }
  1753.         }
  1754.     if (response == 2)       /* Response still undetermined. */
  1755.         {
  1756.         /* Requesting client has no IP address (i.e. - initial request
  1757.          * from SELECTING state or verification of cached lease from 
  1758.          * INIT-REBOOT state).
  1759.          */
  1760.         reqip.s_addr = 0;
  1761.         option = pickup_opt (dhcpsMsgIn.dhcp, rdhcplen, 
  1762.                              _DHCP_REQUEST_IPADDR_TAG);
  1763.         if (option != NULL)
  1764.             reqip.s_addr = htonl (GETHL (OPTBODY (option)));
  1765.         if (reqip.s_addr != 0) 
  1766.             {
  1767.             if (get_snmk (dhcpsMsgIn.dhcp, rdhcplen, &netmask, ifp) == 0) 
  1768.                 {
  1769.                 /*
  1770.                  * Deny request received if client is on the wrong network.
  1771.                  * (Suggested behavior from RFC 1541).
  1772.                  */
  1773.                 if (dhcpsMsgIn.dhcp->giaddr.s_addr != 0) 
  1774.                     {
  1775.                     /* Deny request received from relay agent if requested IP 
  1776.                      * address is not on agent's subnet. 
  1777.                      */
  1778.             if ( (dhcpsMsgIn.dhcp->giaddr.s_addr & netmask.s_addr) !=
  1779.                  (reqip.s_addr & netmask.s_addr))
  1780.                         response = 0;         /* Send NAK to client. */
  1781.                     }
  1782.                 else 
  1783.                     {
  1784.                     /* Deny request received directly if requested IP address
  1785.                      * on different subnet from receiving interface. 
  1786.                      */
  1787.             if ( (ifp->ipaddr.s_addr & netmask.s_addr) !=
  1788.                  (reqip.s_addr & netmask.s_addr))
  1789.                 response = 0;         /* Send NAK to client. */
  1790.                     }
  1791.                 }
  1792.             if (response == 2)          /* Response still undetermined. */
  1793.                 { 
  1794.                 /* Subnets match - look for entry in address pool. */
  1795.                 res = NULL;
  1796.                 res = select_wcid (DHCPREQUEST, &cid, curr_epoch);
  1797.                 if (res != NULL && res->ip_addr.s_addr != 0 &&
  1798.                      res->ip_addr.s_addr == reqip.s_addr)
  1799.                     response = 1;       /* Send ACK to client. */
  1800.                 /* 
  1801.                  * Deny request if offered address is no longer available,
  1802.                  * or if requested address doesn't match offered address.
  1803.                  */
  1804.                 else if (reqforme == TRUE)
  1805.                     /*
  1806.                      * Client's notion of IP address is incorrect -  
  1807.                      * deny the request. (Follows suggested behavior 
  1808.                      * of RFC 1541 for client in SELECTING state).
  1809.                      */
  1810.                     response = 0;       /* Send NAK to client. */
  1811.                 else
  1812.                     /*
  1813.                      * No record of client, or another server was
  1814.                      * selected - ignore request message.
  1815.                      * Implements mandatory behavior from RFC 1541
  1816.                      * for client in INIT-REBOOT state.
  1817.                      */
  1818.                     {
  1819.                     semGive (dhcpsMutexSem);
  1820.                     return (-1);
  1821.                     }
  1822.                 }
  1823.             }
  1824.    
  1825.         /* Ignore message if needed IP address not present. */ 
  1826.         if (response == 2)   /* Don't exit if response determined. */
  1827.             {
  1828.             semGive (dhcpsMutexSem);
  1829.             return (-1);
  1830.             }
  1831.         }
  1832.     if (response == 1)      /* A DHCP server will send ACK to client. */
  1833.         {
  1834.         offer_lease = choose_lease (reqlease, curr_epoch, res);
  1835.  
  1836.         if (update_db (DHCPREQUEST, &cid, res, offer_lease, curr_epoch) != 0) 
  1837.             {
  1838.             if (reqforme == TRUE)  /* Error in selected server's database. */
  1839.                 response = 0;
  1840.             else
  1841.                 {
  1842.                 semGive (dhcpsMutexSem);
  1843.                 return (-1);
  1844.                 }
  1845.             }
  1846.         if (response == 1)      /* Lease selected and stored in database. */
  1847.             {
  1848.             semGive (dhcpsMutexSem);
  1849.             construct_msg (DHCPACK, res, offer_lease, ifp);
  1850.             /* send DHCPACK from the interface
  1851.              * xxx must be able to handle the fragments, but currently not 
  1852.              * implemented.
  1853.              */
  1854.             send_dhcp (ifp, DHCPACK);
  1855.             res->binding->flag |= COMPLETE_ENTRY;    /* binding is complete. */
  1856.             strcpy (datestr, ctime (&res->binding->expire_epoch));
  1857.             datestr [strlen (datestr) - 1] = '';
  1858. #ifdef DHCPS_DEBUG
  1859.             inet_ntoa_b (res->ip_addr, addrstr);
  1860.             logMsg ("Address %s assigned to client(cid: "%s") till "%s".",
  1861.              (int)addrstr, (int)cidtos (&res->binding->cid, 1), 
  1862.                      (int)datestr, 0, 0, 0);
  1863. #endif
  1864.             }
  1865.         }
  1866.     /* Deny request with NAK if client or server error occurred. */
  1867.     if (response == 0)
  1868.         {
  1869.         semGive (dhcpsMutexSem);
  1870.         construct_msg (DHCPNAK, NULL, offer_lease, ifp);
  1871.         /*
  1872.          * send DHCPNAK from the interface
  1873.          * xxx must be able to handle fragments, but currently not implemented
  1874.          */
  1875.         send_dhcp (ifp, DHCPNAK);
  1876.         }
  1877.     return (0);
  1878.     }
  1879. /*******************************************************************************
  1880. *
  1881. * decline - handle client decline messages
  1882. *
  1883. * This routine responds to optional decline messages sent by clients which 
  1884. * have selected a different offer or detected that an offered address is 
  1885. * already in use. If the server generated the corresponding offer, it
  1886. * updates the internal data structure to indicate the address is unavailable.
  1887. * Otherwise, the decline message is ignored.
  1888. *
  1889. * RETURNS: 0 if processing successful, or -1 on error.
  1890. *
  1891. * ERRNO: N/A
  1892. *
  1893. * NOMANUAL
  1894. */
  1895. static int decline
  1896.     (
  1897.     struct if_info *ifp    /* pointer to descriptor of receiving interface */
  1898.     )
  1899.     {
  1900.     struct dhcp_binding *binding = NULL;
  1901.     struct dhcp_resource *res = NULL;
  1902.     struct client_id cid;
  1903.     char *option = NULL;
  1904.     char msg [255];
  1905. #ifdef DHCPS_DEBUG
  1906.     char output [INET_ADDR_LEN];
  1907. #endif
  1908.     bzero ((char *)&cid, sizeof (cid));
  1909.     /* DECLINE for another server */
  1910.     option = pickup_opt (dhcpsMsgIn.dhcp, rdhcplen, _DHCP_SERVER_ID_TAG);
  1911.     if (option == NULL ||
  1912.           htonl (GETHL (OPTBODY (option))) != ifp->ipaddr.s_addr) 
  1913.         return (0);
  1914.     get_cid (dhcpsMsgIn.dhcp, rdhcplen, &cid);
  1915.     /* Critical section with dhcpsLeaseEntryAdd(). */
  1916.     semTake (dhcpsMutexSem, WAIT_FOREVER);
  1917.     if (get_subnet (dhcpsMsgIn.dhcp, rdhcplen, &cid.subnet, ifp) != 0)
  1918.         { 
  1919.         semGive (dhcpsMutexSem);
  1920.         return (-1);
  1921.         }
  1922.     /* search with haddr (haddr has same format as client identifier) */
  1923.     binding = (struct dhcp_binding *)hash_find (&cidhashtable, cid.id, 
  1924.                                                 cid.idlen, bindcidcmp, &cid);
  1925.     if (binding == NULL) 
  1926.         {
  1927. #ifdef DHCPS_DEBUG
  1928.         logMsg ("DHCPDECLINE received from unknown client.n", 
  1929.                  0, 0, 0, 0, 0, 0);
  1930. #endif
  1931.         return (-1);
  1932.         } 
  1933.     else 
  1934.         res = binding->res;
  1935.     /* Remove link between lease descriptor and record of (declined) offer. */
  1936.     if (binding->res != NULL) 
  1937.         turnoff_bind (binding);
  1938.     else 
  1939.         {
  1940. #ifdef DHCPS_DEBUG
  1941.         logMsg ("Received invalid DHCPDECLINE.n", 0, 0, 0, 0, 0, 0);
  1942. #endif
  1943.         semGive (dhcpsMutexSem);
  1944.         return (0);
  1945.         }
  1946.     semGive (dhcpsMutexSem);
  1947. #ifdef DHCPS_DEBUG
  1948.     inet_ntoa_b (binding->res->ip_addr, output);
  1949.     logMsg ("Received DHCP decline for entry %s (IP=%s).n",
  1950.              (int)binding->res->entryname,
  1951.              output, 0, 0, 0, 0);
  1952. #endif
  1953.     option = pickup_opt (dhcpsMsgIn.dhcp, rdhcplen, _DHCP_ERRMSG_TAG);
  1954.     if (option != NULL) 
  1955.         {
  1956.         nvttostr (OPTBODY (option), msg, (int)DHCPOPTLEN (option));
  1957. #ifdef DHCPS_DEBUG
  1958.         if (msg[0] != '') 
  1959.             logMsg ("Client decline message: "%s".n", (int)msg, 
  1960.                     0, 0, 0, 0, 0);
  1961. #endif
  1962.         }
  1963.     return (0);
  1964.     }
  1965. /*******************************************************************************
  1966. *
  1967. * release - handle client release messages
  1968. *
  1969. * This routine responds to release messages sent by clients to relinquish
  1970. * their lease before it expires. If the server holds a dynamic (i.e. - finite)
  1971. * lease for the originating client, it updates the internal data structure
  1972. * to mark the corresponding address pool entry as available.
  1973. *
  1974. * RETURNS: 0 if processing successful, or -1 on error.
  1975. *
  1976. * ERRNO: N/A
  1977. *
  1978. * NOMANUAL
  1979. */
  1980. static int release
  1981.     (
  1982.     struct if_info *ifp    /* pointer to descriptor of receiving interface */
  1983.     )
  1984.     {
  1985.     struct dhcp_binding *binding = NULL;
  1986.     struct dhcp_resource *res = NULL;
  1987.     char *option = NULL;
  1988. #ifdef DHCPS_DEBUG
  1989.     char output [INET_ADDR_LEN];
  1990. #endif
  1991.     /* release for another server */
  1992.     option = pickup_opt (dhcpsMsgIn.dhcp, rdhcplen, _DHCP_SERVER_ID_TAG);
  1993.     if (option == NULL ||
  1994.           htonl (GETHL (OPTBODY (option))) != ifp->ipaddr.s_addr) 
  1995.         return (0);
  1996.     /* Critical section with dhcpsLeaseEntryAdd(). */
  1997.     semTake (dhcpsMutexSem, WAIT_FOREVER);
  1998.     /* search with ciaddr */
  1999.     res = (struct dhcp_resource *)hash_find (&iphashtable, 
  2000.                                        (char *)&dhcpsMsgIn.dhcp->ciaddr.s_addr,
  2001.                              sizeof (u_long), resipcmp, 
  2002.                                              &dhcpsMsgIn.dhcp->ciaddr);
  2003.     semGive (dhcpsMutexSem);
  2004.     if (res == NULL) 
  2005.         {
  2006. #ifdef DHCPS_DEBUG
  2007.         inet_ntoa_b (dhcpsMsgIn.dhcp->ciaddr, output);
  2008.         logMsg ("Received DHCPRELEASE from unknown client (IP:%s).n",
  2009.          (int)output, 0, 0, 0, 0, 0);
  2010. #endif
  2011.         return (-1);
  2012.         }
  2013.     else 
  2014.         binding = res->binding;
  2015.     /* Verify retrieved entry matches requesting non-manual client. */
  2016.     if (binding != NULL && binding->res != NULL && binding->res == res && 
  2017.           ISCLR (binding->res->valid, S_CLIENT_ID) && 
  2018.           binding->haddr.htype == dhcpsMsgIn.dhcp->htype && 
  2019.           binding->haddr.hlen == dhcpsMsgIn.dhcp->hlen &&
  2020.           bcmp (binding->haddr.haddr, dhcpsMsgIn.dhcp->chaddr, 
  2021.                 dhcpsMsgIn.dhcp->hlen) == 0) 
  2022.         binding->expire_epoch = 0;
  2023.     else 
  2024.         {
  2025. #ifdef DHCPS_DEBUG
  2026.         /* CLIENT_ID option is only present for manual (infinite) leases. */
  2027.         inet_ntoa_b (dhcpsMsgIn.dhcp->ciaddr, output);
  2028.         if (binding != NULL && binding->res != NULL &&
  2029.               ISSET (binding->res->valid, S_CLIENT_ID)) 
  2030.             logMsg ("DHCPRELEASE received for static entry(IP:%s).n",
  2031.              (int)output, 0, 0, 0, 0, 0);
  2032.         else 
  2033.             /* Hardware address mismatch with requesting client. */
  2034.             logMsg ("Received invalid DHCPRELEASE from client(IP:%s).n",
  2035.                      (int)output, 0, 0, 0, 0, 0);
  2036. #endif
  2037.         return (0);
  2038.         }
  2039.     return (0);
  2040.     }
  2041. /*******************************************************************************
  2042. *
  2043. * send_dhcp - transmit a server response
  2044. *
  2045. * This routine sends messages formulated by an earlier construct_msg() call
  2046. * directly to the destination address, if available, or as a broadcast to 
  2047. * the client's subnet.
  2048. *
  2049. * RETURNS: 0 if message sent, or -1 on error.
  2050. *
  2051. * ERRNO: N/A
  2052. *
  2053. * NOMANUAL
  2054. */
  2055. static int send_dhcp
  2056.     (
  2057.     struct if_info *ifp,  /* descriptor of transmission interface */
  2058.     int msgtype  /* type of DHCP message */
  2059.     )
  2060.     {
  2061.     int msglen = 0;
  2062.     char devName [10];
  2063.     struct iovec bufvec[2];
  2064.     struct sockaddr_in dst;
  2065.     BOOL bcastFlag;
  2066.     int result;
  2067.     struct msghdr msg;
  2068.     struct ifnet *pIf;
  2069.     bzero ((char *)&dst, sizeof (dst));
  2070.     dst.sin_len = sizeof (struct sockaddr_in);
  2071.     dst.sin_family = AF_INET;
  2072.     if (overload & FILE_ISOPT) 
  2073.         dhcpsMsgOut.dhcp->file[off_file] = _DHCP_END_TAG;
  2074.     if (overload & SNAME_ISOPT) 
  2075.         dhcpsMsgOut.dhcp->sname[off_sname] = _DHCP_END_TAG;
  2076.     if (off_options < DFLTOPTLEN) 
  2077.         dhcpsMsgOut.dhcp->options[off_options] = _DHCP_END_TAG;
  2078.     else if (off_extopt > 0 && off_extopt < maxoptlen - DFLTOPTLEN)
  2079.         sbufvec[1].iov_base[off_extopt++] = _DHCP_END_TAG;
  2080.     if (off_extopt < sbufvec[1].iov_len) 
  2081.         sbufvec[1].iov_len = off_extopt;
  2082.     /*
  2083.      * Send message through socket if received from relay agent or client 
  2084.      * on a different subnet than the receiving network interface.
  2085.      */
  2086.     if (dhcpsMsgIn.dhcp->giaddr.s_addr != 0 ||
  2087.         (dhcpsMsgIn.dhcp->ciaddr.s_addr != 0 &&
  2088.          (dhcpsMsgIn.dhcp->ciaddr.s_addr & ifp->subnetmask.s_addr) !=
  2089.          (ifp->ipaddr.s_addr & ifp->subnetmask.s_addr))) 
  2090.         {
  2091.         if (dhcpsMsgIn.dhcp->ciaddr.s_addr != 0) 
  2092.             {
  2093.             dst.sin_port = dhcpc_port;
  2094.             bcopy ( (char *)&dhcpsMsgIn.dhcp->ciaddr, 
  2095.                     (char *)&dst.sin_addr, sizeof (u_long));
  2096.             }
  2097.         else if (dhcpsMsgIn.dhcp->giaddr.s_addr != 0) 
  2098.             {
  2099.             dst.sin_port = dhcps_port;
  2100.             bcopy ( (char *)&dhcpsMsgIn.dhcp->giaddr, 
  2101.                     (char *)&dst.sin_addr, sizeof (u_long));
  2102.             }
  2103.         bufvec[0].iov_base = (char *) dhcpsMsgOut.dhcp;
  2104.         msglen = bufvec[0].iov_len = DFLTDHCPLEN;
  2105.         if (sbufvec[1].iov_len == 0)
  2106.             bufvec[1].iov_base = NULL;
  2107.         else
  2108.             bufvec[1].iov_base = sbufvec[1].iov_base;
  2109.         bufvec[1].iov_len = sbufvec[1].iov_len;
  2110.         msglen += bufvec[1].iov_len;
  2111.         if (setsockopt (dhcpsIntfaceList->fd, SOL_SOCKET, SO_SNDBUF, 
  2112.                         (char *)&msglen, sizeof (msglen)) < 0) 
  2113.             {
  2114. #ifdef DHCPS_DEBUG
  2115.             logMsg ("Warning: Couldn't set transmit buffer size for DHCP.n",
  2116.                      0, 0, 0, 0, 0, 0);
  2117. #endif
  2118.             return (-1);
  2119.             }
  2120.         bzero ((char *)&msg, sizeof (msg));
  2121.         msg.msg_name = (caddr_t) &dst;
  2122.         msg.msg_namelen = sizeof (dst);
  2123.         msg.msg_iov = bufvec;
  2124.         if (bufvec[1].iov_base == NULL)
  2125.             msg.msg_iovlen = 1;
  2126.         else
  2127.             msg.msg_iovlen = 2;
  2128.         if (sendmsg (dhcpsIntfaceList->fd, &msg, 0) < 0) 
  2129.             {
  2130. #ifdef DHCPS_DEBUG
  2131.             logMsg ("Warning: Couldn't send DHCP message.n", 
  2132.                      0, 0, 0, 0, 0, 0);
  2133. #endif
  2134.             return (-1);
  2135.             }
  2136.         return (0);
  2137.         }
  2138.     /*
  2139.      * Message sent by client on same subnet as receiving interface.
  2140.      * The destination may not respond to ARP requests, so the
  2141.      * socket interface is unusable. Build and send the reply within
  2142.      * a complete link-level frame instead.
  2143.      */
  2144.     /* Set destination address and fill pseudo header to calculate checksum */
  2145.     bcopy ( (char *)&ifp->ipaddr.s_addr, 
  2146.             (char *)&dhcpsMsgOut.ip->ip_src, sizeof (u_long));
  2147.     if (dhcpsMsgOut.dhcp->yiaddr.s_addr != 0 &&
  2148.         !ISBRDCST (dhcpsMsgIn.dhcp->flags)) 
  2149.         {
  2150.         /* Send DHCPOFFER and DHCPACK messages to the client via unicast. */
  2151.         dhcpsMsgOut.ip->ip_dst.s_addr = dhcpsMsgOut.dhcp->yiaddr.s_addr;
  2152.         dst.sin_addr.s_addr = dhcpsMsgOut.dhcp->yiaddr.s_addr;
  2153.         bcastFlag = FALSE;
  2154.         }
  2155.     else
  2156.         {
  2157.         /*
  2158.          * Broadcast DHCPOFFER and DHCPACK messages if needed by the client.
  2159.          * Also broadcast all DHCPNAK messages with a non-zero 'giaddr'
  2160.          * field (because the 'yiaddr' field is always zero in this case).
  2161.          */
  2162.         dst.sin_addr.s_addr = dhcpsMsgOut.ip->ip_dst.s_addr = 0xffffffff;
  2163.         bcastFlag = TRUE;
  2164.         }
  2165.     dst.sin_port = dhcpc_port;
  2166.     dhcpsMsgOut.udp->uh_sport = dhcps_port;
  2167.     dhcpsMsgOut.udp->uh_dport = dhcpc_port;
  2168.     dhcpsMsgOut.udp->uh_ulen = htons (off_extopt + DFLTDHCPLEN + UDPHL);
  2169.     dhcpsMsgOut.udp->uh_sum = get_udpsum (dhcpsMsgOut.ip, dhcpsMsgOut.udp);
  2170.     dhcpsMsgOut.ip->ip_v = IPVERSION;
  2171.     dhcpsMsgOut.ip->ip_hl = IPHL >> 2;
  2172.     dhcpsMsgOut.ip->ip_tos = 0;
  2173.     dhcpsMsgOut.ip->ip_len = htons (off_extopt + DFLTDHCPLEN + UDPHL + IPHL);
  2174.     dhcpsMsgOut.ip->ip_id = dhcpsMsgOut.udp->uh_sum;
  2175.     dhcpsMsgOut.ip->ip_off = htons (IP_DF);    /* XXX */
  2176.     dhcpsMsgOut.ip->ip_ttl = 0x20;            /* XXX */
  2177.     dhcpsMsgOut.ip->ip_p = IPPROTO_UDP;
  2178.     dhcpsMsgOut.ip->ip_sum = get_ipsum (dhcpsMsgOut.ip);
  2179.     sprintf (devName, "%s%d", ifp->name, ifp->unit);
  2180.     pIf = ifunit (devName);
  2181.     if (pIf == NULL)
  2182.         {
  2183. #ifdef DHCPS_DEBUG
  2184.         logMsg ("Warning: couldn't access network interface %s.n", 
  2185.                  (int)devName, 0, 0, 0, 0, 0);
  2186. #endif
  2187.         return (-1);
  2188.         }
  2189.     if (sbufvec[1].iov_len == 0) 
  2190.         {
  2191.         result = dhcpsSend (pIf,
  2192.                             dhcpsMsgOut.dhcp->chaddr, dhcpsMsgOut.dhcp->hlen,
  2193.                             &dst, (char *)dhcpsMsgOut.ip, sbufvec[0].iov_len,
  2194.                             bcastFlag);
  2195.         if (result != OK)
  2196.             {
  2197. #ifdef DHCPS_DEBUG
  2198.             logMsg ("Warning: couldn't send DHCP message.n", 
  2199.                      0, 0, 0, 0, 0, 0);
  2200. #endif
  2201.             return (-1);
  2202.             }
  2203.         } 
  2204.     else 
  2205.         {
  2206.         /* Send message which includes extra options (exceeds default size). */
  2207.         msglen = sbufvec[0].iov_len + sbufvec[1].iov_len;
  2208.         result = dhcpsSend (pIf,
  2209.                             dhcpsMsgOut.dhcp->chaddr, dhcpsMsgOut.dhcp->hlen,
  2210.                             &dst, (char *)dhcpsMsgOut.ip, msglen, bcastFlag);
  2211.         if (result != OK)
  2212.             {
  2213. #ifdef DHCPS_DEBUG
  2214.             logMsg ("Warning: couldn't send DHCP message.n", 
  2215.                      0, 0, 0, 0, 0, 0);
  2216. #endif
  2217.             return (-1);
  2218.             }
  2219.         }
  2220.     return (0);
  2221.     }
  2222. /*******************************************************************************
  2223. *
  2224. * available_forbootp - check resource for issuance to BOOTP client
  2225. *
  2226. * This routine determines if a resource entry may be granted to a BOOTP
  2227. * client. Only entries which are specifically marked as available for
  2228. * BOOTP (with "albp=true") will be granted. Among those entries, they
  2229. * must be unused, expired, or manually assigned to the BOOTP client.
  2230. *
  2231. * RETURNS: TRUE if resource available, or FALSE otherwise.
  2232. *
  2233. * ERRNO: N/A
  2234. *
  2235. * NOMANUAL
  2236. */
  2237. static int available_forbootp
  2238.     (
  2239.     struct dhcp_resource *res,  /* pointer to lease descriptor */
  2240.     struct client_id *cid,  /* pointer to client identifier */
  2241.     time_t curr_epoch  /* current time, in seconds */
  2242.     )
  2243.     {
  2244.     if (res->allow_bootp == FALSE)
  2245.         return (FALSE);
  2246.     /*
  2247.      * Allow resource use if currently unused, or in use by requesting client,
  2248.      * or if previous lease has expired. 
  2249.      */
  2250.     if (res->binding == NULL ||
  2251.         cidcmp (&res->binding->cid, cid) ||
  2252.         (res->binding->expire_epoch != 0xffffffff &&
  2253.          res->binding->expire_epoch < curr_epoch)) 
  2254.         return (TRUE);
  2255.     return (FALSE);
  2256.     }
  2257. /*******************************************************************************
  2258. *
  2259. * choose_forbootp - select resource for BOOTP client
  2260. *
  2261. * This routine retrieves a dhcp_resource structure which may be used
  2262. * for a BOOTP client. The corresponding IP address must be on the
  2263. * same subnet as the requesting client. The client must also provide a
  2264. * matching client identifier, if included in the server's database entry.
  2265. *
  2266. * RETURNS: Matching resource, or NULL if none or not available.
  2267. *
  2268. * ERRNO: N/A
  2269. *
  2270. * NOMANUAL
  2271. */
  2272. /*
  2273.  * choose a new address for a bootp client
  2274.  */
  2275. static struct dhcp_resource * choose_forbootp
  2276.     (
  2277.     struct client_id *cid,  /* pointer to client identifier */
  2278.     time_t curr_epoch  /* current time, in seconds */
  2279.     ) 
  2280.     {
  2281.     struct dhcp_resource *res = NULL;
  2282.     struct dhcp_resource *offer = NULL;
  2283.     struct hash_member *resptr = NULL;
  2284.     resptr = reslist;
  2285.     while (resptr != NULL) 
  2286.        {
  2287.        res = (struct dhcp_resource *) resptr->data;
  2288.        /* if it is dummy entry, skip it */
  2289.        if (res->ip_addr.s_addr == 0) 
  2290.            {
  2291.            resptr = resptr->next;
  2292.            continue;
  2293.            }
  2294.        /* check the resource for valid subnet and availability. */
  2295.        if (cid->subnet.s_addr == 
  2296.                 (res->ip_addr.s_addr & res->subnet_mask.s_addr) &&
  2297.    available_forbootp (res, cid, curr_epoch)) 
  2298.            {
  2299.            if (dhcpsMsgIn.dhcp->ciaddr.s_addr != 0) 
  2300.                {
  2301.        offer = res;
  2302.        break;
  2303.                }
  2304.            /* Specify DHCPDISCOVER to force generation of ICMP request. */
  2305.            else if (icmp_check(DHCPDISCOVER, &res->ip_addr) == GOOD) 
  2306.                {
  2307.        offer = res;
  2308.        break;
  2309.                }
  2310.            else 
  2311.        turnoff_bind (res->binding);
  2312.            }
  2313.        resptr = resptr->next;
  2314.        }
  2315. #ifdef DHCPS_DEBUG
  2316.     if (offer == NULL) 
  2317.         logMsg ("Warning: BOOTP - No available addresses in the pool.n",
  2318.                  0, 0, 0, 0, 0, 0);
  2319. #endif
  2320.     return (offer);
  2321.     }
  2322. /*******************************************************************************
  2323. *
  2324. * construct_bootp - make an outgoing BOOTP message
  2325. *
  2326. * This routine creates a BOOTP reply if an address pool entry is found for
  2327. * a requesting BOOTP client. The corresponding resource is granted with an
  2328. * infinite lease, since BOOTP clients require statically configured 
  2329. * parameters.
  2330. *
  2331. * RETURNS: N/A
  2332. *
  2333. * ERRNO: N/A
  2334. *
  2335. * NOMANUAL
  2336. */
  2337. static void construct_bootp
  2338.     (
  2339.     struct dhcp_resource *res  /* pointer to lease descriptor for offer */
  2340.     ) 
  2341.     { 
  2342.     int i = 0;
  2343.     char inserted [32];
  2344.     int result;
  2345.     bzero (inserted, sizeof (inserted));
  2346.     clean_sbuf ();
  2347.     overload = 0;
  2348.     dhcpsMsgOut.dhcp->op = BOOTREPLY;
  2349.     dhcpsMsgOut.dhcp->htype = dhcpsMsgIn.dhcp->htype;
  2350.     dhcpsMsgOut.dhcp->hlen = dhcpsMsgIn.dhcp->hlen;
  2351.     dhcpsMsgOut.dhcp->hops = 0;
  2352.     dhcpsMsgOut.dhcp->xid = dhcpsMsgIn.dhcp->xid;
  2353.     dhcpsMsgOut.dhcp->secs = 0;
  2354.     if (dhcpsMsgIn.dhcp->giaddr.s_addr != 0) 
  2355.         dhcpsMsgOut.dhcp->flags = dhcpsMsgIn.dhcp->flags;
  2356.     else
  2357.         dhcpsMsgOut.dhcp->flags = 0;
  2358.     dhcpsMsgOut.dhcp->giaddr.s_addr = dhcpsMsgIn.dhcp->giaddr.s_addr;
  2359.     bcopy (dhcpsMsgIn.dhcp->chaddr, dhcpsMsgOut.dhcp->chaddr, 
  2360.            dhcpsMsgIn.dhcp->hlen);
  2361.     dhcpsMsgOut.dhcp->yiaddr.s_addr = res->ip_addr.s_addr;
  2362.     if (ISSET (res->valid, S_SIADDR)) 
  2363.         dhcpsMsgOut.dhcp->siaddr.s_addr = res->siaddr.s_addr;
  2364.     else 
  2365.         dhcpsMsgOut.dhcp->siaddr.s_addr = 0;
  2366.     if (ISSET (res->valid, S_SNAME)) 
  2367.         {
  2368.         strncpy (dhcpsMsgOut.dhcp->sname, res->sname, MAX_SNAME);
  2369.         dhcpsMsgOut.dhcp->sname [MAX_SNAME - 1] = '';
  2370.         }
  2371.     if (ISSET (res->valid, S_FILE)) 
  2372.         {
  2373.         strncpy (dhcpsMsgOut.dhcp->file, res->file, MAX_FILE);
  2374.         dhcpsMsgOut.dhcp->file [MAX_FILE - 1] = '';
  2375.         }
  2376.     /* insert magic cookie */
  2377.     bcopy ((char *)dhcpCookie, dhcpsMsgOut.dhcp->options, MAGIC_LEN);
  2378.     off_options = MAGIC_LEN;
  2379.     off_extopt = 0;
  2380.     /* insert subnet mask */
  2381.     result = insert_opt (res, 0xffffffff, _DHCP_SUBNET_MASK_TAG, inserted, 
  2382.                          PASSIVE);
  2383. #ifdef DHCPS_DEBUG
  2384.     if (result == E_NOMORE) 
  2385.         logMsg ("No space left in BOOTP options field.n", 0, 0, 0, 0, 0, 0);
  2386. #endif
  2387.     /*
  2388.      * insert any binding options that differ from "Host requirement RFC" 
  2389.      * defaults 
  2390.      */
  2391.     for (i = 0; i < _DHCP_LAST_OPTION; i++) 
  2392.         {
  2393.         if (ISCLR (inserted, i)) 
  2394.             if (insert_opt (res, 0xffffffff, i, inserted, PASSIVE) == E_NOMORE)
  2395.                 {
  2396. #ifdef DHCPS_DEBUG
  2397.                 logMsg ("No space left in BOOTP options field.n",
  2398.                          0, 0, 0, 0, 0, 0);
  2399. #endif
  2400.                 break;
  2401.                 }
  2402.         }
  2403.     return;
  2404.     }
  2405. /*******************************************************************************
  2406. *
  2407. * bootp - handle BOOTP requests
  2408. *
  2409. * This routine examines requests from BOOTP clients, selects an available
  2410. * resource (if any), calculates the length of the lease, and sends the
  2411. * appropriate offer BOOTP reply to the client.
  2412. *
  2413. * RETURNS: 0 if processing successful, or -1 on error.
  2414. *
  2415. * ERRNO: N/A
  2416. *
  2417. * NOMANUAL
  2418. */
  2419. static int bootp 
  2420.     (
  2421.     struct if_info *ifp    /* pointer to descriptor of receiving interface */
  2422.     )
  2423.     {
  2424.     char addrstr [sizeof (BRDCSTSTR)];
  2425.     struct client_id cid;
  2426.     struct dhcp_binding *binding = NULL;
  2427.     struct dhcp_resource *res = NULL;
  2428.     time_t curr_epoch = 0;
  2429.     bzero ( (char *)&cid, sizeof (cid));
  2430.     bcopy (BRDCSTSTR, addrstr, sizeof (BRDCSTSTR));
  2431. #ifdef NOBOOTP
  2432.   return (0);
  2433. #endif
  2434.     if (dhcpTime (&curr_epoch) == -1) 
  2435.         {
  2436. #ifdef DHCPS_DEBUG
  2437.         logMsg ("Warning: couldn't get timestamp processing bootp message.n",
  2438.                  0, 0, 0, 0, 0, 0);
  2439. #endif
  2440.         return (-1);
  2441.         }
  2442.     get_cid (dhcpsMsgIn.dhcp, rdhcplen, &cid);
  2443.     /* Critical section with dhcpsLeaseEntryAdd(). */
  2444.     semTake (dhcpsMutexSem, WAIT_FOREVER);
  2445.     if (get_subnet (dhcpsMsgIn.dhcp, rdhcplen, &cid.subnet, ifp) != 0)
  2446.         { 
  2447.         semGive (dhcpsMutexSem);
  2448.         return (-1);
  2449.         }
  2450.     maxoptlen = BOOTPOPTLEN;
  2451.     /* search with haddr (haddr is same as client identfier) */
  2452.     binding = (struct dhcp_binding *)hash_find (&cidhashtable, cid.id, 
  2453.                                                 cid.idlen, bindcidcmp, &cid);
  2454.     if (binding != NULL) 
  2455.         {
  2456.         if (cidcmp (&binding->cid, &cid))
  2457.             res = binding->res;
  2458.         }
  2459.     if (res == NULL && (res = choose_forbootp(&cid, curr_epoch)) == NULL)
  2460.         {
  2461.         semGive (dhcpsMutexSem);
  2462.         return (-1);
  2463.         }
  2464.     if (update_db (BOOTP, &cid, res, 0xffffffff, curr_epoch) != 0)
  2465.         {
  2466.         semGive (dhcpsMutexSem);
  2467.         return (-1);
  2468.         }
  2469.     semGive (dhcpsMutexSem);
  2470.     /* binding is complete. Update entry flags. */
  2471.     res->binding->flag |= (COMPLETE_ENTRY | BOOTP_ENTRY);
  2472.     construct_bootp (res);
  2473.     send_bootp (ifp);
  2474.     inet_ntoa_b (res->ip_addr,addrstr);
  2475. #ifdef DHCPS_DEBUG
  2476.     logMsg ("Sending BOOTP reply to client(IP:%s, cid:"%s").n",
  2477.      (int)addrstr, (int)cidtos (&res->binding->cid, 1), 0, 0, 0, 0);
  2478. #endif
  2479.     return (0);
  2480.     }
  2481. /*******************************************************************************
  2482. *
  2483. * send_bootp - transmit a BOOTP reply
  2484. *
  2485. * This routine sends messages formulated by an earlier construct_bootp() call
  2486. * directly to the destination address, if available, or as a link layeri
  2487. * broadcast on the client's subnet.
  2488. *
  2489. * RETURNS: 0 if message sent, or -1 otherwise.
  2490. *
  2491. * ERRNO: N/A
  2492. *
  2493. * NOMANUAL
  2494. */
  2495. static int send_bootp
  2496.     (
  2497.     struct if_info *ifp  /* descriptor for transmission interface */
  2498.     )
  2499.     {
  2500.     int buflen = 0;
  2501.     char devName [10];
  2502.     struct sockaddr_in srcaddr; 
  2503.     struct sockaddr_in dstaddr;
  2504.     struct ifnet *pIf;    
  2505.     BOOL bcastFlag;
  2506.     int result;
  2507.     bzero ((char *)&srcaddr, sizeof (srcaddr));
  2508.     bzero ((char *)&dstaddr, sizeof (dstaddr));
  2509.     dstaddr.sin_len = sizeof (struct sockaddr_in);
  2510.     dstaddr.sin_family = AF_INET;
  2511.     if (off_options < BOOTPOPTLEN) 
  2512.         dhcpsMsgOut.dhcp->options [off_options] = _DHCP_END_TAG;
  2513.     /* if received message was relayed from relay agent,
  2514.        send reply from normal socket */
  2515.     if (dhcpsMsgIn.dhcp->giaddr.s_addr != 0) 
  2516.         {
  2517.         if (dhcpsMsgIn.dhcp->ciaddr.s_addr == 0 ||
  2518.             dhcpsMsgIn.dhcp->ciaddr.s_addr != dhcpsMsgOut.dhcp->yiaddr.s_addr) 
  2519.             {
  2520.             dstaddr.sin_port = dhcps_port;
  2521.             bcopy( (char *)&dhcpsMsgIn.dhcp->giaddr, 
  2522.                    (char *)&dstaddr.sin_addr, sizeof (u_long));
  2523.             } 
  2524.         else 
  2525.             {
  2526.             dstaddr.sin_port = dhcpc_port;
  2527.             bcopy ( (char *)&dhcpsMsgOut.dhcp->yiaddr, 
  2528.                     (char *)&dstaddr.sin_addr, sizeof (u_long));
  2529.             }
  2530.         buflen = DFLTBOOTPLEN;
  2531.         if (setsockopt (dhcpsIntfaceList->fd, SOL_SOCKET, SO_SNDBUF, 
  2532.             (char *)&buflen, sizeof (buflen)) < 0) 
  2533.             {
  2534. #ifdef DHCPS_DEBUG
  2535.             logMsg ("Warning: Couldn't set transmit buffer size for BOOTP.n", 
  2536.                      0, 0, 0, 0, 0, 0);
  2537. #endif
  2538.             return (-1);
  2539.             }
  2540.         /*
  2541.          * This will send to any network interface, 
  2542.          * independent of the local address it is bound to.
  2543.          */
  2544.         if (sendto (dhcpsIntfaceList->fd, (caddr_t)dhcpsMsgOut.dhcp, buflen,
  2545.                     0, (struct sockaddr *)&dstaddr, sizeof (dstaddr)) < 0) 
  2546.             {
  2547. #ifdef DHCPS_DEBUG
  2548.             logMsg ("Warning: Couldn't send BOOTP message.n", 
  2549.                      0, 0, 0, 0, 0, 0);
  2550. #endif
  2551.             return (-1);
  2552.             }
  2553.         return (0);
  2554.         }
  2555.     /* if directly received packet.... */
  2556.     /* Set destination address and fill pseudo header to calculate checksum */
  2557.     bcopy ( (char *)&ifp->ipaddr.s_addr, 
  2558.             (char *)&dhcpsMsgOut.ip->ip_src, sizeof (u_long));
  2559.     if (ISBRDCST (dhcpsMsgIn.dhcp->flags) || 
  2560.         dhcpsMsgIn.dhcp->ciaddr.s_addr != dhcpsMsgOut.dhcp->yiaddr.s_addr)
  2561.         {
  2562.         dstaddr.sin_addr.s_addr = dhcpsMsgOut.ip->ip_dst.s_addr = 0xffffffff;
  2563.         bcastFlag = TRUE;
  2564.         }
  2565.     else
  2566.         {
  2567.         dhcpsMsgOut.ip->ip_dst.s_addr = dhcpsMsgOut.dhcp->yiaddr.s_addr;
  2568.         dstaddr.sin_addr.s_addr = dhcpsMsgOut.dhcp->yiaddr.s_addr;
  2569.         bcastFlag = FALSE;
  2570.         }
  2571.     dhcpsMsgOut.udp->uh_sport = dhcps_port;
  2572.     dhcpsMsgOut.udp->uh_dport = dhcpc_port;
  2573.     dhcpsMsgOut.udp->uh_ulen = htons (DFLTBOOTPLEN + UDPHL);
  2574.     dhcpsMsgOut.udp->uh_sum = get_udpsum (dhcpsMsgOut.ip, dhcpsMsgOut.udp);
  2575.     dhcpsMsgOut.ip->ip_v = IPVERSION;
  2576.     dhcpsMsgOut.ip->ip_hl = IPHL >> 2;
  2577.     dhcpsMsgOut.ip->ip_tos = 0;
  2578.     dhcpsMsgOut.ip->ip_len = htons (DFLTBOOTPLEN + UDPHL + IPHL);
  2579.     dhcpsMsgOut.ip->ip_id = dhcpsMsgOut.udp->uh_sum;
  2580.     dhcpsMsgOut.ip->ip_off = htons (IP_DF);    /* XXX */
  2581.     dhcpsMsgOut.ip->ip_ttl = 0x20;            /* XXX */
  2582.     dhcpsMsgOut.ip->ip_p = IPPROTO_UDP;
  2583.     dhcpsMsgOut.ip->ip_sum = get_ipsum (dhcpsMsgOut.ip);
  2584.     buflen = DFLTBOOTPLEN + UDPHL + IPHL + ETHERHL;
  2585.     sprintf (devName, "%s%d", ifp->name, ifp->unit);
  2586.     pIf = ifunit (devName);
  2587.     if (pIf == NULL)
  2588.         {
  2589. #ifdef DHCPS_DEBUG
  2590.         logMsg ("Warning: couldn't access network interface %s.n", 
  2591.                  (int)devName, 0, 0, 0, 0, 0);
  2592. #endif
  2593.         return (-1);
  2594.         }
  2595.     result = dhcpsSend (pIf,
  2596.                         dhcpsMsgOut.dhcp->chaddr, dhcpsMsgOut.dhcp->hlen,
  2597.                         &dstaddr, (char *)dhcpsMsgOut.ip, buflen, bcastFlag);
  2598.     if (result != OK)
  2599.         {
  2600. #ifdef DHCPS_DEBUG
  2601.         logMsg ("Warning: couldn't send BOOTP message.n", 0, 0, 0, 0, 0, 0);
  2602. #endif
  2603.         return (-1);
  2604.         }
  2605.     return (0);
  2606.     }
  2607. /*******************************************************************************
  2608. *
  2609. * ins_ip - insert options containing a single IP address
  2610. *
  2611. * This routine inserts any available options in the selected resource which
  2612. * consist of a single IP address as options in an outgoing DHCP message.
  2613. *
  2614. * RETURNS: 0 if option inserted, or negative value on error.
  2615. *
  2616. * ERRNO: N/A
  2617. *
  2618. * NOMANUAL
  2619. */
  2620. static int ins_ip
  2621.     (
  2622.     struct dhcp_resource *res,  /* pointer to lease descriptor */
  2623.     u_long lease,  /* lease duration, in seconds */
  2624.     int tagnum,  /* tag value of option */
  2625.     char *inserted,  /* bitmap of inserted options */
  2626.     char flag  /* if ACTIVE, marks non-default options */
  2627.     )
  2628.     {
  2629.     u_long *addr = 0;
  2630.     char option[6];
  2631.     int symbol = 0;
  2632.     int retval = 0;
  2633.     bzero (option, sizeof (option));
  2634.     /* Access offset for appropriate data. */
  2635.     switch (tagnum) 
  2636.         {
  2637.         case _DHCP_SUBNET_MASK_TAG:
  2638.             symbol = S_SUBNET_MASK; 
  2639.             addr = &res->subnet_mask.s_addr;
  2640.             break;
  2641.         case _DHCP_SWAP_SERVER_TAG:
  2642.             symbol = S_SWAP_SERVER;
  2643.             addr = &res->swap_server.s_addr;
  2644.             break;
  2645.         case _DHCP_BRDCAST_ADDR_TAG:
  2646.             symbol = S_BRDCAST_ADDR;
  2647.             addr = &res->brdcast_addr.s_addr;
  2648.             break;
  2649.         case _DHCP_ROUTER_SOLICIT_TAG:
  2650.             symbol = S_ROUTER_SOLICIT; 
  2651.             addr = &res->router_solicit.s_addr;
  2652.             break;
  2653.         default:
  2654.             return (-1);
  2655.         }
  2656.    
  2657.     /* Copy data if valid. */ 
  2658.     if ( (flag == PASSIVE && ISSET (res->valid, symbol)) ||
  2659.          (flag == ACTIVE && ISSET (res->active, symbol))) 
  2660.         {
  2661.         option[0] = tagnum;
  2662.         option[1] = 4;               /* Length of option data, in bytes. */
  2663.         bcopy ( (char *)addr, &option[2], 4);
  2664.         if ( (retval = insert_it (option)) == 0)
  2665.             SETBIT (inserted, tagnum);
  2666.         }
  2667.     return (retval);
  2668.     }
  2669. /*******************************************************************************
  2670. *
  2671. * ins_ips - insert options containing multiple IP addresses
  2672. *
  2673. * This routine inserts any available options in the selected resource which
  2674. * consist of one or more IP addresses as options in an outgoing DHCP message.
  2675. *
  2676. * RETURNS: 0 if option inserted, or negative value on error.
  2677. *
  2678. * ERRNO: N/A
  2679. *
  2680. * NOMANUAL
  2681. */
  2682. static int ins_ips
  2683.     (
  2684.     struct dhcp_resource *res,  /* pointer to lease descriptor */
  2685.     u_long lease,  /* lease duration, in seconds */
  2686.     int tagnum,  /* tag value of option */
  2687.     char *inserted,  /* bitmap of inserted options */
  2688.     char flag  /* if ACTIVE, marks non-default options */
  2689.     )
  2690.     {
  2691.     struct in_addrs *addr = NULL;
  2692.     char option [254];
  2693.     int symbol = 0;
  2694.     int retval = 0;
  2695.     int i = 0;
  2696.     bzero (option, sizeof (option));
  2697.     switch(tagnum) 
  2698.         {
  2699.         case _DHCP_ROUTER_TAG:
  2700.             symbol = S_ROUTER;
  2701.             addr = &res->router;
  2702.             break;
  2703.         case _DHCP_TIME_SERVER_TAG:
  2704.             symbol = S_TIME_SERVER;
  2705.             addr = &res->time_server;
  2706.             break;
  2707.         case _DHCP_NAME_SERVER_TAG:
  2708.             symbol = S_NAME_SERVER; 
  2709.             addr = &res->name_server;
  2710.             break;
  2711.         case _DHCP_DNS_SERVER_TAG:
  2712.             symbol = S_DNS_SERVER; 
  2713.             addr = &res->dns_server;
  2714.             break;
  2715.         case _DHCP_LOG_SERVER_TAG:
  2716.             symbol = S_LOG_SERVER; 
  2717.             addr = &res->log_server;
  2718.             break;
  2719.         case _DHCP_COOKIE_SERVER_TAG:
  2720.             symbol = S_COOKIE_SERVER; 
  2721.             addr = &res->cookie_server;
  2722.             break;
  2723.         case _DHCP_LPR_SERVER_TAG:
  2724.             symbol = S_LPR_SERVER; 
  2725.             addr = &res->lpr_server;
  2726.             break;
  2727.         case _DHCP_IMPRESS_SERVER_TAG:
  2728.             symbol = S_IMPRESS_SERVER; 
  2729.             addr = &res->impress_server;
  2730.             break;
  2731.         case _DHCP_RLS_SERVER_TAG:
  2732.             symbol = S_RLS_SERVER; 
  2733.             addr = &res->rls_server;
  2734.             break;
  2735.         case _DHCP_NIS_SERVER_TAG:
  2736.             symbol = S_NIS_SERVER; 
  2737.             addr = &res->nis_server;
  2738.             break;
  2739.         case _DHCP_NTP_SERVER_TAG:
  2740.             symbol = S_NTP_SERVER; 
  2741.             addr = &res->ntp_server;
  2742.             break;
  2743.         case _DHCP_NBN_SERVER_TAG:
  2744.             symbol = S_NBN_SERVER; 
  2745.             addr = &res->nbn_server;
  2746.             break;
  2747.         case _DHCP_NBDD_SERVER_TAG:
  2748.             symbol = S_NBDD_SERVER; 
  2749.             addr = &res->nbdd_server;
  2750.             break;
  2751.         case _DHCP_XFONT_SERVER_TAG:
  2752.             symbol = S_XFONT_SERVER; 
  2753.             addr = &res->xfont_server;
  2754.             break;
  2755.         case _DHCP_XDISPLAY_MANAGER_TAG:
  2756.             symbol = S_XDISPLAY_MANAGER; 
  2757.             addr = &res->xdisplay_manager;
  2758.             break;
  2759.         case _DHCP_NISP_SERVER_TAG:
  2760.             symbol = S_NISP_SERVER; 
  2761.             addr = &res->nisp_server;
  2762.             break;
  2763.         case _DHCP_MOBILEIP_HA_TAG:
  2764.             symbol = S_MOBILEIP_HA; 
  2765.             addr = &res->mobileip_ha;
  2766.             break;
  2767.         case _DHCP_SMTP_SERVER_TAG:
  2768.             symbol = S_SMTP_SERVER; 
  2769.             addr = &res->smtp_server;
  2770.             break;
  2771.         case _DHCP_POP3_SERVER_TAG:
  2772.             symbol = S_POP3_SERVER; 
  2773.             addr = &res->pop3_server;
  2774.             break;
  2775.         case _DHCP_NNTP_SERVER_TAG:
  2776.             symbol = S_NNTP_SERVER; 
  2777.             addr = &res->nntp_server;
  2778.             break;
  2779.         case _DHCP_DFLT_WWW_SERVER_TAG:
  2780.             symbol = S_DFLT_WWW_SERVER; 
  2781.             addr = &res->dflt_www_server;
  2782.             break;
  2783.         case _DHCP_DFLT_FINGER_SERVER_TAG:
  2784.             symbol = S_DFLT_FINGER_SERVER; 
  2785.             addr = &res->dflt_finger_server;
  2786.             break;
  2787.         case _DHCP_DFLT_IRC_SERVER_TAG:
  2788.             symbol = S_DFLT_IRC_SERVER; 
  2789.             addr = &res->dflt_irc_server;
  2790.             break;
  2791.         case _DHCP_STREETTALK_SERVER_TAG:
  2792.             symbol = S_STREETTALK_SERVER; 
  2793.             addr = &res->streettalk_server;
  2794.             break;
  2795.         case _DHCP_STDA_SERVER_TAG:
  2796.             symbol = S_STDA_SERVER; 
  2797.             addr = &res->stda_server;
  2798.             break;
  2799.         default:
  2800.             return (-1);
  2801.         }
  2802.     if ( (flag == PASSIVE && ISSET (res->valid, symbol)) ||
  2803.          (flag == ACTIVE && ISSET (res->active, symbol))) 
  2804.         {
  2805.         option[0] = tagnum;
  2806.         option[1] = addr->num * 4;   /* Length of data, in bytes. */
  2807.         for (i = 0; i < addr->num; i++)
  2808.             bcopy ((char *)&addr->addr[i].s_addr, &option [i * 4 + 2], 4);
  2809.         if ( (retval = insert_it (option)) == 0)
  2810.             SETBIT (inserted, tagnum);
  2811.         }
  2812.     return (retval);
  2813.     }
  2814. /*******************************************************************************
  2815. *
  2816. * ins_ippairs - insert options containing multiple IP address pairs
  2817. *
  2818. * This routine inserts any available options in the selected resource which
  2819. * consist of one or more pairs of IP addresses as options in an outgoing 
  2820. * DHCP message.
  2821. *
  2822. * RETURNS: 0 if option inserted, or negative value on error.
  2823. *
  2824. * ERRNO: N/A
  2825. *
  2826. * NOMANUAL
  2827. */
  2828. static int ins_ippairs
  2829.     (
  2830.     struct dhcp_resource *res,  /* pointer to lease descriptor */
  2831.     u_long lease,  /* lease duration, in seconds */
  2832.     int tagnum,  /* tag value of option */
  2833.     char *inserted,  /* bitmap of inserted options */
  2834.     char flag  /* if ACTIVE, marks non-default options */
  2835.     )
  2836.     {
  2837.     struct ip_pairs *pair = NULL;
  2838.     char option [254];
  2839.     int symbol = 0;
  2840.     int retval = 0;
  2841.     int i = 0;
  2842.     bzero (option, sizeof (option));
  2843.     switch (tagnum) 
  2844.         {
  2845.         case _DHCP_POLICY_FILTER_TAG:
  2846.             symbol = S_POLICY_FILTER;
  2847.             pair = &res->policy_filter;
  2848.             break;
  2849.         case _DHCP_STATIC_ROUTE_TAG:
  2850.             symbol = S_STATIC_ROUTE; 
  2851.             pair = &res->static_route;
  2852.             break;
  2853.         default:
  2854.             return (-1);
  2855.         }
  2856.     if ( (flag == PASSIVE && ISSET (res->valid, symbol)) ||
  2857.          (flag == ACTIVE && ISSET (res->active, symbol))) 
  2858.         {
  2859.         option[0] = tagnum;
  2860.         option[1] = pair->num * 8;
  2861.         for (i = 0; i < pair->num; i++) 
  2862.             {
  2863.             bcopy ( (char *)&pair->addr1[i].s_addr, &option [i * 8 + 2], 4);
  2864.             bcopy ( (char *)&pair->addr2[i].s_addr, &option [i * 8 + 6], 4);
  2865.             }
  2866.         if ( (retval = insert_it (option)) == 0)
  2867.             SETBIT (inserted, tagnum);
  2868.         }
  2869.     return (retval);
  2870.     }
  2871. /*******************************************************************************
  2872. *
  2873. * ins_long - insert options containing long integers
  2874. *
  2875. * This routine inserts any available options in the selected resource which
  2876. * consist of a long (4 byte) value as options in an outgoing DHCP message.
  2877. *
  2878. * RETURNS: 0 if option inserted, or negative value on error.
  2879. *
  2880. * ERRNO: N/A
  2881. *
  2882. * NOMANUAL
  2883. */
  2884. static int ins_long
  2885.     (
  2886.     struct dhcp_resource *res,  /* pointer to lease descriptor */
  2887.     u_long lease,  /* lease duration, in seconds */
  2888.     int tagnum,  /* tag value of option */
  2889.     char *inserted,  /* bitmap of inserted options */
  2890.     char flag  /* if ACTIVE, marks non-default options */
  2891.     )
  2892.     {
  2893.     long *num = NULL;
  2894.     char option[6];
  2895.     int symbol = 0;
  2896.     int retval = 0;
  2897.     bzero (option, sizeof (option));
  2898.     switch (tagnum) 
  2899.         {
  2900.         case _DHCP_TIME_OFFSET_TAG:
  2901.             symbol = S_TIME_OFFSET; 
  2902.             num = &res->time_offset;
  2903.             break;
  2904.         case _DHCP_MTU_AGING_TIMEOUT_TAG:
  2905.             symbol = S_MTU_AGING_TIMEOUT; 
  2906.             num = (long *)&res->mtu_aging_timeout;
  2907.             break;
  2908.         case _DHCP_ARP_CACHE_TIMEOUT_TAG:
  2909.             symbol = S_ARP_CACHE_TIMEOUT; 
  2910.             num = (long *)&res->arp_cache_timeout;
  2911.             break;
  2912.         case _DHCP_KEEPALIVE_INTERVAL_TAG:
  2913.             symbol = S_KEEPALIVE_INTER; 
  2914.             num = (long *)&res->keepalive_inter;
  2915.             break;
  2916.         default:
  2917.             return (-1);
  2918.         }
  2919.     
  2920.     if ( (flag == PASSIVE && ISSET (res->valid, symbol)) ||
  2921.          (flag == ACTIVE && ISSET (res->active, symbol)))
  2922.         {
  2923.         option [0] = tagnum;
  2924.         option [1] = 4;
  2925.         bcopy ( (char *)num, &option[2], 4);
  2926.         if ( (retval = insert_it (option)) == 0)
  2927.             SETBIT (inserted, tagnum);
  2928.         }
  2929.     return (retval);
  2930.     }
  2931. /*******************************************************************************
  2932. *
  2933. * ins_short - insert options containing short integers 
  2934. *
  2935. * This routine inserts any available options in the selected resource which
  2936. * consist of a short (2 byte) value as options in an outgoing DHCP message.
  2937. *
  2938. * RETURNS: 0 if option inserted, or negative value on error.
  2939. *
  2940. * ERRNO: N/A
  2941. *
  2942. * NOMANUAL
  2943. */
  2944. static int ins_short
  2945.     (
  2946.     struct dhcp_resource *res,  /* pointer to lease descriptor */
  2947.     u_long lease,  /* lease duration, in seconds */
  2948.     int tagnum,  /* tag value of option */
  2949.     char *inserted,  /* bitmap of inserted options */
  2950.     char flag  /* if ACTIVE, marks non-default options */
  2951.     )
  2952.     {
  2953.     short *num = NULL;
  2954.     char option[4];
  2955.     int symbol = 0;
  2956.     int retval = 0;
  2957.     bzero (option, sizeof (option));
  2958.  
  2959.     switch (tagnum) 
  2960.         {
  2961.         case _DHCP_BOOTSIZE_TAG:
  2962.             symbol = S_BOOTSIZE; 
  2963.             num = (short *)&res->bootsize;
  2964.             break;
  2965.         case _DHCP_MAX_DGRAM_SIZE_TAG:
  2966.             symbol = S_MAX_DGRAM_SIZE; 
  2967.             num = (short *) &res->max_dgram_size;
  2968.             break;
  2969.         case _DHCP_IF_MTU_TAG:
  2970.             symbol = S_IF_MTU; 
  2971.             num = (short *)&res->intf_mtu;
  2972.             break;
  2973.         default:
  2974.             return (-1);
  2975.         }
  2976.     if ( (flag == PASSIVE && ISSET (res->valid, symbol)) ||
  2977.          (flag == ACTIVE && ISSET (res->active, symbol))) 
  2978.         {
  2979.         option[0] = tagnum;
  2980.         option[1] = 2;
  2981.         bcopy ((char *)num, &option[2], 2);
  2982.         if ( (retval = insert_it (option)) == 0)
  2983.             SETBIT (inserted, tagnum);
  2984.         }
  2985.     return (retval);
  2986.     }
  2987. /*******************************************************************************
  2988. *
  2989. * ins_octet - insert options containing single bytes
  2990. *
  2991. * This routine inserts any available options in the selected resource which
  2992. * consist of a 1 byte value as options in an outgoing DHCP message.
  2993. *
  2994. * RETURNS: 0 if option inserted, or negative value on error.
  2995. *
  2996. * ERRNO: N/A
  2997. *
  2998. * NOMANUAL
  2999. */
  3000. static int ins_octet
  3001.     (
  3002.     struct dhcp_resource *res,  /* pointer to lease descriptor */
  3003.     u_long lease,  /* lease duration, in seconds */
  3004.     int tagnum,  /* tag value of option */
  3005.     char *inserted,  /* bitmap of inserted options */
  3006.     char flag  /* if ACTIVE, marks non-default options */
  3007.     )
  3008.     {
  3009.     char num = 0;
  3010.     char option[3];
  3011.     int symbol = 0;
  3012.     int retval = 0;
  3013.     bzero (option, sizeof (option));
  3014.     switch (tagnum) 
  3015.         {
  3016.         case _DHCP_IP_FORWARD_TAG:
  3017.             symbol = S_IP_FORWARD; 
  3018.             num = res->ip_forward;
  3019.             break;
  3020.         case _DHCP_NONLOCAL_SRCROUTE_TAG:
  3021.             symbol = S_NONLOCAL_SRCROUTE; 
  3022.             num = res->nonlocal_srcroute;
  3023.             break;
  3024.         case _DHCP_DEFAULT_IP_TTL_TAG:
  3025.             symbol = S_DEFAULT_IP_TTL; 
  3026.             num = res->default_ip_ttl;
  3027.             break;
  3028.         case _DHCP_ALL_SUBNET_LOCAL_TAG:
  3029.             symbol = S_ALL_SUBNET_LOCAL; 
  3030.             num = res->all_subnet_local;
  3031.             break;
  3032.         case _DHCP_MASK_DISCOVER_TAG:
  3033.             symbol = S_MASK_DISCOVER; 
  3034.             num = res->mask_discover;
  3035.             break;
  3036.         case _DHCP_MASK_SUPPLIER_TAG:
  3037.             symbol = S_MASK_SUPPLIER; 
  3038.             num = res->mask_supplier;
  3039.             break;
  3040.         case _DHCP_ROUTER_DISCOVER_TAG:
  3041.             symbol = S_ROUTER_DISCOVER; 
  3042.             num = res->router_discover;
  3043.             break;
  3044.         case _DHCP_TRAILER_TAG:
  3045.             symbol = S_TRAILER; 
  3046.             num = res->trailer;
  3047.             break;
  3048.         case _DHCP_ETHER_ENCAP_TAG:
  3049.             symbol = S_ETHER_ENCAP; 
  3050.             num = res->ether_encap;
  3051.             break;
  3052.         case _DHCP_DEFAULT_TCP_TTL_TAG:
  3053.             symbol = S_DEFAULT_TCP_TTL; 
  3054.             num = res->default_tcp_ttl;
  3055.             break;
  3056.         case _DHCP_KEEPALIVE_GARBAGE_TAG:
  3057.             symbol = S_KEEPALIVE_GARBA; 
  3058.             num = res->keepalive_garba;
  3059.             break;
  3060.         case _DHCP_NB_NODETYPE_TAG:
  3061.             symbol = S_NB_NODETYPE; 
  3062.             num = res->nb_nodetype;
  3063.             break;
  3064.         default:
  3065.             return (-1);
  3066.         }
  3067.     
  3068.     if ( (flag == PASSIVE && ISSET (res->valid, symbol)) ||
  3069.          (flag == ACTIVE && ISSET (res->active, symbol))) 
  3070.         {
  3071.         option[0] = tagnum;
  3072.         option[1] = 1;
  3073.         option[2] = num;
  3074.         if ( (retval = insert_it (option)) == 0)
  3075.             SETBIT(inserted, tagnum);
  3076.         }
  3077.     return (retval);
  3078.     }
  3079. /*******************************************************************************
  3080. *
  3081. * ins_str - insert options containing strings
  3082. *
  3083. * This routine inserts any available options in the selected resource which
  3084. * consist of a NULL terminated string as options in an outgoing DHCP message.
  3085. *
  3086. * RETURNS: 0 if option inserted, or negative value on error.
  3087. *
  3088. * ERRNO: N/A
  3089. *
  3090. * NOMANUAL
  3091. */
  3092. /*
  3093.  * insert string
  3094.  */
  3095. /* ARGSUSED */
  3096. static int ins_str
  3097.     (
  3098.     struct dhcp_resource *res,  /* pointer to lease descriptor */
  3099.     u_long lease,  /* lease duration, in seconds */
  3100.     int tagnum,  /* tag value of option */
  3101.     char *inserted,  /* bitmap of inserted options */
  3102.     char flag  /* if ACTIVE, marks non-default options */
  3103.     )
  3104.     {
  3105.     char *str = NULL;
  3106.     char option [258];
  3107.     int symbol = 0;
  3108.     int retval = 0;
  3109.     int i = 0;
  3110.     bzero (option, sizeof (option));
  3111.  
  3112.     switch (tagnum) 
  3113.         {
  3114.         case _DHCP_HOSTNAME_TAG:
  3115.             symbol = S_HOSTNAME; 
  3116.             str = res->hostname;
  3117.             break;
  3118.         case _DHCP_MERIT_DUMP_TAG:
  3119.             symbol = S_MERIT_DUMP; 
  3120.             str = res->merit_dump;
  3121.             break;
  3122.         case _DHCP_DNS_DOMAIN_TAG:
  3123.             symbol = S_DNS_DOMAIN; 
  3124.             str = res->dns_domain;
  3125.             break;
  3126.         case _DHCP_ROOT_PATH_TAG:
  3127.             symbol = S_ROOT_PATH; 
  3128.             str = res->root_path;
  3129.             break;
  3130.         case _DHCP_EXTENSIONS_PATH_TAG:
  3131.             symbol = S_EXTENSIONS_PATH; 
  3132.             str = res->extensions_path;
  3133.             break;
  3134.         case _DHCP_NIS_DOMAIN_TAG:
  3135.             symbol = S_NIS_DOMAIN; 
  3136.             str = res->nis_domain;
  3137.             break;
  3138.         case _DHCP_NB_SCOPE_TAG:
  3139.             symbol = S_NB_SCOPE; 
  3140.             str = res->nb_scope;
  3141.             break;
  3142.         case _DHCP_NISP_DOMAIN_TAG:
  3143.             symbol = S_NISP_DOMAIN; 
  3144.             str = res->nisp_domain;
  3145.             break;
  3146.         default:
  3147.             return (-1);
  3148.         }
  3149.     if ( (flag == PASSIVE && ISSET (res->valid, symbol)) ||
  3150.          (flag == ACTIVE && ISSET (res->active, symbol))) 
  3151.         {
  3152.         option[0] = tagnum;
  3153.         option[1] = ( (i = strlen (str)) > MAXOPT) ? MAXOPT : i;
  3154.         bcopy (str, &option[2], option[1]);
  3155.         if ( (retval = insert_it (option)) == 0)
  3156.             SETBIT (inserted, tagnum);
  3157.         }
  3158.     return (retval);
  3159.     }
  3160. /*******************************************************************************
  3161. *
  3162. * ins_dht - insert lease timers
  3163. *
  3164. * This routine inserts the values for T1 and T2 into the options of an
  3165. * outgoing DHCP message.
  3166. *
  3167. * RETURNS: 0 if options inserted, or negative value on error.
  3168. *
  3169. * ERRNO: N/A
  3170. *
  3171. * NOMANUAL
  3172. */
  3173. static int ins_dht
  3174.     (
  3175.     struct dhcp_resource *res,  /* pointer to lease descriptor */
  3176.     u_long lease,  /* lease duration, in seconds */
  3177.     int tagnum,  /* tag value of option */
  3178.     char *inserted,  /* bitmap of inserted options */
  3179.     char flag  /* if ACTIVE, marks non-default options */
  3180.     )
  3181.     {
  3182.     char option[6];
  3183.     int symbol = 0;
  3184.     int retval = 0;
  3185.     long num = 0;
  3186.     bzero (option, sizeof (option));
  3187.     switch (tagnum) 
  3188.         {
  3189.         case _DHCP_T1_TAG:
  3190.             symbol = S_DHCP_T1;
  3191.             num = htonl (lease * (res->dhcp_t1 + (rand () & 0x03)) / 1000);
  3192.             break;
  3193.         case _DHCP_T2_TAG:
  3194.             symbol = S_DHCP_T2;
  3195.             num = htonl (lease * (res->dhcp_t2 + (rand () & 0x03)) / 1000);
  3196.             break;
  3197.         default:
  3198.             return (-1);
  3199.         }
  3200.     
  3201.     if ( (flag == PASSIVE && ISSET (res->valid, symbol)) ||
  3202.          (flag == ACTIVE && ISSET (res->active, symbol))) 
  3203.         {
  3204.         option[0] = tagnum;
  3205.         option[1] = 4;
  3206.         bcopy ( (char *)&num, &option[2], option[1]);
  3207.         if ( (retval = insert_it (option)) == 0)
  3208.             SETBIT (inserted, tagnum);
  3209.         }
  3210.     return (retval);
  3211.     }
  3212. /*******************************************************************************
  3213. *
  3214. * ins_mtpt - insert MTU plateau table options
  3215. *
  3216. * This routine inserts the values specified for the MTU table as options in an 
  3217. * outgoing DHCP message.
  3218. *
  3219. * RETURNS: 0 if option inserted, or negative value on error.
  3220. *
  3221. * ERRNO: N/A
  3222. *
  3223. * NOMANUAL
  3224. */
  3225. /*
  3226.  * insert mtu_plateau_table
  3227.  */
  3228. static int ins_mtpt
  3229.     (
  3230.     struct dhcp_resource *res,  /* pointer to lease descriptor */
  3231.     u_long lease,  /* lease duration, in seconds */
  3232.     int tagnum,  /* tag value of option */
  3233.     char *inserted,  /* bitmap of inserted options */
  3234.     char flag  /* if ACTIVE, marks non-default options */
  3235.     )
  3236.     {
  3237.     char option [256];
  3238.     int retval = 0;
  3239.     int i = 0;
  3240.     bzero (option, sizeof (option));
  3241.     if (tagnum != _DHCP_MTU_PLATEAU_TABLE_TAG) 
  3242.         return (-1);
  3243.     if ( (flag == PASSIVE && ISSET (res->valid, S_MTU_PLATEAU_TABLE)) ||
  3244.          (flag == ACTIVE && ISSET (res->active, S_MTU_PLATEAU_TABLE))) 
  3245.         {
  3246.         option[0] = tagnum;
  3247.         option[1] = res->mtu_plateau_table.num * 2;
  3248.         for (i = 0; i < res->mtu_plateau_table.num; i++)
  3249.             bcopy ( (char *)&res->mtu_plateau_table.shorts[i], 
  3250.                    &option [i * 2 + 2], 2);
  3251.         if ( (retval = insert_it (option)) == 0)
  3252.             SETBIT (inserted, tagnum);
  3253.         }
  3254.     return (retval);
  3255.     }
  3256. /*******************************************************************************
  3257. *
  3258. * insert_opt - add an option to a outgoing message
  3259. *
  3260. * This routine multiplexes on the option type, then calls the appropriate
  3261. * insertion routine to store the given option in the outgoing message buffer.
  3262. *
  3263. * RETURNS: 0 if option inserted, or negative value on error.
  3264. *
  3265. * ERRNO: N/A
  3266. *
  3267. * NOMANUAL
  3268. */
  3269. static int insert_opt
  3270.     (
  3271.     struct dhcp_resource *res,  /* pointer to lease descriptor */
  3272.     u_long lease,  /* lease duration, in seconds */
  3273.     int tagnum,  /* tag value of option */
  3274.     char *inserted,  /* bitmap of inserted options */
  3275.     char flag  /* if ACTIVE, marks non-default options */
  3276.     )
  3277.     {
  3278.     if (tagnum < _DHCP_PAD_TAG || tagnum > _DHCP_LAST_OPTION || 
  3279.         ins_opt [tagnum] == NULL)
  3280.         return (-1);
  3281.     return ( (*ins_opt [tagnum]) (res, lease, tagnum, inserted, flag));
  3282.     }
  3283. /*******************************************************************************
  3284. *
  3285. * insert_it - transfer data to outgoing message buffer
  3286. *
  3287. * This routine is called by all the type-specific insertion routines defined
  3288. * above to store option data in the appropriate buffer for the outgoing
  3289. * message. If possible, the message is added to the options field of the
  3290. * standard DHCP message. If that field is full, the sname or file field are
  3291. * used to store the option, if available. Otherwise, the option is stored in
  3292. * an overflow buffer containing the excess bytes the client can receive, if
  3293. * any.
  3294. *
  3295. * RETURNS: 0 if option stored, or E_NOMORE if buffers not available.
  3296. *
  3297. * ERRNO: N/A
  3298. *
  3299. * NOMANUAL
  3300. */
  3301. static int insert_it
  3302.     ( 
  3303.     char *opt  /* pointer to buffer containing option */
  3304.     )
  3305.     {
  3306.     char len = 0;
  3307.     int done = 0;
  3308.     len = opt[1] + 2;   /* 2 == tag number and length field */
  3309.     if (off_options + len < maxoptlen && off_options + len < DFLTOPTLEN) 
  3310.         {
  3311.         bcopy (opt, &dhcpsMsgOut.dhcp->options [off_options], len);
  3312.         off_options += len;
  3313.         return (0);
  3314.         }
  3315.     else if ( (overload & FILE_ISOPT) != 0 && off_file + len < MAX_FILE) 
  3316.         {
  3317.         bcopy (opt, &dhcpsMsgOut.dhcp->file [off_file], len);
  3318.         off_file += len;
  3319.         return (0);
  3320.         }
  3321.     else if ((overload & SNAME_ISOPT) != 0 && off_sname + len < MAX_SNAME) 
  3322.         {
  3323.         bcopy (opt, &dhcpsMsgOut.dhcp->sname [off_sname], len);
  3324.         off_sname += len;
  3325.         return (0);
  3326.         }
  3327.     else if (len < maxoptlen - off_options - off_extopt) 
  3328.         {
  3329.         if (maxoptlen > DFLTOPTLEN) 
  3330.             {
  3331.             sbufvec[1].iov_len = maxoptlen - DFLTOPTLEN;
  3332.             /* off_options never exceeds DFLTOPTLEN. */
  3333.             done = DFLTOPTLEN - off_options;
  3334.             if (done > 0)           /* Only true for first overflow entry. */
  3335.                 {
  3336.                 bcopy (opt, &dhcpsMsgOut.dhcp->options[off_options], done);
  3337.                 len -= done;
  3338.                 off_options += done; /* off_options offset is now invalid. */
  3339.                 }
  3340.             
  3341.             /* done = 0 for all but first overflow entries. */
  3342.             bcopy (&opt [done], &sbufvec[1].iov_base[off_extopt], len);
  3343.             off_extopt += len;
  3344.             return (0);
  3345.             }
  3346.         }
  3347.     /* Option not stored - no space found. */
  3348.     if ( (off_options + off_extopt >= maxoptlen) &&
  3349.          ((overload & FILE_ISOPT) == 0 || off_file >= MAX_FILE) &&
  3350.          ((overload & SNAME_ISOPT) == 0 || off_sname >= MAX_SNAME))
  3351.         return (E_NOMORE);
  3352.     return (-1);
  3353.     }
  3354. /*******************************************************************************
  3355. *
  3356. * cidcmp - compare client identifiers
  3357. *
  3358. * This routine checks if two client identifiers match. It is used before 
  3359. * assigning manual lease entries. The test fails if the identifiers do not
  3360. * match or if the client with the same identifier is on a new subnet.
  3361. *
  3362. * RETURNS: TRUE if identifier and subnet matches, or FALSE otherwise.
  3363. *
  3364. * ERRNO: N/A
  3365. *
  3366. * NOMANUAL
  3367. */
  3368. static int cidcmp
  3369.     (
  3370.     struct client_id *cid1,  /* pointer to assigned client ID */
  3371.     struct client_id *cid2  /* pointer to requesting client ID */
  3372.     )
  3373.     {
  3374.     return (cid1->subnet.s_addr == cid2->subnet.s_addr &&
  3375.     cid1->idtype == cid2->idtype && cid1->idlen == cid2->idlen &&
  3376.     bcmp (cid1->id, cid2->id, cid1->idlen) == 0);
  3377.     }
  3378. /*******************************************************************************
  3379. *
  3380. * icmp_check - verify IP address not in use
  3381. *
  3382. * This routine attempts to verify a selected IP address is actually available
  3383. * before sending an offer to a client. It generates an ICMP request and 
  3384. * waits 1/2 of a second for a reply. If no reply is received, the test is 
  3385. * passed. At other times (after offers are sent), the test is automatically
  3386. * passed.
  3387. *
  3388. * RETURNS: GOOD if no response received or if test unneeded, or BAD otherwise.
  3389. *
  3390. * ERRNO: N/A
  3391. *
  3392. * NOMANUAL
  3393. */
  3394. static int icmp_check
  3395.     (
  3396.     int msgtype,  /* DHCP message type received */
  3397.     struct in_addr *ip  /* IP address to test */
  3398.     )
  3399.     {
  3400.     struct sockaddr_in dst;
  3401.     struct sockaddr_in from;
  3402.     struct icmp * sicmp = NULL;
  3403.     struct icmp * ricmp = NULL;
  3404.     struct ip * ipp = NULL;
  3405.     char rcvbuf [1024];
  3406.     char sndbuf [ICMP_MINLEN];
  3407.     int rlen = 0;
  3408.     int fromlen = 0;
  3409.     int i = 0;
  3410.     int sockfd = 0;
  3411.     u_short pid = 0;
  3412. #ifdef DHCPS_DEBUG
  3413.     char output [INET_ADDR_LEN];
  3414. #endif
  3415. #ifdef NOICMPCHK
  3416.     return (GOOD);
  3417. #endif
  3418.     if (msgtype != DHCPDISCOVER)
  3419.         return (GOOD);
  3420.     bzero ( (char *)&dst, sizeof (dst));
  3421.     bzero ( (char *)&from, sizeof (from));
  3422.     bzero (sndbuf, sizeof (sndbuf));
  3423.     bzero (rcvbuf, sizeof (rcvbuf));
  3424.     sicmp = (struct icmp *) sndbuf;
  3425.     pid = (short)taskIdSelf () & 0xffff;
  3426.     if ( (sockfd = socket (PF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) 
  3427.         {
  3428. #ifdef DHCPS_DEBUG
  3429.         logMsg ("Warning: couldn't open socket in icmp_check()n",
  3430.                  0, 0, 0, 0, 0, 0);
  3431. #endif
  3432.         return (GOOD);
  3433.         }
  3434.     delarp (ip, sockfd);
  3435.   
  3436.     i = 1;
  3437.     if (ioctl (sockfd, FIONBIO, (int)&i) < 0) 
  3438.         {
  3439. #ifdef DHCPS_DEBUG
  3440.         logMsg ("Warning: couldn't set non-blocking I/O in icmp_check()n",
  3441.                  0, 0, 0, 0, 0, 0);
  3442. #endif
  3443.         return(GOOD);
  3444.         }
  3445.     dst.sin_family = AF_INET;
  3446.     dst.sin_addr.s_addr = ip->s_addr;
  3447.     sicmp->icmp_type = ICMP_ECHO;
  3448.     sicmp->icmp_code = 0;
  3449.     sicmp->icmp_cksum = 0;
  3450.     sicmp->icmp_id = pid;
  3451.     sicmp->icmp_seq = 0;
  3452.     sicmp->icmp_cksum = checksum ( (u_short *)sndbuf, sizeof (sndbuf));
  3453.     fromlen = sizeof (from);
  3454.     i = sendto (sockfd, sndbuf, sizeof (sndbuf), 0,
  3455.                 (struct sockaddr *) &dst, sizeof (dst));
  3456.     if (i < 0 || i != sizeof (sndbuf)) 
  3457.         {
  3458. #ifdef DHCPS_DEBUG
  3459.         logMsg ("Warning: Can't send icmp echo request.n", 0, 0, 0, 0, 0, 0);
  3460. #endif
  3461.         return (GOOD);
  3462.         }
  3463.     /* Wait half a second for an ICMP reply */ 
  3464.     taskDelay (sysClkRateGet () / 2);
  3465.     FOREVER
  3466.         { 
  3467.         rlen = recvfrom (sockfd, rcvbuf, sizeof (rcvbuf), 0,
  3468.          (struct sockaddr *)&from, &fromlen);
  3469.         if (rlen < 0)
  3470.             break;
  3471.         ipp = (struct ip *) rcvbuf;
  3472.         if (rlen < (ipp->ip_hl << 2) + ICMP_MINLEN) 
  3473.             {
  3474.             continue;
  3475.             }
  3476.         ricmp = (struct icmp *) (rcvbuf + (ipp->ip_hl << 2));
  3477.         if (ricmp->icmp_type != ICMP_ECHOREPLY) 
  3478.             {
  3479.             continue;
  3480.             }
  3481.         if (ricmp->icmp_id == pid) 
  3482.             {
  3483.             break;
  3484.             }
  3485.         }
  3486.     close (sockfd);
  3487.     if (ricmp != NULL && ricmp->icmp_id == pid) 
  3488.         {
  3489.         errno = 0;
  3490. #ifdef DHCPS_DEBUG
  3491.         inet_ntoa_b (*ip, output);
  3492.         logMsg ("Warning: IP address %s may be in use.n", (int)output,
  3493.                  0, 0, 0, 0, 0);
  3494. #endif
  3495.         return (BAD);
  3496.         }
  3497.     return (GOOD);
  3498.     }
  3499. /*******************************************************************************
  3500. *
  3501. * free_bind - remove DHCP binding
  3502. *
  3503. * This routine removes the record of a lease binding from the internal linked
  3504. * list before either adding a new record (if an offer was accepted) or as a
  3505. * result of garbage collection, if the lease has expired.
  3506. *
  3507. * RETURNS: 0, always.
  3508. *
  3509. * ERRNO: N/A
  3510. *
  3511. * NOMANUAL
  3512. */
  3513. static int free_bind
  3514.     (
  3515.     struct hash_member *hash_m  /* hash table entry to delete */
  3516.     )
  3517.     {
  3518.     struct dhcp_binding *bp = NULL;
  3519.     struct dhcp_binding *cbp = NULL;
  3520.     struct hash_member *current = NULL;
  3521.     struct hash_member *previous = NULL;
  3522.     bp = (struct dhcp_binding *)hash_m->data;
  3523.     previous = current = bindlist;
  3524.     /* Remove target element from binding database, if present. */
  3525.     while (current != NULL) 
  3526.        {
  3527.        cbp = (struct dhcp_binding *)current->data;
  3528.        if (cbp != NULL && cidcmp (&bp->cid, &cbp->cid) == TRUE)
  3529.            break;
  3530.        else 
  3531.            {
  3532.            previous = current;
  3533.            current = current->next;
  3534.            }
  3535.        }
  3536.     /* Mark end of list if given entry not found. */
  3537.     if (current == NULL) 
  3538.         {
  3539.         if (previous != NULL) 
  3540.             previous->next = NULL;
  3541.         } 
  3542.     /* Reset list pointers if given entry found. */
  3543.     else 
  3544.         {
  3545.         /* Set pointer for new head of list. */
  3546.         if (current == bindlist) 
  3547.             bindlist = current->next;
  3548.         else 
  3549.             /* Set pointer to skip target element within list. */
  3550.             if (previous != NULL) 
  3551.         previous->next = current->next;
  3552.         free (current);             /* Remove target element. */
  3553.         }
  3554.     /* Remove target element from hash table. */
  3555.     if (bp->res != NULL) 
  3556.         {
  3557.         bp->res->binding = NULL;
  3558.         bp->res = NULL;
  3559.         }
  3560.     free (bp);
  3561.     free (hash_m);
  3562.     nbind--;
  3563.     return (0);
  3564.     }
  3565. /*******************************************************************************
  3566. *
  3567. * free_fake - simulate removal of DHCP binding
  3568. *
  3569. * This routine decreases the number of identified active bindings when the 
  3570. * server detects an IP address is unexpectedly in use. However, the record of 
  3571. * the active lease is not removed.
  3572. *
  3573. * RETURNS: 0, always.
  3574. *
  3575. * ERRNO: N/A
  3576. *
  3577. * NOMANUAL
  3578. */
  3579. static int free_fake
  3580.     (
  3581.     struct hash_member *hash_m
  3582.     )
  3583.     {
  3584.     nbind--;
  3585.     return (0);
  3586.     }
  3587. /*******************************************************************************
  3588. *
  3589. * garbage_collect - store a snapshot of known client states
  3590. *
  3591. * This timer-driven routine is called after each iteration of the 
  3592. * message-processing loop. If the time specified by GC_INTERVAL (currently
  3593. * 10 minutes) has elapsed since the previous call, the routine updates the
  3594. * server internal data structures. It accesses the user-supplied cache hook to 
  3595. * transfer a record of the active or offered leases to permanent storage. It 
  3596. * then removes all incomplete entries, and as many expired entries as necessary
  3597. * to reduce the internal table size below the maximum number of entries 
  3598. * (currently 500).
  3599. *
  3600. * RETURNS: N/A
  3601. *
  3602. * ERRNO: N/A
  3603. *
  3604. * NOMANUAL
  3605. */
  3606. static void garbage_collect (void)
  3607.     {
  3608. #define MTOB(X)   ((struct dhcp_binding *)((X)->data))
  3609.     struct hash_member *bindptr = NULL,
  3610.                        *oldest = NULL;
  3611.     struct dhcp_binding *tmpptr;
  3612.     time_t curr_epoch = 0;
  3613.     static time_t prev_epoch = 0;
  3614.     if (dhcpTime (&curr_epoch) == -1) 
  3615.         return;
  3616.     /* Exit if interval has not expired. (Currently ten minutes). */
  3617.     if (curr_epoch - prev_epoch < GC_INTERVAL)
  3618.         return;
  3619.     prev_epoch = curr_epoch;
  3620.     /* Critical section with dhcpsLeaseEntryAdd(). */
  3621.     semTake (dhcpsMutexSem, WAIT_FOREVER);
  3622.     dump_bind_db ();    /* Write current bindings to permanent storage. */
  3623.     bindptr = bindlist;
  3624.     while (bindptr != NULL) 
  3625.        {
  3626.        tmpptr = MTOB (bindptr);
  3627.        if ((tmpptr->flag & COMPLETE_ENTRY) == 0 &&
  3628.     tmpptr->temp_epoch < curr_epoch) 
  3629.            {
  3630.            hash_del (&cidhashtable, tmpptr->cid.id, tmpptr->cid.idlen, 
  3631.                      bindcidcmp, &tmpptr->cid, free_bind);
  3632.            }
  3633.        bindptr = bindptr->next;
  3634.        }
  3635.     /* Find and delete oldest expired entry if table exceeds size limit. */
  3636.     while (nbind > MAX_NBIND) 
  3637.         {
  3638.         bindptr = bindlist;
  3639.         while (bindptr != NULL) 
  3640.             {
  3641.             tmpptr = MTOB (bindptr);
  3642.             if ( (tmpptr->flag & COMPLETE_ENTRY) != 0 &&
  3643.          tmpptr->expire_epoch != 0xffffffff &&
  3644.          tmpptr->expire_epoch < curr_epoch &&
  3645.          (oldest == NULL || 
  3646.                   tmpptr->expire_epoch < MTOB (oldest)->expire_epoch)) 
  3647.         oldest = bindptr;
  3648.             bindptr = bindptr->next;
  3649.             }
  3650.         if (oldest == NULL)
  3651.             {
  3652.             semGive (dhcpsMutexSem);
  3653.             return;
  3654.             }
  3655.         else 
  3656.             {
  3657.             tmpptr = MTOB (oldest);
  3658.             hash_del (&cidhashtable, tmpptr->cid.id, tmpptr->cid.idlen, 
  3659.                       bindcidcmp, &tmpptr->cid, free_bind);
  3660.             oldest = NULL;
  3661.             }
  3662.         }
  3663.     semGive (dhcpsMutexSem);
  3664.     return;
  3665.     }
  3666. /*******************************************************************************
  3667. *
  3668. * nvttostr - convert NVT ASCII to strings
  3669. *
  3670. * This routine implements a limited NVT conversion which removes any embedded
  3671. * NULL characters from the input string.
  3672. *
  3673. * RETURNS: 0, always. 
  3674. *
  3675. * ERRNO: N/A
  3676. *
  3677. * NOMANUAL
  3678. */
  3679. static int nvttostr
  3680.     (
  3681.     char *nvtstr,  /* input buffer, possibly has embedded NULLs */
  3682.     char *str,  /* output buffer for converted string */
  3683.     int length  /* number of characters in input buffer */
  3684.     )
  3685.     {
  3686.     FAST int i = 0;
  3687.     FAST char *tmp = NULL;
  3688.     tmp = str;
  3689.     for (i = 0; i < length; i++) 
  3690.         if (nvtstr[i] != (char)NULL) 
  3691.             {
  3692.             *tmp = nvtstr[i];
  3693.             tmp++;
  3694.             }
  3695.     str [length] = '';
  3696.     return (0);
  3697.     }