Merge branch 'mobike-fixes'
These changes improve the handling of MOBIKE tasks, for instance, when retransmitting and no path is available. Fixes #632.
This commit is contained in:
@@ -1911,11 +1911,29 @@ static bool is_any_path_valid(private_ike_sa_t *this)
|
|||||||
bool valid = FALSE;
|
bool valid = FALSE;
|
||||||
enumerator_t *enumerator;
|
enumerator_t *enumerator;
|
||||||
host_t *src = NULL, *addr;
|
host_t *src = NULL, *addr;
|
||||||
|
int family = AF_UNSPEC;
|
||||||
|
|
||||||
|
switch (charon->socket->supported_families(charon->socket))
|
||||||
|
{
|
||||||
|
case SOCKET_FAMILY_IPV4:
|
||||||
|
family = AF_INET;
|
||||||
|
break;
|
||||||
|
case SOCKET_FAMILY_IPV6:
|
||||||
|
family = AF_INET6;
|
||||||
|
break;
|
||||||
|
case SOCKET_FAMILY_BOTH:
|
||||||
|
case SOCKET_FAMILY_NONE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
DBG1(DBG_IKE, "old path is not available anymore, try to find another");
|
DBG1(DBG_IKE, "old path is not available anymore, try to find another");
|
||||||
enumerator = create_peer_address_enumerator(this);
|
enumerator = create_peer_address_enumerator(this);
|
||||||
while (enumerator->enumerate(enumerator, &addr))
|
while (enumerator->enumerate(enumerator, &addr))
|
||||||
{
|
{
|
||||||
|
if (family != AF_UNSPEC && addr->get_family(addr) != family)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
DBG1(DBG_IKE, "looking for a route to %H ...", addr);
|
DBG1(DBG_IKE, "looking for a route to %H ...", addr);
|
||||||
src = hydra->kernel_interface->get_source_addr(
|
src = hydra->kernel_interface->get_source_addr(
|
||||||
hydra->kernel_interface, addr, NULL);
|
hydra->kernel_interface, addr, NULL);
|
||||||
|
|||||||
@@ -120,6 +120,11 @@ struct private_task_manager_t {
|
|||||||
*/
|
*/
|
||||||
exchange_type_t type;
|
exchange_type_t type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TRUE if exchange was deferred because no path was available
|
||||||
|
*/
|
||||||
|
bool deferred;
|
||||||
|
|
||||||
} initiating;
|
} initiating;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -236,16 +241,12 @@ METHOD(task_manager_t, retransmit, status_t,
|
|||||||
if (task->get_type(task) == TASK_IKE_MOBIKE)
|
if (task->get_type(task) == TASK_IKE_MOBIKE)
|
||||||
{
|
{
|
||||||
mobike = (ike_mobike_t*)task;
|
mobike = (ike_mobike_t*)task;
|
||||||
if (!mobike->is_probing(mobike))
|
|
||||||
{
|
|
||||||
mobike = NULL;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
enumerator->destroy(enumerator);
|
enumerator->destroy(enumerator);
|
||||||
|
|
||||||
if (mobike == NULL)
|
if (!mobike || !mobike->is_probing(mobike))
|
||||||
{
|
{
|
||||||
if (this->initiating.retransmitted <= this->retransmit_tries)
|
if (this->initiating.retransmitted <= this->retransmit_tries)
|
||||||
{
|
{
|
||||||
@@ -268,8 +269,26 @@ METHOD(task_manager_t, retransmit, status_t,
|
|||||||
charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND,
|
charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND,
|
||||||
this->initiating.packet);
|
this->initiating.packet);
|
||||||
}
|
}
|
||||||
packet = this->initiating.packet->clone(this->initiating.packet);
|
if (!mobike)
|
||||||
charon->sender->send(charon->sender, packet);
|
{
|
||||||
|
packet = this->initiating.packet->clone(this->initiating.packet);
|
||||||
|
charon->sender->send(charon->sender, packet);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!mobike->transmit(mobike, this->initiating.packet))
|
||||||
|
{
|
||||||
|
DBG1(DBG_IKE, "no route found to reach peer, MOBIKE update "
|
||||||
|
"deferred");
|
||||||
|
this->ike_sa->set_condition(this->ike_sa, COND_STALE, TRUE);
|
||||||
|
this->initiating.deferred = TRUE;
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
else if (mobike->is_probing(mobike))
|
||||||
|
{
|
||||||
|
timeout = ROUTEABILITY_CHECK_INTERVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{ /* for routeability checks, we use a more aggressive behavior */
|
{ /* for routeability checks, we use a more aggressive behavior */
|
||||||
@@ -289,7 +308,14 @@ METHOD(task_manager_t, retransmit, status_t,
|
|||||||
DBG1(DBG_IKE, "path probing attempt %d",
|
DBG1(DBG_IKE, "path probing attempt %d",
|
||||||
this->initiating.retransmitted);
|
this->initiating.retransmitted);
|
||||||
}
|
}
|
||||||
mobike->transmit(mobike, this->initiating.packet);
|
if (!mobike->transmit(mobike, this->initiating.packet))
|
||||||
|
{
|
||||||
|
DBG1(DBG_IKE, "no route found to reach peer, path probing "
|
||||||
|
"deferred");
|
||||||
|
this->ike_sa->set_condition(this->ike_sa, COND_STALE, TRUE);
|
||||||
|
this->initiating.deferred = TRUE;
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->initiating.retransmitted++;
|
this->initiating.retransmitted++;
|
||||||
@@ -315,6 +341,12 @@ METHOD(task_manager_t, initiate, status_t,
|
|||||||
DBG2(DBG_IKE, "delaying task initiation, %N exchange in progress",
|
DBG2(DBG_IKE, "delaying task initiation, %N exchange in progress",
|
||||||
exchange_type_names, this->initiating.type);
|
exchange_type_names, this->initiating.type);
|
||||||
/* do not initiate if we already have a message in the air */
|
/* do not initiate if we already have a message in the air */
|
||||||
|
if (this->initiating.deferred)
|
||||||
|
{ /* re-initiate deferred exchange */
|
||||||
|
this->initiating.deferred = FALSE;
|
||||||
|
this->initiating.retransmitted = 0;
|
||||||
|
return retransmit(this, this->initiating.mid);
|
||||||
|
}
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,6 +490,7 @@ METHOD(task_manager_t, initiate, status_t,
|
|||||||
message->set_exchange_type(message, exchange);
|
message->set_exchange_type(message, exchange);
|
||||||
this->initiating.type = exchange;
|
this->initiating.type = exchange;
|
||||||
this->initiating.retransmitted = 0;
|
this->initiating.retransmitted = 0;
|
||||||
|
this->initiating.deferred = FALSE;
|
||||||
|
|
||||||
enumerator = array_create_enumerator(this->active_tasks);
|
enumerator = array_create_enumerator(this->active_tasks);
|
||||||
while (enumerator->enumerate(enumerator, &task))
|
while (enumerator->enumerate(enumerator, &task))
|
||||||
@@ -1246,6 +1279,8 @@ METHOD(task_manager_t, process_message, status_t,
|
|||||||
METHOD(task_manager_t, queue_task, void,
|
METHOD(task_manager_t, queue_task, void,
|
||||||
private_task_manager_t *this, task_t *task)
|
private_task_manager_t *this, task_t *task)
|
||||||
{
|
{
|
||||||
|
int pos = ARRAY_TAIL;
|
||||||
|
|
||||||
if (task->get_type(task) == TASK_IKE_MOBIKE)
|
if (task->get_type(task) == TASK_IKE_MOBIKE)
|
||||||
{ /* there is no need to queue more than one mobike task */
|
{ /* there is no need to queue more than one mobike task */
|
||||||
enumerator_t *enumerator;
|
enumerator_t *enumerator;
|
||||||
@@ -1262,9 +1297,12 @@ METHOD(task_manager_t, queue_task, void,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
enumerator->destroy(enumerator);
|
enumerator->destroy(enumerator);
|
||||||
|
/* insert MOBIKE tasks first as we currently might not have a usable
|
||||||
|
* path to initiate any other tasks */
|
||||||
|
pos = ARRAY_HEAD;
|
||||||
}
|
}
|
||||||
DBG2(DBG_IKE, "queueing %N task", task_type_names, task->get_type(task));
|
DBG2(DBG_IKE, "queueing %N task", task_type_names, task->get_type(task));
|
||||||
array_insert(this->queued_tasks, ARRAY_TAIL, task);
|
array_insert(this->queued_tasks, pos, task);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1368,7 +1406,25 @@ METHOD(task_manager_t, queue_mobike, void,
|
|||||||
mobike = ike_mobike_create(this->ike_sa, TRUE);
|
mobike = ike_mobike_create(this->ike_sa, TRUE);
|
||||||
if (roam)
|
if (roam)
|
||||||
{
|
{
|
||||||
|
enumerator_t *enumerator;
|
||||||
|
task_t *current;
|
||||||
|
|
||||||
mobike->roam(mobike, address);
|
mobike->roam(mobike, address);
|
||||||
|
|
||||||
|
/* enable path probing for a currently active MOBIKE task. This might
|
||||||
|
* not be the case if an address appeared on a new interface while the
|
||||||
|
* current address is not working but has not yet disappeared. */
|
||||||
|
enumerator = array_create_enumerator(this->active_tasks);
|
||||||
|
while (enumerator->enumerate(enumerator, ¤t))
|
||||||
|
{
|
||||||
|
if (current->get_type(current) == TASK_IKE_MOBIKE)
|
||||||
|
{
|
||||||
|
ike_mobike_t *active = (ike_mobike_t*)current;
|
||||||
|
active->enable_probing(active);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enumerator->destroy(enumerator);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2010-2012 Tobias Brunner
|
* Copyright (C) 2010-2014 Tobias Brunner
|
||||||
* Copyright (C) 2007 Martin Willi
|
* Copyright (C) 2007 Martin Willi
|
||||||
* Hochschule fuer Technik Rapperswil
|
* Hochschule fuer Technik Rapperswil
|
||||||
*
|
*
|
||||||
@@ -77,6 +77,11 @@ struct private_ike_mobike_t {
|
|||||||
* additional addresses got updated
|
* additional addresses got updated
|
||||||
*/
|
*/
|
||||||
bool addresses_updated;
|
bool addresses_updated;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* whether the pending updates counter was increased
|
||||||
|
*/
|
||||||
|
bool pending_update;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -301,35 +306,61 @@ static void apply_port(host_t *host, host_t *old, u_int16_t port, bool local)
|
|||||||
host->set_port(host, port);
|
host->set_port(host, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
METHOD(ike_mobike_t, transmit, void,
|
METHOD(ike_mobike_t, transmit, bool,
|
||||||
private_ike_mobike_t *this, packet_t *packet)
|
private_ike_mobike_t *this, packet_t *packet)
|
||||||
{
|
{
|
||||||
host_t *me, *other, *me_old, *other_old;
|
host_t *me, *other, *me_old, *other_old;
|
||||||
enumerator_t *enumerator;
|
enumerator_t *enumerator;
|
||||||
ike_cfg_t *ike_cfg;
|
ike_cfg_t *ike_cfg;
|
||||||
packet_t *copy;
|
packet_t *copy;
|
||||||
|
int family = AF_UNSPEC;
|
||||||
if (!this->check)
|
bool found = FALSE;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
me_old = this->ike_sa->get_my_host(this->ike_sa);
|
me_old = this->ike_sa->get_my_host(this->ike_sa);
|
||||||
other_old = this->ike_sa->get_other_host(this->ike_sa);
|
other_old = this->ike_sa->get_other_host(this->ike_sa);
|
||||||
ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
|
ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
|
||||||
|
|
||||||
|
if (!this->check)
|
||||||
|
{
|
||||||
|
me = hydra->kernel_interface->get_source_addr(hydra->kernel_interface,
|
||||||
|
other_old, me_old);
|
||||||
|
if (me)
|
||||||
|
{
|
||||||
|
if (me->ip_equals(me, me_old))
|
||||||
|
{
|
||||||
|
charon->sender->send(charon->sender, packet->clone(packet));
|
||||||
|
me->destroy(me);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
me->destroy(me);
|
||||||
|
}
|
||||||
|
this->check = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (charon->socket->supported_families(charon->socket))
|
||||||
|
{
|
||||||
|
case SOCKET_FAMILY_IPV4:
|
||||||
|
family = AF_INET;
|
||||||
|
break;
|
||||||
|
case SOCKET_FAMILY_IPV6:
|
||||||
|
family = AF_INET6;
|
||||||
|
break;
|
||||||
|
case SOCKET_FAMILY_BOTH:
|
||||||
|
case SOCKET_FAMILY_NONE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
enumerator = this->ike_sa->create_peer_address_enumerator(this->ike_sa);
|
enumerator = this->ike_sa->create_peer_address_enumerator(this->ike_sa);
|
||||||
while (enumerator->enumerate(enumerator, (void**)&other))
|
while (enumerator->enumerate(enumerator, (void**)&other))
|
||||||
{
|
{
|
||||||
|
if (family != AF_UNSPEC && other->get_family(other) != family)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
me = hydra->kernel_interface->get_source_addr(
|
me = hydra->kernel_interface->get_source_addr(
|
||||||
hydra->kernel_interface, other, NULL);
|
hydra->kernel_interface, other, NULL);
|
||||||
if (me)
|
if (me)
|
||||||
{
|
{
|
||||||
if (me->get_family(me) != other->get_family(other))
|
|
||||||
{
|
|
||||||
me->destroy(me);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/* reuse port for an active address, 4500 otherwise */
|
/* reuse port for an active address, 4500 otherwise */
|
||||||
apply_port(me, me_old, ike_cfg->get_my_port(ike_cfg), TRUE);
|
apply_port(me, me_old, ike_cfg->get_my_port(ike_cfg), TRUE);
|
||||||
other = other->clone(other);
|
other = other->clone(other);
|
||||||
@@ -339,9 +370,11 @@ METHOD(ike_mobike_t, transmit, void,
|
|||||||
copy->set_source(copy, me);
|
copy->set_source(copy, me);
|
||||||
copy->set_destination(copy, other);
|
copy->set_destination(copy, other);
|
||||||
charon->sender->send(charon->sender, copy);
|
charon->sender->send(charon->sender, copy);
|
||||||
|
found = TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
enumerator->destroy(enumerator);
|
enumerator->destroy(enumerator);
|
||||||
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
METHOD(task_t, build_i, status_t,
|
METHOD(task_t, build_i, status_t,
|
||||||
@@ -481,9 +514,7 @@ METHOD(task_t, process_i, status_t,
|
|||||||
}
|
}
|
||||||
else if (message->get_exchange_type(message) == INFORMATIONAL)
|
else if (message->get_exchange_type(message) == INFORMATIONAL)
|
||||||
{
|
{
|
||||||
u_int32_t updates = this->ike_sa->get_pending_updates(this->ike_sa) - 1;
|
if (this->ike_sa->get_pending_updates(this->ike_sa) > 1)
|
||||||
this->ike_sa->set_pending_updates(this->ike_sa, updates);
|
|
||||||
if (updates > 0)
|
|
||||||
{
|
{
|
||||||
/* newer update queued, ignore this one */
|
/* newer update queued, ignore this one */
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
@@ -560,7 +591,6 @@ METHOD(task_t, process_i, status_t,
|
|||||||
this->natd = ike_natd_create(this->ike_sa, this->initiator);
|
this->natd = ike_natd_create(this->ike_sa, this->initiator);
|
||||||
}
|
}
|
||||||
this->check = FALSE;
|
this->check = FALSE;
|
||||||
this->ike_sa->set_pending_updates(this->ike_sa, 1);
|
|
||||||
return NEED_MORE;
|
return NEED_MORE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -573,8 +603,12 @@ METHOD(ike_mobike_t, addresses, void,
|
|||||||
private_ike_mobike_t *this)
|
private_ike_mobike_t *this)
|
||||||
{
|
{
|
||||||
this->address = TRUE;
|
this->address = TRUE;
|
||||||
this->ike_sa->set_pending_updates(this->ike_sa,
|
if (!this->pending_update)
|
||||||
|
{
|
||||||
|
this->pending_update = TRUE;
|
||||||
|
this->ike_sa->set_pending_updates(this->ike_sa,
|
||||||
this->ike_sa->get_pending_updates(this->ike_sa) + 1);
|
this->ike_sa->get_pending_updates(this->ike_sa) + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
METHOD(ike_mobike_t, roam, void,
|
METHOD(ike_mobike_t, roam, void,
|
||||||
@@ -582,8 +616,12 @@ METHOD(ike_mobike_t, roam, void,
|
|||||||
{
|
{
|
||||||
this->check = TRUE;
|
this->check = TRUE;
|
||||||
this->address = address;
|
this->address = address;
|
||||||
this->ike_sa->set_pending_updates(this->ike_sa,
|
if (!this->pending_update)
|
||||||
|
{
|
||||||
|
this->pending_update = TRUE;
|
||||||
|
this->ike_sa->set_pending_updates(this->ike_sa,
|
||||||
this->ike_sa->get_pending_updates(this->ike_sa) + 1);
|
this->ike_sa->get_pending_updates(this->ike_sa) + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
METHOD(ike_mobike_t, dpd, void,
|
METHOD(ike_mobike_t, dpd, void,
|
||||||
@@ -593,8 +631,12 @@ METHOD(ike_mobike_t, dpd, void,
|
|||||||
{
|
{
|
||||||
this->natd = ike_natd_create(this->ike_sa, this->initiator);
|
this->natd = ike_natd_create(this->ike_sa, this->initiator);
|
||||||
}
|
}
|
||||||
this->ike_sa->set_pending_updates(this->ike_sa,
|
if (!this->pending_update)
|
||||||
|
{
|
||||||
|
this->pending_update = TRUE;
|
||||||
|
this->ike_sa->set_pending_updates(this->ike_sa,
|
||||||
this->ike_sa->get_pending_updates(this->ike_sa) + 1);
|
this->ike_sa->get_pending_updates(this->ike_sa) + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
METHOD(ike_mobike_t, is_probing, bool,
|
METHOD(ike_mobike_t, is_probing, bool,
|
||||||
@@ -603,6 +645,12 @@ METHOD(ike_mobike_t, is_probing, bool,
|
|||||||
return this->check;
|
return this->check;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
METHOD(ike_mobike_t, enable_probing, void,
|
||||||
|
private_ike_mobike_t *this)
|
||||||
|
{
|
||||||
|
this->check = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
METHOD(task_t, get_type, task_type_t,
|
METHOD(task_t, get_type, task_type_t,
|
||||||
private_ike_mobike_t *this)
|
private_ike_mobike_t *this)
|
||||||
{
|
{
|
||||||
@@ -618,11 +666,21 @@ METHOD(task_t, migrate, void,
|
|||||||
{
|
{
|
||||||
this->natd->task.migrate(&this->natd->task, ike_sa);
|
this->natd->task.migrate(&this->natd->task, ike_sa);
|
||||||
}
|
}
|
||||||
|
if (this->pending_update)
|
||||||
|
{
|
||||||
|
this->ike_sa->set_pending_updates(this->ike_sa,
|
||||||
|
this->ike_sa->get_pending_updates(this->ike_sa) + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
METHOD(task_t, destroy, void,
|
METHOD(task_t, destroy, void,
|
||||||
private_ike_mobike_t *this)
|
private_ike_mobike_t *this)
|
||||||
{
|
{
|
||||||
|
if (this->pending_update)
|
||||||
|
{
|
||||||
|
this->ike_sa->set_pending_updates(this->ike_sa,
|
||||||
|
this->ike_sa->get_pending_updates(this->ike_sa) - 1);
|
||||||
|
}
|
||||||
chunk_free(&this->cookie2);
|
chunk_free(&this->cookie2);
|
||||||
if (this->natd)
|
if (this->natd)
|
||||||
{
|
{
|
||||||
@@ -650,6 +708,7 @@ ike_mobike_t *ike_mobike_create(ike_sa_t *ike_sa, bool initiator)
|
|||||||
.dpd = _dpd,
|
.dpd = _dpd,
|
||||||
.transmit = _transmit,
|
.transmit = _transmit,
|
||||||
.is_probing = _is_probing,
|
.is_probing = _is_probing,
|
||||||
|
.enable_probing = _enable_probing,
|
||||||
},
|
},
|
||||||
.ike_sa = ike_sa,
|
.ike_sa = ike_sa,
|
||||||
.initiator = initiator,
|
.initiator = initiator,
|
||||||
|
|||||||
@@ -70,8 +70,9 @@ struct ike_mobike_t {
|
|||||||
* probing.
|
* probing.
|
||||||
*
|
*
|
||||||
* @param packet the packet to transmit
|
* @param packet the packet to transmit
|
||||||
|
* @return TRUE if transmitted, FALSE if no path found
|
||||||
*/
|
*/
|
||||||
void (*transmit)(ike_mobike_t *this, packet_t *packet);
|
bool (*transmit)(ike_mobike_t *this, packet_t *packet);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this task is probing for routability.
|
* Check if this task is probing for routability.
|
||||||
@@ -79,6 +80,11 @@ struct ike_mobike_t {
|
|||||||
* @return TRUE if task is probing
|
* @return TRUE if task is probing
|
||||||
*/
|
*/
|
||||||
bool (*is_probing)(ike_mobike_t *this);
|
bool (*is_probing)(ike_mobike_t *this);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable probing for routability.
|
||||||
|
*/
|
||||||
|
void (*enable_probing)(ike_mobike_t *this);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user