/* ****************************************************************************** * INTERPEAK SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipdhcps_db.c,v $ $Revision: 1.54.10.14 $ * $Source: /home/interpeak/CVSRoot/ipdhcps/src/ipdhcps_db.c,v $ * $Author: jwang9 $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * * Design and implementation by Jonas Rendel ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #include "ipdhcps_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #include #include #include #include #include "ipdhcps.h" #include "ipdhcps_h.h" /* **************************************************************************** * 4 DEFINES **************************************************************************** */ /* *=========================================================================== * Hash callback orders *=========================================================================== */ #define IPDHCPS_HASH_CB_ORDER_DELETE (0) #define IPDHCPS_HASH_CB_ORDER_FIND_HOST (1) #define IPDHCPS_HASH_CB_ORDER_CHECK_EXPIRY (2) #define IPDHCPS_HASH_CB_ORDER_UPDATE_OPTION_LIST (3) /* *=========================================================================== * Sec -> msec convert macro *=========================================================================== */ #define IPDHCPS_MSEC(time) (time * 1000) /* **************************************************************************** * 5 TYPES **************************************************************************** */ /* *=========================================================================== * Ipdhcps_host_key_struct *=========================================================================== * Contains all data needed to create/find a host lease in the lease database * */ typedef struct Ipdhcps_host_key_struct { Ipdhcps_subnet *subnet; Ip_size_t hlen; Ip_u8 *haddr; Ip_size_t cid_len; Ip_u8 *cid; } Ipdhcps_host_key; /* *=========================================================================== * Ipdhcps_hash_cb_info_struct *=========================================================================== * Used for passing data in and out from hash callback routines */ typedef struct Ipdhcps_hash_cb_info_struct { Ip_u32 order; /* Hash callback order */ void* in_data; /* Pointer to data forwarded to callback function */ void* out_data; /* Pointer to data forwarded from callback function */ } Ipdhcps_hash_cb_info; /* *=========================================================================== * Ipdhcps_subnet_find_struct *=========================================================================== * Used for passing data in and out from subnet hash table */ typedef struct Ipdhcps_subnet_find_info_struct { Ip_u32 addr; Ip_bool found; Ipdhcps_subnet *subnet; } Ipdhcps_subnet_find_info; /* *=========================================================================== * Ipdhcps_host_lease_find_struct *=========================================================================== * Used for passing data in and out from lease hash table */ typedef struct Ipdhcps_host_lease_find_info_struct { Ip_u32 addr; Ip_bool found; } Ipdhcps_host_lease_find_info; /* *=========================================================================== * Ipdhcps_class_find_id_struct *=========================================================================== * Used for searching the class hash tables. */ typedef struct Ipdhcps_class_find_id_struct { char *name; Ipdhcps_class *client_class; } Ipdhcps_class_find_id; /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ IP_EXTERN Ipdhcps_data ipdhcpsc; IP_EXTERN const char *ipdhcps_binding_state_str[]; /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ IP_STATIC void ipdhcps_list_init(Ipdhcps_list *head); IP_STATIC void ipdhcps_list_remove_from(void *entry); IP_STATIC void ipdhcps_list_add_to(void *h, void *entry); IP_STATIC void ipdhcps_lease_table_free(Ipcom_hash *ht); IP_STATIC void ipdhcps_lease_table_cb(Ipdhcps_lease *l, Ipdhcps_hash_cb_info *info); IP_STATIC unsigned ipdhcps_do_hash(Ip_size_t len, Ip_u8 *data); IP_STATIC Ip_err ipdhcps_subnet_do_delete(Ipdhcps_subnet *s); IP_STATIC void ipdhcps_subnet_delete_cb(Ipdhcps_subnet *s, void *data); IP_STATIC Ipdhcps_subnet *ipdhcps_subnet_new(Ip_u32 subnet); IP_STATIC unsigned ipdhcps_subnet_hash(Ipdhcps_subnet* s); IP_STATIC unsigned ipdhcps_subnet_key_hash(Ip_u32 *key); IP_STATIC Ip_bool ipdhcps_subnet_match(Ipdhcps_subnet *s, Ip_u32 *key); IP_STATIC void ipdhcps_interface_subnet_map(Ipdhcps_subnet *subnet); IP_STATIC unsigned ipdhcps_host_hash(Ipdhcps_host *host); IP_STATIC Ipdhcps_host* ipdhcps_host_new(Ip_u32 inaddr, Ip_size_t hwa_len, Ip_u8 *hwaddr, Ip_size_t cid_len, Ip_u8 *cid); IP_STATIC Ip_err ipdhcps_host_do_delete(Ipdhcps_host *h); IP_STATIC void ipdhcps_host_delete_cb(Ipdhcps_host *h, void *data); IP_STATIC void ipdhcps_option_set_do_delete(Ipdhcps_option *o, Ipdhcps_node *node); IP_STATIC unsigned ipdhcps_lease_hash(void *obj); IP_STATIC unsigned ipdhcps_host_key_hash(Ip_u32 *key); IP_STATIC Ip_bool ipdhcps_host_lease_match(void *obj, void* key); IP_STATIC Ip_err ipdhcps_pool_do_delete(Ipdhcps_pool *p); IP_STATIC Ip_err ipdhcps_pool_new(Ipdhcps_subnet *s, Ipdhcps_list *head, Ip_u32 addr_min, Ip_u32 addr_max, char *classname); IP_STATIC unsigned ipdhcps_class_hash(Ipdhcps_class *c); IP_STATIC Ip_err ipdhcps_class_new(Ipdhcps_subnet *s, Ipdhcps_classinfo *client_class); IP_STATIC Ip_bool ipdhcps_pool_config_sane(Ipdhcps_list *head, Ip_u32 addr_min, Ip_u32 arr_max); IP_STATIC Ip_err ipdhcps_option_set_add(Ipdhcps_node *node, Ip_u8 *options, Ip_u16 type); IP_STATIC void ipdhcps_host_lease_cmp(Ipdhcps_lease *l, Ipdhcps_host_lease_find_info *info); /* **************************************************************************** * 8 DATA **************************************************************************** */ IP_STATIC char logbuf[50]; /* **************************************************************************** * 9 STATIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipdhcps_lease_table_cb *=========================================================================== * Description: Common hash table callback function. * * Parameters: obj - pointer to hash object. * data - pointer to hash callback info struct * * Returns: - * */ IP_STATIC void ipdhcps_lease_table_cb(Ipdhcps_lease *l,Ipdhcps_hash_cb_info *info) { Ip_u32 now; switch(info->order) { case IPDHCPS_HASH_CB_ORDER_DELETE: (void)ipdhcps_lease_delete(l); break; case IPDHCPS_HASH_CB_ORDER_FIND_HOST: if(*(Ip_u32*)(info->in_data) == l->yiaddr) info->out_data = l; break; case IPDHCPS_HASH_CB_ORDER_CHECK_EXPIRY: now = (Ip_u32)ipcom_time(0); if(now >= (Ip_u32)l->expires) { if(l->binding_state == IPDHCPS_BINDING_STATE_ACTIVE) { /* This lease has expired */ l->binding_state = IPDHCPS_BINDING_STATE_EXPIRED; /* Avoid overflow */ if(now > now + ipdhcpsc.config.in_expired_state_max_time) now = 0; l->expires = now + ipdhcpsc.config.in_expired_state_max_time; } else if((l->binding_state == IPDHCPS_BINDING_STATE_ABANDONED) || (l->binding_state == IPDHCPS_BINDING_STATE_OFFERED) || (l->binding_state == IPDHCPS_BINDING_STATE_RELEASED) || (l->binding_state == IPDHCPS_BINDING_STATE_EXPIRED) || (l->binding_state == IPDHCPS_BINDING_STATE_BOOTP)) { /* This lease can be used again. Release it.*/ IPCOM_LOG2(DEBUG, "ipdhcps_daemon :: freeing %s lease with address %s", ipdhcps_binding_state_str[l->binding_state], ipcom_inet_ntop(IP_AF_INET, &l->yiaddr, logbuf, sizeof(logbuf))); (void)ipdhcps_lease_delete(l); } } break; default: break; } } /* *=========================================================================== * ipdhcps_lease_table_free *=========================================================================== * Description: Releases all elements in a hash table and finally frees * the hash table itself * * Parameters: ht - Pointer to hash table. * Returns: - * */ IP_STATIC void ipdhcps_lease_table_free(Ipcom_hash* ht) { Ipdhcps_hash_cb_info info; info.order = IPDHCPS_HASH_CB_ORDER_DELETE; ipcom_hash_for_each(ht, (Ipcom_hash_foreach_cb_func)ipdhcps_lease_table_cb, &info); ipcom_hash_delete(ht); } /* *=========================================================================== * ipdhcps_list_init *=========================================================================== * Description: Initiates an linked list used by IPDHCPS * Parameters: head - pointer to list head * Returns: - * */ IP_STATIC void ipdhcps_list_init(Ipdhcps_list *head) { head->next = IP_NULL; head->prev = IP_NULL; } /* *=========================================================================== * ipdhcps_list_remove_from *=========================================================================== * Description: Removes an entry from a linked list used by IPDHCPS. * * Parameters: entry p- pointer to entry in list. * Returns: - * */ IP_STATIC void ipdhcps_list_remove_from(void *entry) { Ipdhcps_list *node =(Ipdhcps_list*) entry; if(node->prev != IP_NULL) node->prev->next = node->next; if(node->next != IP_NULL) node->next->prev = node->prev; } /* *=========================================================================== * ipdhcps_list_add_to *=========================================================================== * Description: Adds an entry to a linked list used by IPDHCPS. * * Parameters: h - pointer to list head * entry - pointer to list entry * Returns: - * */ IP_STATIC void ipdhcps_list_add_to(void *h, void *entry) { Ipdhcps_list *head =(Ipdhcps_list*)h; Ipdhcps_list *node =(Ipdhcps_list*)entry; node->next = head->next; node->prev = head; if(node->next != IP_NULL) node->next->prev = entry; if(node->prev != IP_NULL) node->prev->next = entry; } /* *=========================================================================== * ipdhcps_host_hash *=========================================================================== * Description: Hash calculation routine for hashing subnets. * Parameters: obj - pointer to subnet descriptor. * Returns: hash result * */ IP_STATIC unsigned ipdhcps_host_hash(Ipdhcps_host *host) { return(unsigned)(host->lease.yiaddr); } /* *=========================================================================== * ipdhcps_host_key_hash *=========================================================================== * Description: Hash calculation routine for hashing subnet keys. * Parameters: obj - pointer to key descriptor. * Returns: hash result * */ IP_STATIC unsigned ipdhcps_host_key_hash(Ip_u32 *key) { return(unsigned) *key; } /* *=========================================================================== * ipdhcps_host_match *=========================================================================== * Description: Host to key match routine. Called when searching for a * host in host database. * * Parameters: obj - pointer to subnet descriptor * key - pointer to key(ip address) * * Returns: IP_TRUE - match * IP_FALSE - no match */ IP_STATIC Ip_bool ipdhcps_host_match(Ipdhcps_host *host, Ip_u32 *key) { if (host->lease.yiaddr == *key) return IP_TRUE; else return IP_FALSE; } /* *=========================================================================== * ipdhcps_host_do_delete *=========================================================================== * Description: Host destructor/checkout callback. Removes the host * descriptor from the database and releases all its allocated * resources. * * Parameters: obj - pointer to subnet descriptor. * * Returns: IPCOM_SUCCESS - success * other - failure * */ IP_STATIC Ip_err ipdhcps_host_do_delete(Ipdhcps_host *h) { Ip_err retval; Ipdhcps_pool *p; Ipdhcps_subnet* subnet= IP_NULL; Ip_u32 req_addr=0; /* Remove from hash table */ retval = ipcom_hash_remove(ipdhcpsc.host_head, h); req_addr = h->lease.yiaddr; /* Remove from lease data base */ ipdhcps_lease_delete(&h->lease); subnet = ipdhcps_subnet_find_matching(req_addr); if(subnet != IP_NULL) { req_addr = ip_ntohl(req_addr); IPDHCPS_LIST_FOREACH(p, (&subnet->pools), Ipdhcps_pool*) { if((req_addr <= p->yiaddr_max) && (req_addr >= p->yiaddr_min)) { p->leases[req_addr - p->yiaddr_min]->binding_state = IPDHCPS_BINDING_STATE_FREE; } } } if(retval == IPCOM_SUCCESS) { /* Delete this record and all its resources */ if (h->node.option_head != IP_NULL) { ipcom_hash_for_each(h->node.option_head, (Ipcom_hash_foreach_cb_func)ipdhcps_option_set_do_delete, &h->node); ipcom_hash_delete(h->node.option_head); } ipcom_free(h); } return retval; } /* *=========================================================================== * ipdhcps_host_delete_cb *=========================================================================== * Description: * * Parameters: h - host descriptor. * data - not used. * * Returns: * */ IP_STATIC void ipdhcps_host_delete_cb(Ipdhcps_host *h, void *data) { (void)data; (void)ipdhcps_host_do_delete(h); } /* *=========================================================================== * ipdhcps_host_new *=========================================================================== * Description: Host descriptor contructor function. * * Parameters: inaddr - IP address of host. * hwa_len - lenght of hosts link layer address * hwaddr - pointer to hosts link layer address * cid_len - length of host client identifier * cid - pointer to client identifier * * Returns: pointer to host descriptor - success * IP_NULL - failure */ IP_STATIC Ipdhcps_host * ipdhcps_host_new(Ip_u32 inaddr, Ip_size_t hwa_len, Ip_u8 *hwaddr, Ip_size_t cid_len, Ip_u8 *cid) { Ipdhcps_host *h; Ipdhcps_pool *p; Ipdhcps_subnet* subnet= IP_NULL; Ip_u32 req_addr=0; h = ipcom_malloc(sizeof(Ipdhcps_host)); if(h != IP_NULL) { ipcom_memset(h, 0, sizeof(Ipdhcps_host)); if(cid != IP_NULL) { if (cid[0] == '\\' && cid[1] == '0') { cid_len--; cid++; cid[0] = '\0'; } /* Allocate space for a user identifier */ h->lease.cid = ipcom_malloc(cid_len + 1); if(h->lease.cid == IP_NULL) { ipcom_free(h); return (Ipdhcps_host*)IP_NULL; } ipcom_memcpy(h->lease.cid, cid, cid_len); h->lease.cid[cid_len] = 0; h->lease.cid_len = cid_len; } /* Init remaining parts of host lease descriptor */ ipcom_memcpy(h->lease.chaddr.haddr, hwaddr, hwa_len); h->lease.chaddr.hlen = hwa_len; h->lease.yiaddr = inaddr; h->lease.binding_state = IPDHCPS_BINDING_STATE_PERMANENT; h->lease.lease_time = (Ip_time_t)IPDHCPS_LEASE_TIME_PERMANENT; h->lease.parent = &h->node; /* Attach host to host list*/ if (ipdhcpsc.host_head == IP_NULL) { ipdhcpsc.host_head = ipcom_hash_new((Ipcom_hash_obj_func)ipdhcps_host_hash, (Ipcom_hash_key_func)ipdhcps_host_key_hash, (Ipcom_hash_cmp_func)ipdhcps_host_match); if (ipdhcpsc.host_head == IP_NULL) { ipcom_free(h); return IP_NULL; } } ipcom_hash_add(ipdhcpsc.host_head, h); /* Add to permanent lease to database */ ipdhcps_lease_add(&h->lease); req_addr = ip_ntohl(inaddr); subnet = ipdhcps_subnet_find_matching(inaddr); if(subnet != IP_NULL) { IPDHCPS_LIST_FOREACH(p, (&subnet->pools), Ipdhcps_pool*) { if((req_addr <= p->yiaddr_max) && (req_addr >= p->yiaddr_min)) { p->leases[req_addr - p->yiaddr_min]->binding_state = IPDHCPS_BINDING_STATE_PERMANENT; } } } } return h; } /* *=========================================================================== * ipdhcps_do_hash *=========================================================================== * Description: Common hash calculation routine used when hashing leases * * Parameters: len - length of key to hash * data - key to hash * * Returns: hash result * */ IP_STATIC unsigned ipdhcps_do_hash(Ip_size_t len, Ip_u8 *data) { Ip_size_t i; unsigned result = 0; for(i = 0; i < len; i++) result = result * 32 + (unsigned)data[i]; return result; } /* *=========================================================================== * ipdhcps_lease_hash *=========================================================================== * Description: Lease hash routine. Determines if to hash on client * identifier or link layer address. * * Parameters: obj - Pointer to lease * * Returns: hash result */ IP_STATIC unsigned ipdhcps_lease_hash(void *obj) { Ipdhcps_lease *lease = (Ipdhcps_lease*)obj; unsigned result; if(lease->cid != IP_NULL) { /* Create hash value based on client identifier */ result = ipdhcps_do_hash(lease->cid_len, lease->cid); } else { /* Create hash value based on hwaddr */ result = ipdhcps_do_hash(lease->chaddr.hlen, lease->chaddr.haddr); } return(result); } /* *=========================================================================== * ipdhcps_lease_key_hash *=========================================================================== * Description: Lease key hash routine. Generates a hash value based on * client identifier or link layer address. * * Parameters: key - pointer to host key structure * * Returns: hash result */ IP_STATIC unsigned ipdhcps_host_lease_key_hash(Ipdhcps_host_key *k) { unsigned result; if(k->cid != IP_NULL) { /* Create hash value based on client identifier */ result = ipdhcps_do_hash(k->cid_len, k->cid); } else { /* Create hash value based on hwaddr */ result = ipdhcps_do_hash(k->hlen, k->haddr); } return (result); } /* *=========================================================================== * ipdhcps_host_lease_match *=========================================================================== * Description: Lease to host matching routine. Called when searching for * a lease in the lease database. * Verifies that: * 1. lease is not abandoned * 2. cid of key and lease is the same * and/or * 3. link layer address of key and lease is the same * 4. lease IP address is on the same subnet as * calling client. * * Parameters: obj - pointer to lease descriptor. * key - pointer to host key descriptor. * * Returns: IP_TRUE - key and lease matches * IP_FALSE - key and lease do not match * */ IP_STATIC Ip_bool ipdhcps_host_lease_match(void *obj, void* key) { Ip_bool retval; Ip_u32 mask = 0; Ipdhcps_host_key *k = (Ipdhcps_host_key*)key; Ipdhcps_lease *lease = (Ipdhcps_lease*)obj; Ipdhcps_option *o; if(lease->binding_state == IPDHCPS_BINDING_STATE_ABANDONED) IPDHCPS_LEAVE(IP_FALSE); if(k->cid_len != lease->cid_len) IPDHCPS_LEAVE(IP_FALSE); if(k->cid_len > 0) { /* Do a user id verification */ if(ipcom_memcmp(k->cid, lease->cid, k->cid_len) != 0) IPDHCPS_LEAVE(IP_FALSE); if((k->hlen > 0) &&(lease->chaddr.hlen > 0)) { /* Do a hw address verification too */ if(ipcom_memcmp(k->haddr, lease->chaddr.haddr, k->hlen) != 0) IPDHCPS_LEAVE(IP_FALSE); } } else if(k->hlen > 0) { /* Try a hw address verification instead*/ if(k->hlen != lease->chaddr.hlen) IPDHCPS_LEAVE(IP_FALSE); /* Do a hw address verification */ if(ipcom_memcmp(k->haddr, lease->chaddr.haddr, k->hlen) != 0) IPDHCPS_LEAVE(IP_FALSE); } else { /* Nothing to verify on */ IPDHCPS_LEAVE(IP_FALSE); } /* So, far we have a match. Check if we have a subnet descriptor */ if(ipdhcpsc.config.no_strict_subnet_match == IP_FALSE && (k->subnet != IP_NULL) &&(k->subnet != ipdhcpsc.default_net)) { o = ipdhcps_option_set_find(&k->subnet->node, IPDHCPS_OPTCODE_SUBNET_MASK); if(o != IP_NULL) ipcom_memcpy(&mask, &o->opt[IPDHCPS_OPT_DATA], o->opt[IPDHCPS_OPT_LEN]); /* Check if the host address is on this subnet */ if((lease->yiaddr & mask) != k->subnet->addr) IPDHCPS_LEAVE(IP_FALSE); } /* We have a match */ retval = IP_TRUE; leave: return retval; } /* *=========================================================================== * ipdhcps_class_hash *=========================================================================== * Description: Hash calculation routine for hashing classes. * Parameters: obj - pointer to subnet descriptor. * Returns: hash result * */ IP_STATIC unsigned ipdhcps_class_hash(Ipdhcps_class *c) { return ipcom_hash_update(c->data, IPDHCPS_OPT_TOTAL_LEN(c->data[IPDHCPS_OPT_LEN]), 0); } /* *=========================================================================== * ipdhcps_class_key_hash *=========================================================================== * Description: Hash calculation routine for hashing class keys. * Parameters: key - pointer to key descriptor. * Returns: hash result * */ IP_STATIC unsigned ipdhcps_class_key_hash(Ip_u8 *key) { return ipcom_hash_update(key, IPDHCPS_OPT_TOTAL_LEN(key[IPDHCPS_OPT_LEN]), 0); } /* *=========================================================================== * ipdhcps_class_match *=========================================================================== * Description: Class to key match routine. Called when searching for a * class in class database. * * Parameters: class - pointer to class descriptor * key - pointer to key * * Returns: IP_TRUE - match * IP_FALSE - no match */ IP_STATIC Ip_bool ipdhcps_class_match(Ipdhcps_class *c, char *key) { if (ipcom_memcmp(c->data, key,IPDHCPS_OPT_TOTAL_LEN(key[IPDHCPS_OPT_LEN])) == 0) return IP_TRUE; else return IP_FALSE; } /* *=========================================================================== * ipdhcps_subnet_do_delete *=========================================================================== * Description: Subnet destructor/checkout callback. Removes the subnet * descriptor from the database and releases all its allocated * resources. * * Parameters: obj - pointer to subnet descriptor. * * Returns: IPCOM_SUCCESS - success * other - failure * */ IP_STATIC Ip_err ipdhcps_subnet_do_delete(Ipdhcps_subnet *s) { Ip_err retval; Ipdhcps_pool *p, *p_next; Ipdhcps_if *i; if(s != IP_NULL) { /* Remove from hash table */ retval = ipcom_hash_remove(ipdhcpsc.subnet_head, s); if(retval == IPCOM_SUCCESS) { /* Delete this record and all its resources */ if (s->node.option_head != IP_NULL) { ipcom_hash_for_each(s->node.option_head, (Ipcom_hash_foreach_cb_func)ipdhcps_option_set_do_delete, &s->node); ipcom_hash_delete(s->node.option_head); } for (p = (Ipdhcps_pool*)s->pools.next; p != IP_NULL; p = p_next) { p_next = (Ipdhcps_pool*)p->node.next; ipdhcps_pool_do_delete(p); } IPDHCPS_LIST_FOREACH(i, (&ipdhcpsc.if_head), Ipdhcps_if*) { if (i->local_subnet == s) i->local_subnet = ipdhcpsc.default_net; } ipcom_free(s); } } else retval = IPCOM_ERR_FAILED; return retval; } /* *=========================================================================== * ipdhcps_subnet_delete_cb *=========================================================================== * Description: * * Parameters: h - subnet descriptor. * data - not used. * * Returns: * */ IP_STATIC void ipdhcps_subnet_delete_cb(Ipdhcps_subnet *s, void *data) { (void)data; (void)ipdhcps_subnet_do_delete(s); } /* *=========================================================================== * ipdhcps_subnet_new *=========================================================================== * Description: Subnet constructor routine. * Parameters: subnet - subnet address. * Returns: pointer to subnet descriptor. * */ IP_STATIC Ipdhcps_subnet * ipdhcps_subnet_new(Ip_u32 subnet) { Ipdhcps_subnet *s; s = ipcom_calloc(1, sizeof(Ipdhcps_subnet)); if(s != IP_NULL) { s->addr = subnet; ipdhcps_list_init(&s->pools); } return s; } /* *=========================================================================== * ipdhcps_pool_find *=========================================================================== * Description: Search routine to find a certain address pool in a * certain pool list. * * Parameters: head - pool list head * addr_min - first address in pool * addr_max - last address in pool * * Returns: pointer to pool descriptor - found * IP_NULL - not found * */ IP_STATIC Ipdhcps_pool* ipdhcps_pool_find(Ipdhcps_list *head, Ip_u32 addr_min, Ip_u32 addr_max) { Ipdhcps_pool *p; IPDHCPS_LIST_FOREACH(p, head, Ipdhcps_pool*) if((p->yiaddr_min == addr_min) && (p->yiaddr_max == addr_max)) break; return (p); } /* *=========================================================================== * ipdhcps_class_search_name *=========================================================================== * Description: Callback search routine to find a certain class. * * Parameters: class - class descriptor * id - identification data * * Returns: pointer to pool descriptor - found * IP_NULL - not found * */ IP_STATIC void ipdhcps_class_search_name(Ipdhcps_class *client_class, Ipdhcps_class_find_id *id) { if (id->client_class == IP_NULL) { if (ipcom_strcmp(client_class->name, id->name) == 0) id->client_class = client_class; } } /* *=========================================================================== * ipdhcps_pool_config_sane *=========================================================================== * Description: Checks if a an address pool specified by first and last * address is ok and does not overlap existing pools in * in a certain list of pools. * * Parameters: head - pointer to pool list head * addr_min - first address * addr_max - last address * * Returns: IP_TRUE - ok * IP_FALSE - not ok */ IP_STATIC Ip_bool ipdhcps_pool_config_sane(Ipdhcps_list *head, Ip_u32 addr_min, Ip_u32 addr_max) { Ipdhcps_pool *p; /* Is the range sane? */ if(addr_max < addr_min) return IP_FALSE; /* Check for pool with overlapping address range */ IPDHCPS_LIST_FOREACH(p, head, Ipdhcps_pool*) if(((addr_min >= p->yiaddr_min) && (addr_min <= p->yiaddr_max)) || ((addr_max >= p->yiaddr_min) && (addr_max <= p->yiaddr_max))) return IP_FALSE; return IP_TRUE; } /* *=========================================================================== * ipdhcps_pool_do_delete *=========================================================================== * Description: Pool destructor/checkout callback. * Parameters: p - pointer to pool descriptor. * * Returns: IPCOM_SUCCESS - success * other - failure */ IP_STATIC Ip_err ipdhcps_pool_do_delete(Ipdhcps_pool *p) { Ip_u32 i; /* Remove pool from list*/ ipdhcps_list_remove_from(p); /* Destroy lease records */ for(i = 0; i <= (p->yiaddr_max - p->yiaddr_min); i++) { /* Deregister from lease database */ ipdhcps_lease_delete(p->leases[i]); ipcom_free(p->leases[i]); } if (p->class_id != IP_NULL) ipcom_free(p->class_id); ipcom_free(p); return IPCOM_SUCCESS; } /* *=========================================================================== * ipdhcps_pool_new *=========================================================================== * Description: Pool constructor routine. * * Parameters: s - pointer to subnet on which to attach pool * head - pointer to pool list head * addr_min - first address in pool * addr_max - last address in pool * * Returns: IPCOM_SUCCESS - success * other - failure */ IP_STATIC Ip_err ipdhcps_pool_new(Ipdhcps_subnet *s, Ipdhcps_list *head, Ip_u32 addr_min, Ip_u32 addr_max, char *classname) { Ip_err retval; Ip_u32 i; Ip_u32 range; Ipdhcps_pool *p; /* Range of addresses from first to last, inclusive */ range = addr_max - addr_min + 1; /* Allocate pool descriptor w. extra space for lease array */ p = ipcom_calloc(1, sizeof(Ipdhcps_pool) + (range * sizeof(Ipdhcps_lease*))); if(p == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NO_MEMORY); p->yiaddr_min = addr_min; p->yiaddr_max = addr_max; if (classname != IP_NULL) { Ipdhcps_class *c = ipdhcps_class_find_by_name(s, classname); if (c == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NOT_FOUND); p->class_id = ipcom_malloc(IPDHCPS_OPT_TOTAL_LEN(c->data[IPDHCPS_OPT_LEN])); if (p->class_id == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NO_MEMORY); ipcom_memcpy(p->class_id, c->data, IPDHCPS_OPT_TOTAL_LEN(c->data[IPDHCPS_OPT_LEN])); } /* Setup lease descriptors */ for(i = 0; i <= range; i++) { p->leases[i] = ipcom_malloc(sizeof(Ipdhcps_lease)); if(p->leases[i] == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NO_MEMORY); ipcom_memset(p->leases[i], 0, sizeof(Ipdhcps_lease)); p->leases[i]->yiaddr = ip_htonl(p->yiaddr_min + i); p->leases[i]->parent = &s->node; } /* Attach pool first in pool list*/ ipdhcps_list_add_to(head, p); retval = IPCOM_SUCCESS; leave: if((retval != IPCOM_SUCCESS) && (p != IP_NULL)) { /* Release memory on failure*/ /* fix the coverity defect no use to if(p->leases != IP_NULL) */ { for(i = 0; i <= range; i++) { if(p->leases[i] != IP_NULL) ipcom_free(p->leases[i]); } } ipcom_free(p); } return retval; } /* *=========================================================================== * ipdhcps_class_do_delete *=========================================================================== * Description: Pool destructor/checkout callback. * Parameters: p - pointer to pool descriptor. * * Returns: IPCOM_SUCCESS - success * other - failure */ IP_STATIC Ip_err ipdhcps_class_do_delete(Ipdhcps_class *c, Ipcom_hash *head) { ipcom_hash_remove(head, c); ipcom_free(c->name); ipcom_free(c->data); ipcom_free(c->priv_data); if (c->node.option_head != IP_NULL) { ipcom_hash_for_each(c->node.option_head, (Ipcom_hash_foreach_cb_func)ipdhcps_option_set_do_delete, &c->node); ipcom_hash_delete(c->node.option_head); } ipcom_free(c); return IPCOM_SUCCESS; } /* *=========================================================================== * ipdhcps_class_new *=========================================================================== * Description: Class constructor routine. * * Parameters: s - pointer to subnet on which to attach pool * client_class - class information passed from user * * Returns: IPCOM_SUCCESS - success * other - failure */ IP_STATIC Ip_err ipdhcps_class_new(Ipdhcps_subnet *s, Ipdhcps_classinfo *client_class) { Ip_err retval; Ipdhcps_class *c = IP_NULL; char *classname = IP_NULL; Ip_bool is_string = IP_FALSE; Ip_u8 *data = IP_NULL; if (IP_NULL == client_class) IPDHCPS_LEAVE(IPCOM_ERR_INVALID_ARG); classname = client_class->name; is_string = client_class->data_is_string; data = client_class->data; if ((classname == IP_NULL) || (data == IP_NULL)) IPDHCPS_LEAVE(IPCOM_ERR_INVALID_ARG); c = ipcom_calloc(1, sizeof(Ipdhcps_class)); if(c == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NO_MEMORY); c->name = ipcom_strdup(classname); c->data = ipcom_malloc((Ip_size_t)IPDHCPS_OPT_TOTAL_LEN(data[IPDHCPS_OPT_LEN])); c->data_is_string = is_string; if (client_class->priv_data_len) { c->priv_data = ipcom_calloc(1, client_class->priv_data_len); if (IP_NULL == c->priv_data) IPDHCPS_LEAVE(IPCOM_ERR_NO_MEMORY); } if (c->data == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NO_MEMORY); ipcom_memcpy(c->data, data, (Ip_size_t)IPDHCPS_OPT_TOTAL_LEN(data[IPDHCPS_OPT_LEN])); if (s->class_head == IP_NULL) { s->class_head = ipcom_hash_new((Ipcom_hash_obj_func)ipdhcps_class_hash, (Ipcom_hash_key_func)ipdhcps_class_key_hash, (Ipcom_hash_cmp_func)ipdhcps_class_match); if (s->class_head == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NO_MEMORY); } else { if (ipdhcps_class_find_by_name(s, classname) != IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_ALREADY_INSTALLED); if (ipdhcps_class_find_by_id(s, c->data) != IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_ALREADY_INSTALLED); } ipcom_hash_add(s->class_head, c); return IPCOM_SUCCESS; leave: if (c != IP_NULL) { if (c->name != IP_NULL) ipcom_free(c->name); if (c->data) ipcom_free(c->data); ipcom_free(c); } return retval; } /* *=========================================================================== * ipdhcps_subnet_hash *=========================================================================== * Description: Hash calculation routine for hashing subnets. * Parameters: obj - pointer to subnet descriptor. * Returns: hash result * */ IP_STATIC unsigned ipdhcps_subnet_hash(Ipdhcps_subnet *s) { return (unsigned)s->addr; } /* *=========================================================================== * ipdhcps_subnet_key_hash *=========================================================================== * Description: Hash calculation routine for hashing subnet keys. * Parameters: obj - pointer to key descriptor. * Returns: hash result * */ IP_STATIC unsigned ipdhcps_subnet_key_hash(Ip_u32 *key) { return (unsigned) *key; } /* *=========================================================================== * ipdhcps_subnet_match *=========================================================================== * Description: Subnet to key match routine. Called when searching for a * subnet in subnet database. * If ip address in key masked with current subnet mask equals * current subnet address we have a match. * * Parameters: obj - pointer to subnet descriptor * key - pointer to key(ip address) * * Returns: IP_TRUE - match * IP_FALSE - no match */ IP_STATIC Ip_bool ipdhcps_subnet_match(Ipdhcps_subnet *s, Ip_u32 *addr) { if(s->addr == *addr) return IP_TRUE; else return IP_FALSE; } /* *=========================================================================== * ipdhcps_subnet_cmp *=========================================================================== * Description: Host address to subnet compar routine. Called when searching * for a matching subnet in subnet database. * * Parameters: s - pointer to subnet descriptor * info - pointer to search info * * Returns: */ IP_STATIC void ipdhcps_subnet_cmp(Ipdhcps_subnet *s, Ipdhcps_subnet_find_info *info) { Ip_u32 mask = 0; Ipdhcps_option *o; if (info->found == IP_TRUE) return; if (s->addr == IP_INADDR_ANY) return; o = ipdhcps_option_set_find(&s->node, IPDHCPS_OPTCODE_SUBNET_MASK); if(o != IP_NULL) ipcom_memcpy(&mask, &o->opt[IPDHCPS_OPT_DATA], o->opt[IPDHCPS_OPT_LEN]); if((s->addr == (info->addr & mask))) { info->found = IP_TRUE; info->subnet = s; } } /* *=========================================================================== * ipdhcps_host_lease_cmp *=========================================================================== * Description: Compare routine for checking if host address exists in lease * database. * * Parameters: l - pointer to lease descriptor * info - pointer to search info * * Returns: */ IP_STATIC void ipdhcps_host_lease_cmp(Ipdhcps_lease *l, Ipdhcps_host_lease_find_info *info) { if (info->found == IP_TRUE) return; if (l->yiaddr == info->addr) info->found = IP_TRUE; } /* *=========================================================================== * ipdhcps_option_hash *=========================================================================== * Description: Hash calculation routine for hashing subnets. * Parameters: obj - pointer to subnet descriptor. * Returns: hash result * */ IP_STATIC unsigned ipdhcps_option_hash(Ipdhcps_option *o) { return(unsigned)(o->code); } /* *=========================================================================== * ipdhcps_option_key_hash *=========================================================================== * Description: Hash calculation routine for hashing subnet keys. * Parameters: obj - pointer to key descriptor. * Returns: hash result * */ IP_STATIC unsigned ipdhcps_option_key_hash(Ip_u16 *key) { return (unsigned) *key; } /* *=========================================================================== * ipdhcps_option_match *=========================================================================== * Description: Option to key match. Called when searching for an * option in a option hash table. * * Parameters: obj - pointer to option descriptor * key - pointer to key (option code) * * Returns: IP_TRUE - match * IP_FALSE - no match */ IP_STATIC Ip_bool ipdhcps_option_match(Ipdhcps_option *o, Ip_u16 *key) { if(o->code == *key) return IP_TRUE; else return IP_FALSE; } /* *=========================================================================== * ipdhcps_option_do_delete *=========================================================================== * Description: A "for each" callback used for emptying an option hash table * * Parameters: obj - pointer to option descriptor * head - pointer to hash table * * Returns: - */ IP_STATIC void ipdhcps_option_set_do_delete(Ipdhcps_option *o, Ipdhcps_node *node) { if (o != IP_NULL && node != IP_NULL && node->option_head != IP_NULL) { ipcom_hash_remove(node->option_head, o); ipcom_free(o); } } /* *=========================================================================== * ipdhcps_option_add_check *=========================================================================== * Description: Check default-lease-time, max-lease-time and min-lease-time. * * Parameters: options - pointer to array of options in dhcp format * type - the type of the option * * Returns: IPCOM_SUCCESS - success * other - failure * */ IP_STATIC void ipdhcps_option_add_check(Ipdhcps_node *node, Ip_u8 *options, Ip_u16 type) { Ip_u32 deft = 0; Ip_u32 maxt = 0xFFFFFFFF; Ip_u32 mint = 0; Ip_u32 datatype = 0; Ip_u8 *data; Ip_u8 *dataconf; Ipdhcps_option *o = IP_NULL; datatype = IPDHCPS_OPT_CREATE(options[IPDHCPS_OPT_CODE],type); if (datatype == IPDHCPS_CONFCODE_LEASE_TIME_DFLT) { o = ipdhcps_option_set_find(node, IPDHCPS_CONFCODE_LEASE_TIME_MAX); if (o != IP_NULL) { data = &o->opt[IPDHCPS_OPT_DATA]; maxt = *(Ip_u32 *)data; } o = ipdhcps_option_set_find(node, IPDHCPS_CONFCODE_LEASE_TIME_MIN); if (o != IP_NULL) { data = &o->opt[IPDHCPS_OPT_DATA]; mint = *(Ip_u32 *)data; } dataconf = &options[IPDHCPS_OPT_DATA]; deft = *(Ip_u32 *)dataconf; if(deft > maxt) { *(Ip_u32 *)dataconf = maxt; IPCOM_LOG0(WARNING, "default_lease_time is changed " \ "to the value of max_lease_time"); return; } if(deft < mint) { *(Ip_u32 *)dataconf = mint; IPCOM_LOG0(WARNING, "default_lease_time is changed " \ "to the value of min_lease_time"); return; } } if (datatype == IPDHCPS_CONFCODE_LEASE_TIME_MAX) { dataconf = &options[IPDHCPS_OPT_DATA]; maxt = *(Ip_u32 *)dataconf; o = ipdhcps_option_set_find(node, IPDHCPS_CONFCODE_LEASE_TIME_DFLT); if (o != IP_NULL) { data = &o->opt[IPDHCPS_OPT_DATA]; deft = *(Ip_u32 *)data; } else { deft = maxt; } o = ipdhcps_option_set_find(node, IPDHCPS_CONFCODE_LEASE_TIME_MIN); if (o != IP_NULL) { data = &o->opt[IPDHCPS_OPT_DATA]; mint = *(Ip_u32 *)data; } if(maxt < deft) { *(Ip_u32 *)dataconf = deft; IPCOM_LOG0(WARNING, "max_lease_time is changed " \ "to the value of default_lease_time"); return; } if(maxt < mint) { *(Ip_u32 *)dataconf = mint; IPCOM_LOG0(WARNING, "max_lease_time is changed " \ "to the value of min_lease_time"); return; } } if (datatype == IPDHCPS_CONFCODE_LEASE_TIME_MIN) { dataconf = &options[IPDHCPS_OPT_DATA]; mint = *(Ip_u32 *)dataconf; o = ipdhcps_option_set_find(node, IPDHCPS_CONFCODE_LEASE_TIME_DFLT); if (o != IP_NULL) { data = &o->opt[IPDHCPS_OPT_DATA]; deft = *(Ip_u32 *)data; } else { deft = mint; } o = ipdhcps_option_set_find(node, IPDHCPS_CONFCODE_LEASE_TIME_MAX); if (o != IP_NULL) { data = &o->opt[IPDHCPS_OPT_DATA]; maxt = *(Ip_u32 *)data; } if(mint > maxt) { *(Ip_u32 *)dataconf = maxt; IPCOM_LOG0(WARNING, "min_lease_time is changed " \ "to the value of max_lease_time"); return; } if(mint > deft) { *(Ip_u32 *)dataconf = deft; IPCOM_LOG0(WARNING, "min_lease_time is changed " \ "to the value of default_lease_time"); return; } } return; } /* *=========================================================================== * ipdhcps_option_set_add *=========================================================================== * Description: Add a number of options to a option set list. * If option set list is empty we first create a list with * references to the default list. * * Parameters: head - pointer to option set list head * default - pointer top default option set list head * options - pointer to array of options in dhcp format * * Returns: IPCOM_SUCCESS - success * other - failure * */ IP_STATIC Ip_err ipdhcps_option_set_add(Ipdhcps_node *node, Ip_u8 *options, Ip_u16 type) { Ip_err retval; Ipdhcps_option *o = IP_NULL; if (node->option_head == IP_NULL) node->option_head = ipcom_hash_new((Ipcom_hash_obj_func)ipdhcps_option_hash, (Ipcom_hash_key_func)ipdhcps_option_key_hash, (Ipcom_hash_cmp_func)ipdhcps_option_match); if (node->option_head == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NO_MEMORY); /* * Check each option in the attached raw option list and * add each of them to the hash table */ while(options != IP_NULL) { if (ipdhcps_option_valid(options, type) == IP_FALSE) { /* Skip option if it is not valid */ options = ipdhcps_option_get_next(options); continue; } ipdhcps_option_add_check(node, options, type); o = ipdhcps_option_set_find(node, (Ip_u16)IPDHCPS_OPT_CREATE(options[IPDHCPS_OPT_CODE],type)); if (o != IP_NULL) ipdhcps_option_set_do_delete(o, node); o = ipcom_malloc((Ip_size_t)(sizeof(o) + IPDHCPS_OPT_TOTAL_LEN(options[IPDHCPS_OPT_LEN]))); if (o == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NO_MEMORY); o->code = (Ip_u16)IPDHCPS_OPT_CREATE(options[IPDHCPS_OPT_CODE],type); ipcom_memcpy(&o->opt, options, (Ip_size_t)IPDHCPS_OPT_TOTAL_LEN(options[IPDHCPS_OPT_LEN])); /* Add it to hash table */ retval = ipcom_hash_add(node->option_head, o); if (retval != IPCOM_SUCCESS) goto leave; /* Get next option */ options = ipdhcps_option_get_next(options); } return IPCOM_SUCCESS; leave: if (o != IP_NULL) ipcom_free(o); return retval; } /* *=========================================================================== * ipdhcps_interface_delete_cb *=========================================================================== * Description: Interface destructor callback. * Parameters: i - pointer to interface descriptor * * Returns: IPCOM_SUCCESS - success * */ IP_GLOBAL Ip_err ipdhcps_interface_do_delete(Ipdhcps_if *i) { ipdhcps_list_remove_from(i); ipcom_free(i); return IPCOM_SUCCESS; } /* *=========================================================================== * ipdhcps_interface_subnet_map *=========================================================================== * Description: Checks if a specified subnet is local to any of the * interfaces used by IPDHCP. If so the "local subnet" shortcut * the interface is initiated with a pointer to the subnet. * * Parameters: subnet - pointer to subnet descriptor * * Returns: - * */ IP_STATIC void ipdhcps_interface_subnet_map(Ipdhcps_subnet *subnet) { Ipdhcps_if *i; Ip_u32 mask = 0; Ipdhcps_option *o; o = ipdhcps_option_set_find(&subnet->node,IPDHCPS_OPTCODE_SUBNET_MASK); if(o != IP_NULL) ipcom_memcpy(&mask, &o->opt[IPDHCPS_OPT_DATA], o->opt[IPDHCPS_OPT_LEN]); IPDHCPS_LIST_FOREACH(i, (&ipdhcpsc.if_head), Ipdhcps_if*) if(subnet->addr == (i->siaddr & mask)) i->local_subnet = subnet; } /* *=========================================================================== * ipdhcps_default_net_create *=========================================================================== * Description: Creates the default net and inititates it with the default * configurations. * Parameters: - * Returns: IPCOM_SUCCESS - success * other - failure * */ IP_STATIC Ip_err ipdhcps_default_net_create(void) { Ip_err retval; Ip_u8 buf[100]; Ip_size_t i; /* Create a subnet descriptor for global/default values */ ipdhcpsc.default_net = ipdhcps_subnet_new(0); if(ipdhcpsc.default_net == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NO_MEMORY); /* Add it to subnet hash table */ (void) ipcom_hash_add(ipdhcpsc.subnet_head, ipdhcpsc.default_net); /* * Set up default config values */ i=0; /* Default lease time */ buf[i++] = IPDHCPS_OPT_CODE_CREATE(IPDHCPS_CONFCODE_LEASE_TIME_DFLT); buf[i++] = sizeof(Ip_size_t); ipcom_memcpy(&buf[i], &ipdhcpsc.config.default_lease_time, sizeof(Ip_size_t)); i += sizeof(Ip_size_t); /* Max lease time */ buf[i++] = IPDHCPS_OPT_CODE_CREATE(IPDHCPS_CONFCODE_LEASE_TIME_MAX); buf[i++] = sizeof(Ip_size_t); ipcom_memcpy(&buf[i], &ipdhcpsc.config.max_lease_time, sizeof(Ip_size_t)); i += sizeof(Ip_size_t); /* Min lease time */ buf[i++] = IPDHCPS_OPT_CODE_CREATE(IPDHCPS_CONFCODE_LEASE_TIME_MIN); buf[i++] = sizeof(Ip_size_t); ipcom_memcpy(&buf[i], &ipdhcpsc.config.min_lease_time, sizeof(Ip_size_t)); i += sizeof(Ip_size_t); /* Renewal time */ buf[i++] = IPDHCPS_OPT_CODE_CREATE(IPDHCPS_CONFCODE_RENEWAL_TIME); buf[i++] = sizeof(Ip_size_t); ipcom_memcpy(&buf[i], &ipdhcpsc.config.renewal_time, sizeof(Ip_size_t)); i += sizeof(Ip_size_t); /* Renewal time */ buf[i++] = IPDHCPS_OPT_CODE_CREATE(IPDHCPS_CONFCODE_REBINDING_TIME); buf[i++] = sizeof(Ip_size_t); ipcom_memcpy(&buf[i], &ipdhcpsc.config.rebinding_time, sizeof(Ip_size_t)); i += sizeof(Ip_size_t); /* Accept bootp clients by default */ buf[i++] = IPDHCPS_OPT_CODE_CREATE(IPDHCPS_CONFCODE_ALLOW_BOOTP); buf[i++] = 1; buf[i++] =(Ip_u8)ipdhcpsc.config.allow_bootp; /*No dynamic addresses for bootp clients by default */ buf[i++] = IPDHCPS_OPT_CODE_CREATE(IPDHCPS_CONFCODE_ALLOW_DYNAMIC_BOOTP); buf[i++] = 1; buf[i++] =(Ip_u8)ipdhcpsc.config.allow_dynamic_bootp; /* Default tftp server = NULL */ buf[i++] = IPDHCPS_OPT_CODE_CREATE(IPDHCPS_CONFCODE_NEXT_SERVER_NAME); buf[i++] = 1; buf[i++] = IP_NULL; /* Default tftp server = NULL */ buf[i++] = IPDHCPS_OPT_CODE_CREATE(IPDHCPS_CONFCODE_NEXT_SERVER_IP); buf[i++] = 4; buf[i++] = 0; buf[i++] = 0; buf[i++] = 0; buf[i++] = 0; /* Default boot file */ buf[i++] = IPDHCPS_OPT_CODE_CREATE(IPDHCPS_CONFCODE_BOOT_FILE); buf[i++] = 1; buf[i++] = IP_NULL; /* Default config for ICMP address probing */ buf[i++] = IPDHCPS_OPT_CODE_CREATE(IPDHCPS_CONFCODE_DO_ICMP_ADDRESS_CHECK); buf[i++] = 1; buf[i++] = (Ip_u8)ipdhcpsc.config.do_icmp_address_check; buf[i] = IPDHCPS_OPT_CODE_CREATE(IPDHCPS_CONFCODE_END); /* Setup default config option set */ if(ipdhcps_option_set_add(&ipdhcpsc.defaults, buf, IPDHCPS_OPT_CONF) != IPCOM_SUCCESS) IPDHCPS_LEAVE(IPCOM_ERR_FAILED); /* Setup default config options on default net */ if(ipdhcps_option_set_add(&ipdhcpsc.default_net->node, buf, IPDHCPS_OPT_CONF) != IPCOM_SUCCESS) IPDHCPS_LEAVE(IPCOM_ERR_FAILED); i = 0; /* Default config for authorized relay agents. Check if entry fits first */ if ((Ip_u32)sizeof(buf) >= (Ip_u32)IPDHCPS_OPT_TOTAL_LEN(ipdhcpsc.config.no_of_authorized_agents * sizeof(Ip_u32))) { buf[i++] = IPDHCPS_OPT_CODE_CREATE(IPDHCPS_CONFCODE_AUTHORIZED_AGENTS); buf[i++] = (Ip_u8)(ipdhcpsc.config.no_of_authorized_agents * sizeof(Ip_u32)); ipcom_memcpy(&buf[i], ipdhcpsc.config.authorized_agents, (Ip_size_t)(ipdhcpsc.config.no_of_authorized_agents * sizeof(Ip_u32))); buf[IPDHCPS_OPT_TOTAL_LEN(ipdhcpsc.config.no_of_authorized_agents * sizeof(Ip_u32))] = IPDHCPS_OPT_CODE_CREATE(IPDHCPS_CONFCODE_END); /* Setup default config option set */ if(ipdhcps_option_set_add(&ipdhcpsc.defaults, buf, IPDHCPS_OPT_CONF) != IPCOM_SUCCESS) IPDHCPS_LEAVE(IPCOM_ERR_FAILED); /* Setup default config options on default net */ if(ipdhcps_option_set_add(&ipdhcpsc.default_net->node, buf, IPDHCPS_OPT_CONF) != IPCOM_SUCCESS) IPDHCPS_LEAVE(IPCOM_ERR_FAILED); } i = 0; /* Default authentication */ buf[i++] = IPDHCPS_OPT_CODE_CREATE(IPDHCPS_CONFCODE_RA_AUTHENTICATION); buf[i++] = 1; buf[i++] = IP_NULL; /* Setup default config option set */ if (ipdhcps_option_set_add(&ipdhcpsc.defaults, buf, IPDHCPS_OPT_CONF) != IPCOM_SUCCESS) IPDHCPS_LEAVE(IPCOM_ERR_FAILED); i = 0; /* Default drop policy */ buf[i++] = IPDHCPS_OPT_CODE_CREATE(IPDHCPS_CONFCODE_RA_DROP); buf[i++] = 1; buf[i++] = (Ip_u8)IP_FALSE; /* Setup default config option set */ if (ipdhcps_option_set_add(&ipdhcpsc.defaults, buf, IPDHCPS_OPT_CONF) != IPCOM_SUCCESS) IPDHCPS_LEAVE(IPCOM_ERR_FAILED); retval = IPCOM_SUCCESS; leave: return retval; } /* **************************************************************************** * 10 GLOBAL FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipdhcps_sweep_db_cb *=========================================================================== * Description: Timeout routine periodically called to sweep through * the lease data base and relese entries of certain status. * * Parameters: tmo - pointer to timeout descriptor * cookie - pointer to cookie(not used) * * Returns: IP_TRUE (ordering timer reset) * */ IP_GLOBAL Ip_bool ipdhcps_sweep_db_cb(void *cookie) { Ipdhcps_hash_cb_info info; (void)cookie; info.order = IPDHCPS_HASH_CB_ORDER_CHECK_EXPIRY; info.in_data = ipdhcpsc.lease_htable; IPDHCPS_LOCK(); ipcom_hash_for_each(ipdhcpsc.lease_htable, (Ipcom_hash_foreach_cb_func)ipdhcps_lease_table_cb, &info); IPDHCPS_UNLOCK(); return IP_TRUE; } /* *=========================================================================== * ipdhcps_db_init *=========================================================================== * Description: Allocates resources for and intiates the IPDHCPS database * Parameters: - * Returns: IPCOM_SUCCESS - success * other - failure */ IP_GLOBAL Ip_err ipdhcps_db_init(void) { Ip_err retval; /* Clear logbuf */ ipcom_memset(logbuf, 0, sizeof(logbuf)); /* Create subnet hash table */ ipdhcpsc.subnet_head = ipcom_hash_new((Ipcom_hash_obj_func)ipdhcps_subnet_hash, (Ipcom_hash_key_func)ipdhcps_subnet_key_hash, (Ipcom_hash_cmp_func)ipdhcps_subnet_match); if(ipdhcpsc.subnet_head == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NO_MEMORY); /* Create lease db hash table */ ipdhcpsc.lease_htable = ipcom_hash_new((Ipcom_hash_obj_func)ipdhcps_lease_hash, (Ipcom_hash_key_func)ipdhcps_host_lease_key_hash, (Ipcom_hash_cmp_func)ipdhcps_host_lease_match); if(ipdhcpsc.lease_htable == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NO_MEMORY); ipdhcps_list_init(&ipdhcpsc.if_head); /* Setup default net */ if(ipdhcps_default_net_create() != IPCOM_SUCCESS) IPDHCPS_LEAVE(IPCOM_ERR_NO_MEMORY); retval = IPCOM_SUCCESS; leave: return retval; } /* *=========================================================================== * ipdhcps_interface_find *=========================================================================== * Description: Releases all resources allocated for the IPDHCPS database * Parameters: - * Returns: IPCOM_SUCCESS * */ IP_GLOBAL Ip_err ipdhcps_db_destroy(void) { Ipdhcps_if *i; /* Cancel db sweep routine */ (void)ipcom_tmo2_cancel(&ipdhcpsc.sweep_db_tmo); for(i = (Ipdhcps_if*)ipdhcpsc.if_head.next; i != IP_NULL;) { Ipdhcps_if *ni = ((Ipdhcps_if*)(i->node.next)); ipdhcps_interface_do_delete(i); i = ni; } if (ipdhcpsc.host_head != IP_NULL) { ipcom_hash_for_each(ipdhcpsc.host_head, (Ipcom_hash_foreach_cb_func)ipdhcps_host_delete_cb, ipdhcpsc.host_head); ipcom_hash_delete(ipdhcpsc.host_head); } if (ipdhcpsc.defaults.option_head != IP_NULL) { ipcom_hash_for_each(ipdhcpsc.defaults.option_head, (Ipcom_hash_foreach_cb_func)ipdhcps_option_set_do_delete, &ipdhcpsc.defaults); ipcom_hash_delete(ipdhcpsc.defaults.option_head); } if (ipdhcpsc.subnet_head != IP_NULL) { ipcom_hash_for_each(ipdhcpsc.subnet_head, (Ipcom_hash_foreach_cb_func)ipdhcps_subnet_delete_cb, ipdhcpsc.subnet_head); ipcom_hash_delete(ipdhcpsc.subnet_head); } /* Free allocated resources */ ipdhcps_lease_table_free(ipdhcpsc.lease_htable); return IPCOM_SUCCESS; } /* *=========================================================================== * ipdhcps_interface_find *=========================================================================== * Description: Search routine for interfaces used by IPDHCPS. * Parameters: ifindex - interface index * * Returns: pointer to interface dexcriptor - found * IP_NULL - not found * */ IP_GLOBAL Ipdhcps_if * ipdhcps_interface_find(int ifindex) { Ipdhcps_if *ift; IPDHCPS_LIST_FOREACH(ift, (&ipdhcpsc.if_head), Ipdhcps_if*) if(ift->ifindex == ifindex) break; return ift; } /* *=========================================================================== * ipdhcps_interface_new *=========================================================================== * Description: Interface constructor routine. * * Parameters: ifindex - inteface index * ifname - interface name * addr - interface main IPv4 address * * Returns: IPCOM_SUCCESS - success * other - failure */ IP_GLOBAL Ip_err ipdhcps_interface_new(int ifindex, const char *ifname, Ip_u32 addr) { Ip_err retval; Ipdhcps_if *ift; if (ipcom_strlen(ifname) >= IP_IFNAMSIZ) IPDHCPS_LEAVE(IPCOM_ERR_FAILED); /* Check if interface is already added */ IPDHCPS_LIST_FOREACH(ift, (&ipdhcpsc.if_head), Ipdhcps_if*) if(ift->ifindex == ifindex) IPDHCPS_LEAVE(IPCOM_ERR_ALREADY_INSTALLED); /* Allocate a new interface descriptor */ if((ift = ipcom_malloc(sizeof(Ipdhcps_if))) == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NO_MEMORY); /* Init interface descriptor. */ ipcom_memset(ift, 0, sizeof(Ipdhcps_if)); ipcom_strcpy(ift->ifname, ifname); ift->ifindex = ifindex; ift->siaddr = addr; ift->local_subnet = ipdhcpsc.default_net; if (ipdhcpsc.config.enable_new_interface_when_found == IP_TRUE) ift->enabled = IP_TRUE; else ift->enabled = IP_FALSE; IPCOM_LOG2(INFO, "ipdhcps_daemon :: adding interface %s[%d]", ift->ifname, ift->ifindex); /* Do not use interface with no main address set */ ift->functional = addr != IP_INADDR_ANY ? IP_TRUE : IP_FALSE; /* Insert first into interface list. */ ipdhcps_list_add_to(&ipdhcpsc.if_head, ift); retval = IPCOM_SUCCESS; leave: return retval; } /* *=========================================================================== * ipdhcps_host_find *=========================================================================== * Description: Host search routine. * Parameters: addr - hosts IPv4 address. * * Returns: pointer to host descriptor - success * IP_NULL - failure */ IP_GLOBAL Ipdhcps_host * ipdhcps_host_find(Ip_u32 addr) { if (ipdhcpsc.host_head != IP_NULL) return (Ipdhcps_host*)ipcom_hash_get(ipdhcpsc.host_head, &addr); else return IP_NULL; } /* *=========================================================================== * ipdhcps_subnet_find *=========================================================================== * Description: Returns pointer to subnet descriptor that is identified by * specified address. * Parameters: addr - subnet IP address * * Returns: Pointer to subnet descriptor - success * IP_NULL - not found. * */ IP_GLOBAL Ipdhcps_subnet * ipdhcps_subnet_find(Ip_u32 addr) { if (ipdhcpsc.subnet_head != IP_NULL) return (Ipdhcps_subnet*)ipcom_hash_get(ipdhcpsc.subnet_head, &addr); else return IP_NULL; } /* *=========================================================================== * ipdhcps_subnet_find_matching *=========================================================================== * Description: Returns pointer to subnet descriptor that matches * specified address. * Parameters: addr - subnet IP address * * Returns: Pointer to subnet descriptor - success * IP_NULL - not found. * */ IP_GLOBAL Ipdhcps_subnet * ipdhcps_subnet_find_matching(Ip_u32 addr) { Ipdhcps_subnet_find_info info; if (ipdhcpsc.subnet_head != IP_NULL) { info.addr = addr; info.found = IP_FALSE; info.subnet = IP_NULL; ipcom_hash_for_each(ipdhcpsc.subnet_head, (Ipcom_hash_foreach_cb_func)ipdhcps_subnet_cmp, &info); if (info.found == IP_TRUE) return info.subnet; } return IP_NULL; } /* *=========================================================================== * ipdhcps_option_set_find *=========================================================================== * Description: Returns pointer to an option descriptor as * specified by the option code. * * Parameters: head - option set list to search in. * code - option code to search for. * * Returns: pointer to an option set descriptor - found * IP_NULL - not found * */ IP_GLOBAL Ipdhcps_option* ipdhcps_option_set_find(Ipdhcps_node *node, Ip_u16 code) { Ipdhcps_option *o = IP_NULL; if (node->option_head != IP_NULL) o = ipcom_hash_get(node->option_head, &code); return o; } /* *=========================================================================== * ipdhcps_option_set_find_most_valid *=========================================================================== * Description: Returns pointer to an option descriptor as * specified by the option code. First checks * the primary source, then the secondary and * last the default source. * * Parameters: primary - primary list to search in. * secondary - * code - option code to search for. * * Returns: pointer to an option set descriptor - found * IP_NULL - not found * */ IP_GLOBAL Ip_u8 * ipdhcps_option_set_find_most_valid(Ipdhcps_handle *handle, int code) { Ipdhcps_option *o = IP_NULL; /* Search by specific class */ if (handle->client_class != IP_NULL) o = ipdhcps_option_set_find(&handle->client_class->node, (Ip_u16)code); /* Search by specific host */ if (o == IP_NULL && handle->lease != IP_NULL && handle->lease->parent != IP_NULL) o = ipdhcps_option_set_find(handle->lease->parent, (Ip_u16)code); /* Search by specific subnet */ if (o == IP_NULL && handle->subnet != IP_NULL) o = ipdhcps_option_set_find(&handle->subnet->node, (Ip_u16)code); /* Search default net */ if (o == IP_NULL) o = ipdhcps_option_set_find(&ipdhcpsc.default_net->node, (Ip_u16)code); /* Search system defaults */ if (o == IP_NULL) o = ipdhcps_option_set_find(&ipdhcpsc.defaults, (Ip_u16)code); if (o != IP_NULL) return o->opt; else return IP_NULL; } /* *=========================================================================== * ipdhcps_host_lease_find *=========================================================================== * Description: Returns pointer to lease descriptor as specifed by * subnet, link-layer address and client identifier. * * Parameters: subnet - pointer to subnet descriptor * hwa_len - link-layer address length * hwaddr - link-layer address * cid_len - length of client identifier * cid - pointer to client identifer * * Returns: Pointer to lease descriptor - found * IP_NULL - not found * */ IP_GLOBAL Ipdhcps_lease* ipdhcps_host_lease_find(Ipdhcps_subnet* subnet, Ip_size_t hwa_len, Ip_u8 *hwaddr, Ip_u8 cid_len, Ip_u8 *cid) { Ipdhcps_host_key key; /* Build a hash search key */ key.subnet = subnet; key.hlen = hwa_len; key.haddr = hwaddr; key.cid_len = cid_len; key.cid = cid; return(ipcom_hash_get(ipdhcpsc.lease_htable, &key)); } /* *=========================================================================== * ipdhcps_host_lease_checkout *=========================================================================== * Description: Checks out a new lease on a specifed subnet and with a * possibly with a specified IP address. * * Parameters: subnet - Pointer to subnet descriptor * req_addr - Requested address * * Returns: Pointer to lease descriptor - success * IP_NULL - failure * */ IP_GLOBAL Ipdhcps_lease* ipdhcps_host_lease_checkout(Ipdhcps_subnet* subnet, Ip_u32 req_addr, Ip_u8 *client_class) { Ipdhcps_pool *p; Ipdhcps_lease *requested_lease = IP_NULL; Ipdhcps_lease *first_free_lease = IP_NULL; Ipdhcps_lease *first_avail_lease = IP_NULL; Ipdhcps_lease *returned_lease; Ip_u32 i; req_addr = ip_ntohl(req_addr); /* Check if requested address is valid and not occupied */ IPDHCPS_LIST_FOREACH(p, (&subnet->pools), Ipdhcps_pool*) { /* Do some class verifications */ if (client_class == IP_NULL && p->class_id != IP_NULL) continue; if (ipdhcpsc.config.strict_vender_class_id_match == IP_TRUE && client_class != IP_NULL && p->class_id == IP_NULL) continue; if (client_class != IP_NULL && p->class_id != IP_NULL) { if ((client_class[IPDHCPS_OPT_CODE] != p->class_id[IPDHCPS_OPT_CODE]) || (client_class[IPDHCPS_OPT_LEN] != p->class_id[IPDHCPS_OPT_LEN]) || (ipcom_memcmp(&client_class[IPDHCPS_OPT_DATA], &p->class_id[IPDHCPS_OPT_DATA], (Ip_size_t)p->class_id[IPDHCPS_OPT_LEN]) != 0)) continue; } if((req_addr <= p->yiaddr_max) && (req_addr >= p->yiaddr_min)) { /* Found requested lease */ if(p->leases[req_addr - p->yiaddr_min]->binding_state == IPDHCPS_BINDING_STATE_FREE) { /* Lease can be used by client */ requested_lease = p->leases[req_addr - p->yiaddr_min]; first_free_lease = requested_lease; break; } else if(first_free_lease != IP_NULL) { /* Requested lease can not be used, but we have found a free one */ break; } } if(first_free_lease == IP_NULL) { for(i = 0; i <= (p->yiaddr_max - p->yiaddr_min); i++) { if(p->leases[i]->binding_state == IPDHCPS_BINDING_STATE_FREE) { first_free_lease = p->leases[i]; break; } else if((first_avail_lease == IP_NULL) && ((p->leases[i]->binding_state == IPDHCPS_BINDING_STATE_EXPIRED) || (p->leases[i]->binding_state == IPDHCPS_BINDING_STATE_RELEASED))) { first_avail_lease = p->leases[i]; } } } } returned_lease = requested_lease != IP_NULL ? requested_lease : first_free_lease; returned_lease = returned_lease != IP_NULL ? returned_lease : first_avail_lease; if(returned_lease == IP_NULL) { /* We have no leases to offer */ IPCOM_LOG0(DEBUG,"ipdhcps_daemon :: no leases to offer client"); } else if(returned_lease == first_avail_lease) { /* This lease was not free. Print a warning */ IPCOM_LOG2(WARNING, "ipdhcps_daemon :: reusing %s lease with address %s for new client", ipdhcps_binding_state_str[returned_lease->binding_state], ipcom_inet_ntop(IP_AF_INET, &returned_lease->yiaddr, logbuf, sizeof(logbuf))); /* Delete it from lease database */ (void)ipdhcps_lease_delete(returned_lease); } return(returned_lease); } /* *=========================================================================== * ipdhcps_lease_delete *=========================================================================== * Description: Removes lease from lease database. * * Parameters: obj - pointer to lease descriptor. * * Returns: IPCOM_SUCCESS - success * other - failure * */ IP_GLOBAL Ip_err ipdhcps_lease_delete(Ipdhcps_lease *lease) { Ip_err retval = IPCOM_ERR_INVALID_ARG; if (lease != IP_NULL) { retval = ipcom_hash_remove(ipdhcpsc.lease_htable, lease); if(retval == IPCOM_SUCCESS) if(lease->binding_state != IPDHCPS_BINDING_STATE_PERMANENT) { /* Clear dynamic lease descriptor */ if(lease->cid != IP_NULL) ipcom_free(lease->cid); lease->cid_len = 0; lease->cid = 0; lease->binding_state = IPDHCPS_BINDING_STATE_FREE; lease->lease_time = 0; lease->expires = 0; } } return retval; } /* *=========================================================================== * ipdhcps_lease_add *=========================================================================== * Description: Lease checkin callback. Adds lease to lease database. * * Parameters: obj - pointer to lease object. * * Returns: IPCOM_SUCCESS - success * other - failure * */ IP_GLOBAL Ip_err ipdhcps_lease_add(Ipdhcps_lease *l) { return(ipcom_hash_add(ipdhcpsc.lease_htable, l)); } /* *=========================================================================== * ipdhcps_interface_for_each *=========================================================================== * Description: Apply supplied callback for each interface descriptor * found in database. * * Parameters: cb - Pointer to callback function. * data - user supplied data. * * Returns: IPCOM_SUCCESS - success * other - failure * */ IP_GLOBAL Ip_err ipdhcps_interface_for_each(void (*cb)(void *obj, void *data), void* data) { Ipdhcps_if *i; if(ipdhcpsc.opened != IP_TRUE) return(IPCOM_ERR_NOT_OPENED); IPDHCPS_LOCK(); IPDHCPS_LIST_FOREACH(i, (&ipdhcpsc.if_head), Ipdhcps_if*) (*cb)(i,data); IPDHCPS_UNLOCK(); return IPCOM_SUCCESS; } /* *=========================================================================== * ipdhcps_host_for_each *=========================================================================== * Description: Apply supplied callback for each host descriptor * found in database. * * Parameters: cb - Pointer to callback function. * data - user supplied data. * * Returns: IPCOM_SUCCESS - success * other - failure * */ IP_GLOBAL Ip_err ipdhcps_host_for_each(Ipdhcps_foreach_cb cb, void* data) { if(ipdhcpsc.opened != IP_TRUE) return(IPCOM_ERR_NOT_OPENED); IPDHCPS_LOCK(); if (ipdhcpsc.host_head != IP_NULL) ipcom_hash_for_each(ipdhcpsc.host_head, cb, data); IPDHCPS_UNLOCK(); return IPCOM_SUCCESS; } /* *=========================================================================== * ipdhcps_subnet_for_each *=========================================================================== * Description: Apply supplied callback for each subnet descriptor * found in database. * * Parameters: cb - Pointer to callback function. * data - user supplied data. * * Returns: IPCOM_SUCCESS - success * other - failure * */ IP_GLOBAL Ip_err ipdhcps_subnet_for_each(void (*cb)(void *obj, void *data), void* data) { if(ipdhcpsc.opened != IP_TRUE) return(IPCOM_ERR_NOT_OPENED); IPDHCPS_LOCK(); ipcom_hash_for_each(ipdhcpsc.subnet_head, cb, data); IPDHCPS_UNLOCK(); return IPCOM_SUCCESS; } /* *=========================================================================== * ipdhcps_lease_for_each *=========================================================================== * Description: Apply supplied callback for each lease descriptor * found in database. * * Parameters: cb - Pointer to callback function. * data - user supplied data. * * Returns: IPCOM_SUCCESS - success * other - failure * */ IP_GLOBAL Ip_err ipdhcps_lease_for_each(void (*cb)(void *obj, void *data), void* data) { if(ipdhcpsc.opened != IP_TRUE) return(IPCOM_ERR_NOT_OPENED); IPDHCPS_LOCK(); ipcom_hash_for_each(ipdhcpsc.lease_htable, cb, data); IPDHCPS_UNLOCK(); return IPCOM_SUCCESS; } /* *=========================================================================== * ipdhcps_pool_for_each *=========================================================================== * Description: Apply supplied callback for each pool descriptor * matching supplied address to subnet. * * Parameters: addr - Subnet address. * cb - Pointer to callback function. * data - user supplied data. * * Returns: IPCOM_SUCCESS - success * other - failure * * */ IP_GLOBAL Ip_err ipdhcps_pool_for_each(Ip_u32 inaddr, void (*cb)(void *obj, void *data), void* data) { Ip_err retval; Ipdhcps_subnet *s; Ipdhcps_pool *p; if(ipdhcpsc.opened != IP_TRUE) return(IPCOM_ERR_NOT_OPENED); IPDHCPS_LOCK(); if((s = ipdhcps_subnet_find(inaddr)) == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NOT_FOUND); IPDHCPS_LIST_FOREACH(p,(&s->pools), Ipdhcps_pool*) (*cb)(p, data); retval = IPCOM_SUCCESS; leave: IPDHCPS_UNLOCK(); return retval; } /* *=========================================================================== * ipdhcps_option_for_each *=========================================================================== * Description: Apply supplied callback for each pool descriptor * matching supplied address to subnet/host. * * Parameters: addr - Subnet/host address. * cb - Pointer to callback function. * data - user supplied data. * * Returns: IPCOM_SUCCESS - success * other - failure * */ IP_GLOBAL Ip_err ipdhcps_option_for_each(Ip_u32 inaddr, char *client_class, void (*cb)(void *obj, void *data), void* data) { Ip_err retval; Ipdhcps_host *h; Ipdhcps_class *c; Ipdhcps_subnet *s; Ipcom_hash *head; if(ipdhcpsc.opened != IP_TRUE) return(IPCOM_ERR_NOT_OPENED); IPDHCPS_LOCK(); if((s = ipdhcps_subnet_find(inaddr)) != IP_NULL) { if (client_class != IP_NULL) { c = ipdhcps_class_find_by_name(s, client_class); if (c == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_INVALID_CONFIG); head = c->node.option_head; } else head = s->node.option_head; } else if((h = ipdhcps_host_find(inaddr)) != IP_NULL) head = h->node.option_head; else IPDHCPS_LEAVE(IPCOM_ERR_INVALID_CONFIG); if(head) ipcom_hash_for_each(head, (Ipcom_hash_foreach_cb_func)cb, data); retval = IPCOM_SUCCESS; leave: IPDHCPS_UNLOCK(); return retval; } /* *=========================================================================== * ipdhcps_class_for_each *=========================================================================== * Description: Apply supplied callback for each class descriptor * found in database. * * Parameters: cb - Pointer to callback function. * data - user supplied data. * * Returns: IPCOM_SUCCESS - success * other - failure * */ IP_GLOBAL Ip_err ipdhcps_class_for_each(Ip_u32 subnet, void (*cb)(void *obj, void *data), void* data) { Ipdhcps_subnet *s; if(ipdhcpsc.opened != IP_TRUE) return(IPCOM_ERR_NOT_OPENED); IPDHCPS_LOCK(); s = ipdhcps_subnet_find(subnet); if (s != IP_NULL && s->class_head != IP_NULL) ipcom_hash_for_each(s->class_head, cb, data); IPDHCPS_UNLOCK(); return IPCOM_SUCCESS; } /* *=========================================================================== * ipdhcps_class_find_by_name *=========================================================================== * Description: Search routine to find a certain class on a * certain subnet. * * Parameters: s - subnet * classname - class name * * Returns: pointer to pool descriptor - found * IP_NULL - not found * */ IP_GLOBAL Ipdhcps_class* ipdhcps_class_find_by_name(Ipdhcps_subnet *s, char *name) { Ipdhcps_class_find_id id; id.name = name; id.client_class = IP_NULL; if (s != IP_NULL && s->class_head != IP_NULL) ipcom_hash_for_each(s->class_head, (Ipcom_hash_foreach_cb_func)ipdhcps_class_search_name, &id); return id.client_class; } /* *=========================================================================== * ipdhcps_class_find_by_id *=========================================================================== * Description: Search routine to find a certain class on a * certain subnet. * * Parameters: s - subnet * classname - class name * * Returns: pointer to pool descriptor - found * IP_NULL - not found * */ IP_GLOBAL Ipdhcps_class* ipdhcps_class_find_by_id(Ipdhcps_subnet *s, Ip_u8 *id) { if (s != IP_NULL && s->class_head != IP_NULL) return (Ipdhcps_class*)ipcom_hash_get(s->class_head, id); else return IP_NULL; } /* **************************************************************************** * 11 PUBLIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipdhcps_interface_status_set *=========================================================================== * Description: Set status for an interface handled by IPDHCPS. * Parameters: ifname - name of interface. * status - IP_TRUE=enable, IP_FALSE=disable * * Returns: IPCOM_SUCCESS - success * other - failure */ IP_PUBLIC Ip_err ipdhcps_interface_status_set(char* ifname, Ip_bool status) { Ip_err retval; Ipdhcps_if *i; if(ipdhcpsc.opened != IP_TRUE) return (IPCOM_ERR_NOT_OPENED); IPDHCPS_LOCK(); IPDHCPS_LIST_FOREACH(i, (&ipdhcpsc.if_head), Ipdhcps_if*) if(ipcom_strcmp(ifname, i->ifname) == 0) { i->enabled = status; IPDHCPS_LEAVE(IPCOM_SUCCESS); } retval = IPCOM_ERR_NOT_FOUND; leave: IPDHCPS_UNLOCK(); return retval; } /* *=========================================================================== * Ipdhcps_subnet_add *=========================================================================== * Description: Add a new subnet to the DHCP server configuration * Parameters: subnet - Net address. Must be in network byte order. * * netmask - Subnet mask. Must be in network byte order. * pools - Array of address pools to be mapped to the * subnet * n_pools - Size of pool array. * dhcp_options - Byte array of dhcp options specific to this subnet. * Standard DHCP format (code,len,data,code,len,data..) * conf_options - Byte array of configuration options specific to * this subnet. * Standard DHCP format (code,len,data,code,len,data..) * * NOTE 1: DHCP options are always stored in network byte * order. * NOTE 2: Configuration options are always stored in host byte * order. * Returns: * IPCOM_SUCCESS - OK. * IPCOM_ERR_NOT_OPENED - DHCP server not opened yet. * IPCOM_ERR_ALREADY_INSTALLED - Subnet already exists. * IPCOM_ERR_NO_MEMORY - Out of memory. * * */ IP_PUBLIC Ip_err ipdhcps_subnet_add(Ip_u32 subnet, Ipdhcps_poolinfo pools[], Ip_u32 n_pools, Ip_u8 *dhcp_options, Ip_u8 *config_options) { Ip_err retval; Ip_u32 i; Ipdhcps_subnet *s = IP_NULL; if(ipdhcpsc.opened != IP_TRUE) return(IPCOM_ERR_NOT_OPENED); IPDHCPS_LOCK(); if (subnet == IP_INADDR_ANY) IPDHCPS_LEAVE(IPCOM_ERR_INVALID_ARG); if (ipdhcps_subnet_find(subnet) != IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_ALREADY_INSTALLED); s = ipdhcps_subnet_new(subnet); if (s == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NO_MEMORY); for (i = 0; i < n_pools; i++) { if (ipdhcps_pool_config_sane(&s->pools, ip_ntohl(pools[i].first_addr), ip_ntohl(pools[i].last_addr)) != IP_TRUE) { IPDHCPS_LEAVE(IPCOM_ERR_INVALID_CONFIG); } if (ipdhcps_pool_new(s, &s->pools, ip_ntohl(pools[i].first_addr), ip_ntohl(pools[i].last_addr), pools[i].classname) != IPCOM_SUCCESS) { IPDHCPS_LEAVE(IPCOM_ERR_NO_MEMORY); } } /* Add DHCP options */ if (ipdhcps_option_set_add(&s->node, dhcp_options, IPDHCPS_OPT_DHCP) != IPCOM_SUCCESS) IPDHCPS_LEAVE(IPCOM_ERR_INVALID_CONFIG); /* Add configuration options */ if (ipdhcps_option_set_add(&s->node, config_options, IPDHCPS_OPT_CONF) != IPCOM_SUCCESS) IPDHCPS_LEAVE(IPCOM_ERR_INVALID_CONFIG); /* * If the subnet is directly attached to any * of our interfaces we create a shortcut to it. */ ipdhcps_interface_subnet_map(s); /* Add to hash table */ retval = ipcom_hash_add(ipdhcpsc.subnet_head, s); leave: if ((retval != IPCOM_SUCCESS) &&(s != IP_NULL)) { /* Release buffers upon failure */ ipdhcps_subnet_do_delete(s); } IPDHCPS_UNLOCK(); return retval; } /* *=========================================================================== * Ipdhcps_subnet_delete *=========================================================================== * Description: Delete a subnet from the IPDHCPS configuration. * Parameters: subnet - Net address of the subnet. Must be in network byte * order. * * NOTE! The subnet address INADDR_ANY(0) is regarded * illegal here. * * Returns: IPCOM_SUCCESS - OK. * IPCOM_ERR_NOT_OPENED - DHCP server not opened yet. * IPCOM_ERR_NOT_FOUND - Subnet does not exist. * IPCOM_ERR_INVALID_ARG - Invalid net address. * */ IP_PUBLIC Ip_err ipdhcps_subnet_delete(Ip_u32 subnet) { Ip_err retval; Ipdhcps_subnet *s; if(ipdhcpsc.opened != IP_TRUE) return(IPCOM_ERR_NOT_OPENED); IPDHCPS_LOCK(); if(subnet == IP_INADDR_ANY) IPDHCPS_LEAVE(IPCOM_ERR_INVALID_ARG); if((s = ipdhcps_subnet_find(subnet)) == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NOT_FOUND); retval = ipdhcps_subnet_do_delete(s); leave: IPDHCPS_UNLOCK(); return retval; } /* *=========================================================================== * Ipdhcps_host_add *=========================================================================== * Description: Add a permanent host to the IPDHCPS configuration. * Parameters: addr - Permanent host IP address in network byte order. * hwaddr - Link layer address (MAC). * len - Length of link layer address. * dhcp_options - Byte array of dhcp options specific to this * subnet. * Standard DHCP format (code,len,data,code,len,data..) * conf_options - Byte array of configuration options specific * to this subnet. * Standard DHCP format (code,len,data,code,len,data..) * * NOTE 1: DHCP options are always stored in network byte * order. * NOTE 2: Configuration options are always stored in host byte * order. * NOTE 3: It is allowed to leave out the hwaddr if a client * identifier is specified in the DHCP option list * (DHCP option 61). IPDHCPS will then use only this id * for client identification. * If both a hwaddr and a client identifier is specified the * IPDHCPS will identify the client first by its id and then * by hwaddr. * Returns: * IPCOM_SUCCESS - success * other - failure */ IP_PUBLIC Ip_err ipdhcps_host_add(Ip_u32 addr, Ip_u8 *hwaddr, Ip_u8 len, Ip_u8 *dhcp_options, Ip_u8 *conf_options) { Ip_err retval; Ip_u8 *cid = IP_NULL; Ip_u8 cid_len = 0; Ipdhcps_host *host = IP_NULL; Ip_u8 *cid_opt; Ipdhcps_host_lease_find_info info; if(ipdhcpsc.opened != IP_TRUE) return(IPCOM_ERR_NOT_OPENED); IPDHCPS_LOCK(); /* * Determine if we are going to reference this host by its * hardware address or by a possible client identifier. */ cid_opt = ipdhcps_option_find(dhcp_options, IPDHCPS_OPTCODE_CLIENT_IDENTIFIER); if(cid_opt != IP_NULL) { cid = &cid_opt[IPDHCPS_OPT_DATA]; cid_len = cid_opt[IPDHCPS_OPT_LEN]; } else { /* No client id. Check if we have a hw address */ if(hwaddr == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_INVALID_CONFIG); } /* Check if we already have a host with this finger print in lease database */ if (ipdhcpsc.lease_htable != IP_NULL) { info.addr = addr; info.found = IP_FALSE; ipcom_hash_for_each(ipdhcpsc.lease_htable, (Ipcom_hash_foreach_cb_func)ipdhcps_host_lease_cmp, &info); if (info.found == IP_TRUE) IPDHCPS_LEAVE(IPCOM_ERR_ALREADY_INSTALLED); if(ipdhcps_host_lease_find(ipdhcps_subnet_find_matching(addr), (Ip_size_t)len, hwaddr, cid_len, cid) != IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_ALREADY_INSTALLED); } /* Add the host */ if((host = ipdhcps_host_new(addr, (Ip_size_t)len, hwaddr, (Ip_size_t)cid_len, cid)) == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NO_MEMORY); /* Add configuration options */ if(ipdhcps_option_set_add(&host->node, conf_options, IPDHCPS_OPT_CONF) != IPCOM_SUCCESS) IPDHCPS_LEAVE(IPCOM_ERR_INVALID_CONFIG); /* Add dhcp options to the host */ retval = ipdhcps_option_set_add(&host->node, dhcp_options, IPDHCPS_OPT_DHCP); leave: if ((retval != IPCOM_SUCCESS) && (host != IP_NULL)) { /* Release resources upon faliure */ (void)ipdhcps_host_do_delete(host); } IPDHCPS_UNLOCK(); return retval; } /* *=========================================================================== * Ipdhcps_host_delete *=========================================================================== * Description: Deletes a permanent host declaration from the IPDHCPS * configuration. * Parameters: addr - IP address of host to delete in network byte order. * * Returns: IPCOM_SUCCESS - success * other - failure */ IP_PUBLIC Ip_err ipdhcps_host_delete(Ip_u32 addr) { Ip_err retval; Ipdhcps_host *h; if(ipdhcpsc.opened != IP_TRUE) return(IPCOM_ERR_NOT_OPENED); IPDHCPS_LOCK(); if((h = ipdhcps_host_find(addr)) == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NOT_FOUND); retval = ipdhcps_host_do_delete(h); leave: IPDHCPS_UNLOCK(); return retval; } /* *=========================================================================== * Ipdhcps_pool_add *=========================================================================== * Description: Add a new address pool to an existing subnet declaration * in the IPDHCPS configuration. * Parameters: subnet - Subnet IP address in network byte order. * pool - Address pool to be mapped to the subnet. * * NOTE: The value IP_INADDR_ANY for subnet will add * the pool to the global default configuration. * * Returns: IPCOM_SUCCESS - success * other - failure * */ IP_PUBLIC Ip_err ipdhcps_pool_add(Ip_u32 subnet, Ipdhcps_poolinfo *pool) { Ip_err retval; Ipdhcps_subnet *s; if(ipdhcpsc.opened != IP_TRUE) return(IPCOM_ERR_NOT_OPENED); IPDHCPS_LOCK(); if(subnet == 0) s = ipdhcpsc.default_net; else { s = ipdhcps_subnet_find(subnet); if(s == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NOT_FOUND); } if(ipdhcps_pool_config_sane(&s->pools, ip_ntohl(pool->first_addr), ip_ntohl(pool->last_addr)) != IP_TRUE) IPDHCPS_LEAVE(IPCOM_ERR_INVALID_CONFIG); if(ipdhcps_pool_new(s, &s->pools, ip_ntohl(pool->first_addr), ip_ntohl(pool->last_addr), pool->classname) != IPCOM_SUCCESS) IPDHCPS_LEAVE(IPCOM_ERR_NO_MEMORY); retval = IPCOM_SUCCESS; leave: IPDHCPS_UNLOCK(); return retval; } /* *=========================================================================== * Ipdhcps_pool_delete *=========================================================================== * Description: Delete an address pool on an exiating subnet in the * IPDHCPS configuration. * Parameters: subnet - Subnet IP address in network byte order. * pool - Address pool to be mapped to the subnet. * * NOTE: The value IP_INADDR_ANY for subnet will delete * the pool from the global default configuration. * * Returns: IPCOM_SUCCESS - success * other - failure */ IP_PUBLIC Ip_err ipdhcps_pool_delete(Ip_u32 subnet, Ipdhcps_poolinfo *pool) { Ip_err retval; Ipdhcps_pool *p; Ipdhcps_subnet *s; if(ipdhcpsc.opened != IP_TRUE) return(IPCOM_ERR_NOT_OPENED); IPDHCPS_LOCK(); if((s = ipdhcps_subnet_find(subnet)) == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NOT_FOUND); if((p = ipdhcps_pool_find(&s->pools, ip_ntohl(pool->first_addr), ip_ntohl(pool->last_addr))) == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NOT_FOUND); /* Delete pool */ retval = ipdhcps_pool_do_delete(p); leave: IPDHCPS_UNLOCK(); return retval; } /* *=========================================================================== * Ipdhcps_class_add *=========================================================================== * Description: Add a class definition to a certain subnet * in the IPDHCPS configuration. * Parameters: subnet - Subnet IP address in network byte order. * class - Class identifying info. * * * NOTE: The value IP_INADDR_ANY for subnet will add * the class to the global default configuration. * * Returns: IPCOM_SUCCESS - success * other - failure * */ IP_PUBLIC Ip_err ipdhcps_class_add(Ip_u32 subnet, Ipdhcps_classinfo *client_class) { Ip_err retval; Ipdhcps_subnet *s; if(ipdhcpsc.opened != IP_TRUE) return(IPCOM_ERR_NOT_OPENED); IPDHCPS_LOCK(); if(subnet == 0) s = ipdhcpsc.default_net; else { s = ipdhcps_subnet_find(subnet); if(s == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NOT_FOUND); } retval = ipdhcps_class_new(s, client_class); leave: IPDHCPS_UNLOCK(); return retval; } /* *=========================================================================== * Ipdhcps_class_delete *=========================================================================== * Description: Delete a client class on an exiating subnet in the * IPDHCPS configuration. * Parameters: subnet - Subnet IP address in network byte order. * client_class - Class identifying info. * * NOTE: The value IP_INADDR_ANY for subnet will delete * the class from the global default configuration. * * Returns: IPCOM_SUCCESS - success * other - failure */ IP_PUBLIC Ip_err ipdhcps_class_delete(Ip_u32 subnet, char *classname) { Ip_err retval; Ipdhcps_class *c; Ipdhcps_subnet *s; if(ipdhcpsc.opened != IP_TRUE) return(IPCOM_ERR_NOT_OPENED); IPDHCPS_LOCK(); if((s = ipdhcps_subnet_find(subnet)) == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NOT_FOUND); if((c = ipdhcps_class_find_by_name(s, classname)) == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NOT_FOUND); /* Delete class */ retval = ipdhcps_class_do_delete(c, s->class_head); leave: IPDHCPS_UNLOCK(); return retval; } /* *=========================================================================== * Ipdhcps_option_add *=========================================================================== * Description: Add a new set of DHCP or CONFIG options to an existing address * entity (either a subnet or a host). An option that already * exists on the entity will be replaced by the new one in the * set. * * Parameters: addr - IP address to either a subnet or host. * * NOTE 1: DHCP options are always stored in network byte * order. * NOTE 2: The value IP_INADDR_ANY (0) of addr will make IPDHCPS * apply the options on the global default dhcp * option set. * * Returns: IPCOM_SUCCESS - success * other - failure */ IP_PUBLIC Ip_err ipdhcps_option_add(Ip_u32 addr, char *client_class, Ip_u16 type, Ip_u8 *opt) { Ip_err retval; Ipdhcps_host *h; Ipdhcps_subnet *s; Ipdhcps_class *c; Ipdhcps_node *node; if(ipdhcpsc.opened != IP_TRUE) return(IPCOM_ERR_NOT_OPENED); IPDHCPS_LOCK(); if((s = ipdhcps_subnet_find(addr)) != IP_NULL) { if (IPDHCPS_OPTCODE_SUBNET_MASK == IPDHCPS_OPT_CREATE(opt[IPDHCPS_OPT_CODE] ,type)) IPDHCPS_LEAVE(IPCOM_ERR_FAILED); if (client_class != IP_NULL) { c = ipdhcps_class_find_by_name(s, client_class); if (c == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NOT_FOUND); node = &c->node; } else node = &s->node; } else if((h = ipdhcps_host_find(addr)) != IP_NULL) node = &h->node; else IPDHCPS_LEAVE(IPCOM_ERR_NOT_FOUND); if(ipdhcps_option_set_add(node, opt, type) != IPCOM_SUCCESS) IPDHCPS_LEAVE(IPCOM_ERR_FAILED); retval = IPCOM_SUCCESS; leave: IPDHCPS_UNLOCK(); return retval; } /* *=========================================================================== * ipdhcps_option_delete *=========================================================================== * Description: Delete a set of DHCP or CONFIG options on an existing address * entity (either a subnet or a host). * * NOTE! No error will be reported if a certain opiton in * the set does not exist on the entity. * * Parameters: addr - IP address to either a subnet or host. * * NOTE! The value IP_INADDR_ANY (0) of addr will make IPDHCPS * apply the options on the global default configuration * set. * * Returns: IPCOM_SUCCESS - success * other - failure * */ IP_PUBLIC Ip_err ipdhcps_option_delete(Ip_u32 addr, char* client_class, Ip_u16 type, Ip_u8 option_code) { Ip_err retval; Ipdhcps_host *h; Ipdhcps_subnet *s; Ipdhcps_class *c; Ipdhcps_option *o; Ipdhcps_node *node; if(ipdhcpsc.opened != IP_TRUE) return(IPCOM_ERR_NOT_OPENED); IPDHCPS_LOCK(); if((s = ipdhcps_subnet_find(addr)) != IP_NULL) { if (IPDHCPS_OPTCODE_SUBNET_MASK == IPDHCPS_OPT_CREATE(option_code ,type) ) IPDHCPS_LEAVE(IPCOM_ERR_FAILED); if (client_class != IP_NULL) { c = ipdhcps_class_find_by_name(s, client_class); if (c == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NOT_FOUND); node = &c->node; } else node = &s->node; } else if((h = ipdhcps_host_find(addr)) != IP_NULL) node = &h->node; else IPDHCPS_LEAVE(IPCOM_ERR_NOT_FOUND); if((o = ipdhcps_option_set_find(node, (Ip_u16)IPDHCPS_OPT_CREATE(type,option_code))) == IP_NULL) IPDHCPS_LEAVE(IPCOM_ERR_NOT_FOUND); ipdhcps_option_set_do_delete(o, node); retval = IPCOM_SUCCESS; leave: IPDHCPS_UNLOCK(); return retval; } /* **************************************************************************** * END OF FILE **************************************************************************** */