Make rescheduling a job more predictable
[strongswan.git] / src / libcharon / processing / jobs / inactivity_job.c
1 /*
2 * Copyright (C) 2010 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "inactivity_job.h"
17
18 #include <daemon.h>
19
20 typedef struct private_inactivity_job_t private_inactivity_job_t;
21
22 /**
23 * Private data of an inactivity_job_t object.
24 */
25 struct private_inactivity_job_t {
26
27 /**
28 * Public inactivity_job_t interface.
29 */
30 inactivity_job_t public;
31
32 /**
33 * Reqid of CHILD_SA to check
34 */
35 u_int32_t reqid;
36
37 /**
38 * Inactivity timeout
39 */
40 u_int32_t timeout;
41
42 /**
43 * Close IKE_SA if last remaining CHILD inactive?
44 */
45 bool close_ike;
46 };
47
48 METHOD(job_t, destroy, void,
49 private_inactivity_job_t *this)
50 {
51 free(this);
52 }
53
54 METHOD(job_t, execute, job_requeue_t,
55 private_inactivity_job_t *this)
56 {
57 ike_sa_t *ike_sa;
58 u_int32_t reschedule = 0;
59
60 ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
61 this->reqid, TRUE);
62 if (ike_sa)
63 {
64 enumerator_t *enumerator;
65 child_sa_t *child_sa;
66 u_int32_t delete = 0;
67 protocol_id_t proto = 0;
68 int children = 0;
69 status_t status = SUCCESS;
70
71 enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
72 while (enumerator->enumerate(enumerator, (void**)&child_sa))
73 {
74 if (child_sa->get_reqid(child_sa) == this->reqid)
75 {
76 time_t in, out, diff;
77
78 child_sa->get_usestats(child_sa, TRUE, &in, NULL);
79 child_sa->get_usestats(child_sa, FALSE, &out, NULL);
80
81 diff = time_monotonic(NULL) - max(in, out);
82
83 if (diff >= this->timeout)
84 {
85 delete = child_sa->get_spi(child_sa, TRUE);
86 proto = child_sa->get_protocol(child_sa);
87 }
88 else
89 {
90 reschedule = this->timeout - diff;
91 }
92 }
93 children++;
94 }
95 enumerator->destroy(enumerator);
96
97 if (delete)
98 {
99 if (children == 1 && this->close_ike)
100 {
101 DBG1(DBG_JOB, "deleting IKE_SA after %d seconds "
102 "of CHILD_SA inactivity", this->timeout);
103 status = ike_sa->delete(ike_sa);
104 }
105 else
106 {
107 DBG1(DBG_JOB, "deleting CHILD_SA after %d seconds "
108 "of inactivity", this->timeout);
109 status = ike_sa->delete_child_sa(ike_sa, proto, delete, FALSE);
110 }
111 }
112 if (status == DESTROY_ME)
113 {
114 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
115 ike_sa);
116 }
117 else
118 {
119 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
120 }
121 }
122 if (reschedule)
123 {
124 return JOB_RESCHEDULE(reschedule);
125 }
126 return JOB_REQUEUE_NONE;
127 }
128
129 METHOD(job_t, get_priority, job_priority_t,
130 private_inactivity_job_t *this)
131 {
132 return JOB_PRIO_MEDIUM;
133 }
134
135 /**
136 * See header
137 */
138 inactivity_job_t *inactivity_job_create(u_int32_t reqid, u_int32_t timeout,
139 bool close_ike)
140 {
141 private_inactivity_job_t *this;
142
143 INIT(this,
144 .public = {
145 .job_interface = {
146 .execute = _execute,
147 .get_priority = _get_priority,
148 .destroy = _destroy,
149 },
150 },
151 .reqid = reqid,
152 .timeout = timeout,
153 .close_ike = close_ike,
154 );
155
156 return &this->public;
157 }
158