refactored the rpl-nbr-policy to be called from nbr-table module

This commit is contained in:
Joakim Eriksson 2015-09-20 21:02:30 +02:00
parent be81d1d2c6
commit 444015df67
13 changed files with 198 additions and 161 deletions

View file

@ -148,6 +148,13 @@
#define UIP_CONF_IPV6_RPL 1
#endif /* UIP_CONF_IPV6_RPL */
/* If RPL is enabled also enable the RPL NBR Policy */
#if UIP_CONF_IPV6_RPL
#ifndef NBR_TABLE_FIND_REMOVABLE
#define NBR_TABLE_FIND_REMOVABLE rpl_nbr_policy_find_removable
#endif /* NBR_TABLE_FIND_REMOVABLE */
#endif /* UIP_CONF_IPV6_RPL */
/* UIP_CONF_MAX_ROUTES specifies the maximum number of routes that each
node will be able to handle. */
#ifndef UIP_CONF_MAX_ROUTES

View file

@ -79,9 +79,11 @@ uip_ds6_neighbors_init(void)
/*---------------------------------------------------------------------------*/
uip_ds6_nbr_t *
uip_ds6_nbr_add(const uip_ipaddr_t *ipaddr, const uip_lladdr_t *lladdr,
uint8_t isrouter, uint8_t state)
uint8_t isrouter, uint8_t state, nbr_table_reason_t reason,
void *data)
{
uip_ds6_nbr_t *nbr = nbr_table_add_lladdr(ds6_neighbors, (linkaddr_t*)lladdr);
uip_ds6_nbr_t *nbr = nbr_table_add_lladdr(ds6_neighbors, (linkaddr_t*)lladdr
,reason, data);
if(nbr) {
uip_ipaddr_copy(&nbr->ipaddr, ipaddr);
#if UIP_ND6_SEND_NA || UIP_ND6_SEND_RA || !UIP_CONF_ROUTER

View file

@ -86,8 +86,10 @@ typedef struct uip_ds6_nbr {
void uip_ds6_neighbors_init(void);
/** \brief Neighbor Cache basic routines */
uip_ds6_nbr_t *uip_ds6_nbr_add(const uip_ipaddr_t *ipaddr, const uip_lladdr_t *lladdr,
uint8_t isrouter, uint8_t state);
uip_ds6_nbr_t *uip_ds6_nbr_add(const uip_ipaddr_t *ipaddr,
const uip_lladdr_t *lladdr,
uint8_t isrouter, uint8_t state,
nbr_table_reason_t reason, void *data);
int uip_ds6_nbr_rm(uip_ds6_nbr_t *nbr);
const uip_lladdr_t *uip_ds6_nbr_get_ll(const uip_ds6_nbr_t *nbr);
const uip_ipaddr_t *uip_ds6_nbr_get_ipaddr(const uip_ds6_nbr_t *nbr);

View file

@ -348,7 +348,8 @@ uip_ds6_route_add(uip_ipaddr_t *ipaddr, uint8_t length,
initialize this pointer with the list of routing entries that
are attached to this neighbor. */
routes = nbr_table_add_lladdr(nbr_routes,
(linkaddr_t *)nexthop_lladdr);
(linkaddr_t *)nexthop_lladdr,
NBR_TABLE_REASON_ROUTE, NULL);
if(routes == NULL) {
/* This should not happen, as we explicitly deallocated one
route table entry above. */

View file

@ -188,7 +188,7 @@ parse(void)
info = nbr_table_get_from_lladdr(anti_replay_table, sender);
if(!info) {
info = nbr_table_add_lladdr(anti_replay_table, sender);
info = nbr_table_add_lladdr(anti_replay_table, sender, NBR_TABLE_REASON_LLSEC, NULL);
if(!info) {
PRINTF("noncoresec: could not get nbr_table_item\n");
return FRAMER_FAILED;

View file

@ -122,7 +122,7 @@ phase_update(const linkaddr_t *neighbor, rtimer_clock_t time,
} else {
/* No matching phase was found, so we allocate a new one. */
if(mac_status == MAC_TX_OK && e == NULL) {
e = nbr_table_add_lladdr(nbr_phase, neighbor);
e = nbr_table_add_lladdr(nbr_phase, neighbor, NBR_TABLE_REASON_MAC, NULL);
if(e) {
e->time = time;
#if PHASE_DRIFT_CORRECT

View file

@ -47,11 +47,20 @@
static void handle_periodic_timer(void *ptr);
static struct ctimer periodic_timer;
static uint8_t initialized = 0;
static void print_table();
#define PRINTF(...) printf(__VA_ARGS__)
#else
#define PRINTF(...)
#endif
/* This is the callback function that will be called when there is a
* nbr-policy active
**/
#ifdef NBR_TABLE_FIND_REMOVABLE
const linkaddr_t *NBR_TABLE_FIND_REMOVABLE(nbr_table_reason_t reason, void *data);
#endif /* NBR_TABLE_FIND_REMOVABLE */
/* List of link-layer addresses of the neighbors, used as key in the tables */
typedef struct nbr_table_key {
struct nbr_table_key *next;
@ -170,7 +179,7 @@ nbr_set_bit(uint8_t *bitmap, nbr_table_t *table, nbr_table_item_t *item, int val
}
/*---------------------------------------------------------------------------*/
static nbr_table_key_t *
nbr_table_allocate(void)
nbr_table_allocate(nbr_table_reason_t reason, void *data)
{
nbr_table_key_t *key;
int least_used_count = 0;
@ -179,7 +188,32 @@ nbr_table_allocate(void)
key = memb_alloc(&neighbor_addr_mem);
if(key != NULL) {
return key;
} else { /* No more space, try to free a neighbor.
} else {
#ifdef NBR_TABLE_FIND_REMOVABLE
const linkaddr_t *lladdr;
lladdr = NBR_TABLE_FIND_REMOVABLE(reason, data);
if(lladdr == NULL) {
/* Nothing found that can be deleted - return NULL to indicate failure */
PRINTF("*** Not removing entry to allocate new\n");
return NULL;
} else {
/* used least_used_key to indicate what is the least useful entry */
int index;
int locked;
if((index = index_from_lladdr(lladdr)) != -1) {
least_used_key = key_from_index(index);
locked = locked_map[index];
}
/* Allow delete of locked item? */
if(least_used_key != NULL && locked) {
PRINTF("Deleting locked item!\n");
locked_map[index] = 0;
}
}
#endif /* NBR_TABLE_FIND_REMOVABLE */
if(least_used_key == NULL) {
/* No more space, try to free a neighbor.
* The replacement policy is the following: remove neighbor that is:
* (1) not locked
* (2) used by fewest tables
@ -212,6 +246,8 @@ nbr_table_allocate(void)
}
key = list_item_next(key);
}
}
if(least_used_key == NULL) {
/* We haven't found any unlocked item, allocation fails */
return NULL;
@ -289,7 +325,7 @@ nbr_table_next(nbr_table_t *table, nbr_table_item_t *item)
/*---------------------------------------------------------------------------*/
/* Add a neighbor indexed with its link-layer address */
nbr_table_item_t *
nbr_table_add_lladdr(nbr_table_t *table, const linkaddr_t *lladdr)
nbr_table_add_lladdr(nbr_table_t *table, const linkaddr_t *lladdr, nbr_table_reason_t reason, void *data)
{
int index;
nbr_table_item_t *item;
@ -303,7 +339,7 @@ nbr_table_add_lladdr(nbr_table_t *table, const linkaddr_t *lladdr)
if((index = index_from_lladdr(lladdr)) == -1) {
/* Neighbor not yet in table, let's try to allocate one */
key = nbr_table_allocate();
key = nbr_table_allocate(reason, data);
/* No space available for new entry */
if(key == NULL) {
@ -327,6 +363,9 @@ nbr_table_add_lladdr(nbr_table_t *table, const linkaddr_t *lladdr)
memset(item, 0, table->item_size);
nbr_set_bit(used_map, table, item, 1);
#if DEBUG
print_table();
#endif
return item;
}
/*---------------------------------------------------------------------------*/
@ -379,7 +418,7 @@ nbr_table_get_lladdr(nbr_table_t *table, const void *item)
/*---------------------------------------------------------------------------*/
#if DEBUG
static void
handle_periodic_timer(void *ptr)
print_table()
{
int i, j;
/* Printout all neighbors and which tables they are used in */
@ -394,6 +433,12 @@ handle_periodic_timer(void *ptr)
PRINTF("\n");
}
}
}
/*---------------------------------------------------------------------------*/
static void
handle_periodic_timer(void *ptr)
{
print_table();
ctimer_reset(&periodic_timer);
}
#endif

View file

@ -75,6 +75,17 @@ typedef struct nbr_table {
/** \brief Declaration of non-static neighbor tables */
#define NBR_TABLE_DECLARE(name) extern nbr_table_t *name
typedef enum {
NBR_TABLE_REASON_UNDEFINED,
NBR_TABLE_REASON_RPL_DIO,
NBR_TABLE_REASON_RPL_DAO,
NBR_TABLE_REASON_RPL_DIS,
NBR_TABLE_REASON_ROUTE,
NBR_TABLE_REASON_IPV6_ND,
NBR_TABLE_REASON_MAC,
NBR_TABLE_REASON_LLSEC
} nbr_table_reason_t;
/** \name Neighbor tables: register and loop through table elements */
/** @{ */
int nbr_table_register(nbr_table_t *table, nbr_table_callback *callback);
@ -84,7 +95,7 @@ nbr_table_item_t *nbr_table_next(nbr_table_t *table, nbr_table_item_t *item);
/** \name Neighbor tables: add and get data */
/** @{ */
nbr_table_item_t *nbr_table_add_lladdr(nbr_table_t *table, const linkaddr_t *lladdr);
nbr_table_item_t *nbr_table_add_lladdr(nbr_table_t *table, const linkaddr_t *lladdr, nbr_table_reason_t reason, void *data);
nbr_table_item_t *nbr_table_get_from_lladdr(nbr_table_t *table, const linkaddr_t *lladdr);
/** @} */

View file

@ -634,8 +634,9 @@ rpl_add_parent(rpl_dag_t *dag, rpl_dio_t *dio, uip_ipaddr_t *addr)
PRINT6ADDR(addr);
PRINTF("\n");
if(lladdr != NULL) {
/* Add parent in rpl_parents */
p = nbr_table_add_lladdr(rpl_parents, (linkaddr_t *)lladdr);
/* Add parent in rpl_parents - again this is due to DIO */
p = nbr_table_add_lladdr(rpl_parents, (linkaddr_t *)lladdr,
NBR_TABLE_REASON_RPL_DIO, dio);
if(p == NULL) {
PRINTF("RPL: rpl_add_parent p NULL\n");
} else {
@ -1028,7 +1029,7 @@ rpl_join_instance(uip_ipaddr_t *from, rpl_dio_t *dio)
rpl_set_default_route(instance, from);
if(instance->mop != RPL_MOP_NO_DOWNWARD_ROUTES) {
rpl_schedule_dao_immediately(instance);
rpl_schedule_dao(instance);
} else {
PRINTF("RPL: The DIO does not meet the prerequisites for sending a DAO\n");
}
@ -1255,18 +1256,14 @@ rpl_process_parent_event(rpl_instance_t *instance, rpl_parent_t *p)
static int
add_nbr_from_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
{
/* check if it is ok to add this nbr based on this DIO */
if(RPL_NBR_POLICY.check_add_from_dio(from, dio)) {
/* add this to the neighbor cache if not already there */
if(rpl_icmp6_update_nbr_table(from) == NULL) {
if(rpl_icmp6_update_nbr_table(from, NBR_TABLE_REASON_RPL_DIO, dio) == NULL) {
PRINTF("RPL: Out of memory, dropping DIO from ");
PRINT6ADDR(from);
PRINTF("\n");
return 0;
}
return 1;
}
return 0;
}
/*---------------------------------------------------------------------------*/
void

View file

@ -182,14 +182,14 @@ set16(uint8_t *buffer, int pos, uint16_t value)
}
/*---------------------------------------------------------------------------*/
uip_ds6_nbr_t *
rpl_icmp6_update_nbr_table(uip_ipaddr_t *from)
rpl_icmp6_update_nbr_table(uip_ipaddr_t *from, nbr_table_reason_t reason, void *data)
{
uip_ds6_nbr_t *nbr;
if((nbr = uip_ds6_nbr_lookup(from)) == NULL) {
if((nbr = uip_ds6_nbr_add(from, (uip_lladdr_t *)
packetbuf_addr(PACKETBUF_ADDR_SENDER),
0, NBR_REACHABLE)) != NULL) {
0, NBR_REACHABLE, reason, data)) != NULL) {
PRINTF("RPL: Neighbor added to neighbor cache ");
PRINT6ADDR(from);
PRINTF(", ");
@ -231,19 +231,18 @@ dis_input(void)
} else {
#endif /* !RPL_LEAF_ONLY */
/* Check if this neighbor should be added according to the policy. */
if(RPL_NBR_POLICY.check_add_from_dis(&UIP_IP_BUF->srcipaddr)) {
/* Add this to the neighbor cache if not already there */
if(rpl_icmp6_update_nbr_table(&UIP_IP_BUF->srcipaddr) == NULL) {
if(rpl_icmp6_update_nbr_table(&UIP_IP_BUF->srcipaddr,
NBR_TABLE_REASON_RPL_DIS, NULL) == NULL) {
PRINTF("RPL: Out of Memory, not sending unicast DIO, DIS from ");
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
PRINTF(", ");
PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
PRINTF("\n");
return;
}
} else {
PRINTF("RPL: Unicast DIS, reply to sender\n");
dio_output(instance, &UIP_IP_BUF->srcipaddr);
}
/* } */
}
}
}
@ -806,18 +805,18 @@ dao_input(void)
PRINTF("RPL: adding DAO route\n");
/* Check if we should add another neighbor based on DAO. */
if(!RPL_NBR_POLICY.check_add_from_dao(&dao_sender_addr)) {
/* Do not add the neighbor. */
return;
}
/* Update and add neighbor - if no room - fail. */
if((nbr = rpl_icmp6_update_nbr_table(&dao_sender_addr)) == NULL) {
if((nbr = rpl_icmp6_update_nbr_table(&dao_sender_addr, NBR_TABLE_REASON_RPL_DAO, NULL)) == NULL) {
PRINTF("RPL: Out of Memory, dropping DAO from ");
PRINT6ADDR(&dao_sender_addr);
PRINTF(", ");
PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
PRINTF("\n");
if(flags & RPL_DAO_K_FLAG) {
/* signal the failure to add the node */
dao_ack_output(instance, &dao_sender_addr, sequence,
RPL_DAO_ACK_UNABLE_TO_ACCEPT);
}
goto discard;
}
@ -931,7 +930,9 @@ handle_dao_retransmission(void *ptr)
return;
}
ctimer_set(&instance->dao_retransmit_timer, RPL_DAO_RETRANSMISSION_TIMEOUT,
ctimer_set(&instance->dao_retransmit_timer,
RPL_DAO_RETRANSMISSION_TIMEOUT / 2 +
(random_rand() % (RPL_DAO_RETRANSMISSION_TIMEOUT / 2)),
handle_dao_retransmission, parent);
instance->my_dao_transmissions++;
@ -1079,6 +1080,7 @@ dao_ack_input(void)
buffer = UIP_ICMP_PAYLOAD;
instance_id = buffer[0];
sequence = buffer[2];
status = buffer[3];
@ -1131,7 +1133,9 @@ dao_ack_input(void)
if(nexthop == NULL) {
PRINTF("No next hop to fwd DAO ACK to\n");
} else {
PRINTF("Fwd DAO ACK\n");
PRINTF("Fwd DAO ACK to:");
PRINT6ADDR(nexthop);
PRINTF("\n");
buffer[2] = re->state.dao_seqno_in;
uip_icmp6_send(nexthop, ICMP6_RPL, RPL_CODE_DAO_ACK, 4);
}

View file

@ -49,7 +49,7 @@
#include "net/ipv6/uip-ds6-nbr.h"
#include "net/ipv6/uip-ds6-route.h"
#define DEBUG DEBUG_NONE
#define DEBUG DEBUG_FULL
#include "net/ip/uip-debug.h"
/*
@ -58,14 +58,18 @@
* - max X children (nexthops)
* - max Y "best parents"
* => at least MAX_NBRS - (Y + X + 1) free slots for other.
*
* NOTE: this policy assumes that all neighbors end up being IPv6
* neighbors and are not only MAC neighbors.
*/
#define MAX_CHILDREN (NBR_TABLE_MAX_NEIGHBORS - 3)
#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN])
static int num_parents; /* any node that are possible parents */
static int num_children; /* all children that we have as nexthop */
static int num_free;
static uip_ds6_nbr_t *worst_rank_nbr; /* the parent that has the worst rank */
static linkaddr_t *worst_rank_nbr; /* the parent that has the worst rank */
static rpl_rank_t worst_rank;
/*---------------------------------------------------------------------------*/
#if DEBUG == DEBUG_FULL
@ -124,8 +128,12 @@ update_nbr(void)
parent->dag->instance != NULL &&
(rank = parent->dag->instance->of->calculate_rank(parent, 0)) > worst_rank) {
/* This is the worst-rank neighbor - this is a good candidate for removal */
if(uip_ds6_route_is_nexthop((uip_lladdr_t *)lladdr) == 0) {
worst_rank = rank;
worst_rank_nbr = nbr;
worst_rank_nbr = lladdr;
} else {
printf("*** Can not use this as worst rank as it is a next hop\n");
}
}
}
@ -138,10 +146,9 @@ update_nbr(void)
if(is_used == 0) {
/* This neighbor is neither parent or child and can be safely removed */
worst_rank_nbr = nbr;
worst_rank_nbr = lladdr;
worst_rank = INFINITE_RANK;
} else if(is_used > 1) {
/* Both parent and child - this should never happen! */
PRINTF("NBR-POLICY: *** Neighbor is both child and candidate parent: ");
PRINTLLADDR((uip_lladdr_t *)lladdr);
PRINTF("\n");
@ -157,67 +164,36 @@ update_nbr(void)
num_free, num_children, num_parents, uip_ds6_route_num_routes());
}
/*---------------------------------------------------------------------------*/
static int
remove_worst_nbr(void)
{
/* we assume that it is possible to remove the worst parent at the moment */
if(worst_rank_nbr != NULL) {
PRINTF("Removing worst ranked nbr ");
PRINTLLADDR((uip_lladdr_t*)nbr_table_get_lladdr(ds6_neighbors, worst_rank_nbr));
PRINTF(" with rank %d\n", worst_rank);
if(uip_ds6_nbr_rm(worst_rank_nbr)) {
worst_rank_nbr = NULL;
return 1;
}
PRINTF("FAILED to remove worst ranked nbr!\n");
return 0;
}
PRINTF("FAILED to remove worst rank nbr - no found\n");
return 0;
}
/*---------------------------------------------------------------------------*/
/* Called whenever we get a unicast DIS - e.g. someone that already
have this node in its table - since it is a unicast */
static int
check_add_from_dis(uip_ipaddr_t *from)
const linkaddr_t *
find_removable_dis(uip_ipaddr_t *from)
{
/* do a lookup to see if it is alread there - then allow add/update */
if(uip_ds6_nbr_lookup(from)) {
return 1;
}
update_nbr();
if(num_free > 0) {
return 1;
printf("num-free > 0 = %d", num_free);
printf("**** Should remove unused elements but can not... \n");
/* return 1; */
}
if(num_children < MAX_CHILDREN) {
return remove_worst_nbr();
return worst_rank_nbr;
}
return 0;
return NULL;
}
/*---------------------------------------------------------------------------*/
static int
check_add_from_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
const linkaddr_t *
find_removable_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
{
rpl_instance_t *instance;
rpl_rank_t rank;
/* Do a lookup to see if it is already there - then allow add/update. */
if(uip_ds6_nbr_lookup(from)) {
return 1;
}
update_nbr();
/* If there is room for this neighbor just add it. */
if(num_free > 0) {
return 1;
}
instance = rpl_get_instance(dio->instance_id);
if(instance == NULL || instance->current_dag == NULL) {
PRINTF("Did not find instance id: %d\n", dio->instance_id);
return 0;
return NULL;
}
/* Add the new neighbor only if it is better than the preferred parent. */
@ -227,38 +203,43 @@ check_add_from_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
PRINTF("Found better neighbor %d < %d - add to cache...\n",
rank, worst_rank);
return remove_worst_nbr();
return worst_rank_nbr;
}
PRINTF("Found worse neighbor with new %d and old %d - NOT add to cache.\n",
rank, worst_rank);
return 0;
return NULL;
}
/*---------------------------------------------------------------------------*/
static int
check_add_from_dao(uip_ipaddr_t *from)
const linkaddr_t *
find_removable_dao(uip_ipaddr_t *from)
{
/* Do a lookup to see if it is alread there - then allow add/update. */
if(uip_ds6_nbr_lookup(from)) {
return 1;
}
update_nbr();
/* Check if this DAO sender is not yet neighbor and there is already too
many children. */
if(num_children >= MAX_CHILDREN) {
PRINTF("Can not add another child - already at max.\n");
return 0;
return NULL;
}
return 1;
/* remove the worst ranked nbr */
return worst_rank_nbr;
}
/*---------------------------------------------------------------------------*/
const struct nbr_policy rpl_nbr_policy = {
check_add_from_dis,
check_add_from_dio,
check_add_from_dao
};
const linkaddr_t *
rpl_nbr_policy_find_removable(nbr_table_reason_t reason,void * data) {
/* When we get the DIO/DAO/DIS we know that UIP contains the
incoming packet */
switch(reason) {
case NBR_TABLE_REASON_RPL_DIO:
return find_removable_dio(&UIP_IP_BUF->srcipaddr, data);
case NBR_TABLE_REASON_RPL_DAO:
return find_removable_dao(&UIP_IP_BUF->srcipaddr);
case NBR_TABLE_REASON_RPL_DIS:
return find_removable_dis(&UIP_IP_BUF->srcipaddr);
default:
return NULL;
}
}
/*---------------------------------------------------------------------------*/
/** @}*/

View file

@ -270,22 +270,6 @@ typedef struct rpl_stats rpl_stats_t;
extern rpl_stats_t rpl_stats;
#endif
struct nbr_policy {
/** check if it is ok to add a nbr via UC DIS - positive => ok */
int (* check_add_from_dis)(uip_ipaddr_t *from);
int (* check_add_from_dio)(uip_ipaddr_t *from, rpl_dio_t *dio);
int (* check_add_from_dao)(uip_ipaddr_t *from);
};
#ifdef RPL_CONF_NBR_POLICY
#define RPL_NBR_POLICY RPL_CONF_NBR_POLICY
#else /* RPL_CONF_NBR_POLICY */
#define RPL_NBR_POLICY rpl_nbr_policy
#endif /* RPL_CONF_NBR_POLICY */
extern const struct nbr_policy RPL_NBR_POLICY;
/*---------------------------------------------------------------------------*/
/* RPL macros. */
@ -307,7 +291,8 @@ void dao_output(rpl_parent_t *, uint8_t lifetime);
void dao_output_target(rpl_parent_t *, uip_ipaddr_t *, uint8_t lifetime);
void dao_ack_output(rpl_instance_t *, uip_ipaddr_t *, uint8_t, uint8_t);
void rpl_icmp6_register_handlers(void);
uip_ds6_nbr_t *rpl_icmp6_update_nbr_table(uip_ipaddr_t *from);
uip_ds6_nbr_t *rpl_icmp6_update_nbr_table(uip_ipaddr_t *from,
nbr_table_reason_t r, void *data);
/* RPL logic functions. */
void rpl_join_dag(uip_ipaddr_t *from, rpl_dio_t *dio);

View file

@ -224,6 +224,8 @@ set_dao_lifetime_timer(rpl_instance_t *instance)
expiration_time = (clock_time_t)instance->default_lifetime *
(clock_time_t)instance->lifetime_unit *
CLOCK_SECOND / 2;
/* make the time for the re registration be betwen 1/2 - 3/4 of lifetime */
expiration_time = expiration_time + (random_rand() % (expiration_time / 2));
PRINTF("RPL: Scheduling DAO lifetime timer %u ticks in the future\n",
(unsigned)expiration_time);
ctimer_set(&instance->dao_lifetime_timer, expiration_time,