File: | programs/pluto/ikev2_liveness.c |
Warning: | line 69, column 2 Value stored to 'delay' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* IKEv2 LIVENESS probe |
2 | * |
3 | * Copyright (C) 1997 Angelos D. Keromytis. |
4 | * Copyright (C) 1998-2001 D. Hugh Redelmeier. |
5 | * Copyright (C) 2005-2008 Michael Richardson <mcr@xelerance.com> |
6 | * Copyright (C) 2008-2010 Paul Wouters <paul@xelerance.com> |
7 | * Copyright (C) 2009 David McCullough <david_mccullough@securecomputing.com> |
8 | * Copyright (C) 2012 Avesh Agarwal <avagarwa@redhat.com> |
9 | * Copyright (C) 2012-2019 Paul Wouters <pwouters@redhat.com> |
10 | * Copyright (C) 2013 Matt Rogers <mrogers@redhat.com> |
11 | * Copyright (C) 2017 Antony Antony <antony@phenome.org> |
12 | * Copyright (C) 2017-2019 Andrew Cagney <cagney@gnu.org> |
13 | * |
14 | * This program is free software; you can redistribute it and/or modify it |
15 | * under the terms of the GNU General Public License as published by the |
16 | * Free Software Foundation; either version 2 of the License, or (at your |
17 | * option) any later version. See <https://www.gnu.org/licenses/gpl2.txt>. |
18 | * |
19 | * This program is distributed in the hope that it will be useful, but |
20 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
21 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
22 | * for more details. |
23 | */ |
24 | |
25 | #include "defs.h" |
26 | #include "state.h" |
27 | #include "log.h" |
28 | #include "connections.h" |
29 | #include "iface.h" |
30 | #include "kernel.h" |
31 | #include "ikev2_send.h" |
32 | #include "pluto_stats.h" |
33 | #include "timer.h" |
34 | #include "server.h" |
35 | #include "ikev2.h" /* for struct state_v2_microcode */ |
36 | #include "ikev2_liveness.h" |
37 | #include "state_db.h" /* for state_by_serialno() */ |
38 | #include "ikev2_states.h" |
39 | |
40 | static stf_status send_v2_liveness_request(struct ike_sa *ike, |
41 | struct child_sa *child UNUSED__attribute__ ((unused)), |
42 | struct msg_digest *md UNUSED__attribute__ ((unused))) |
43 | { |
44 | /* |
45 | * XXX: What does it mean to send a liveness probe for a CHILD |
46 | * SA? Since the packet contents are empty there's nothing |
47 | * for the other end to identify which child this is for! |
48 | * |
49 | * XXX: See record 'n'_send for how screwed up all this is: |
50 | * need to pass in the CHILD SA so that it's liveness |
51 | * timestamp (and not the IKE) gets updated. |
52 | */ |
53 | pstats_ike_dpd_sent++; |
54 | stf_status e = record_v2_informational_request("liveness probe informational request", |
55 | ike, &ike->sa/*sender*/, |
56 | NULL((void*)0)/*no payloads to emit*/); |
57 | if (e != STF_OK) { |
58 | return STF_INTERNAL_ERROR; |
59 | } |
60 | return STF_OK; |
61 | } |
62 | |
63 | static void schedule_liveness(struct child_sa *child, deltatime_t time_since_last_contact, |
64 | const char *reason) |
65 | { |
66 | struct connection *c = child->sa.st_connection; |
67 | deltatime_t delay = c->dpd_delay; |
68 | /* reduce wait if contact was by some other means */ |
69 | delay = deltatime_sub(delay, time_since_last_contact); |
Value stored to 'delay' is never read | |
70 | /* in case above screws up? */ |
71 | delay = deltatime_max(c->dpd_delay, deltatime(MIN_LIVENESS1)); |
72 | LSWDBGP(DBG_BASE, buf)for (_Bool lswlog_p = (cur_debugging & (((lset_t)1 << (DBG_BASE_IX)))); lswlog_p; lswlog_p = 0) for (char lswbuf[( (size_t)1024)], *lswbuf_ = lswbuf; lswbuf_ != ((void*)0); lswbuf_ = ((void*)0)) for (struct jambuf jambuf = array_as_jambuf((lswbuf ), sizeof(lswbuf)), *buf = &jambuf; buf != ((void*)0); buf = ((void*)0)) for (; buf != ((void*)0); jambuf_to_debug_stream (buf), buf = ((void*)0)) { |
73 | deltatime_buf db; |
74 | endpoint_buf remote_buf; |
75 | jam(buf, "liveness: #%lu scheduling next check for %s in %s seconds", |
76 | child->sa.st_serialno, |
77 | str_endpoint(&child->sa.st_remote_endpoint, &remote_buf), |
78 | str_deltatime(delay, &db)); |
79 | if (deltatime_cmp(time_since_last_contact, !=, deltatime(0))(deltatime_cmp_sign(time_since_last_contact, deltatime(0)) != 0)) { |
80 | deltatime_buf lcb; |
81 | jam(buf, " (%s was %s seconds ago)", |
82 | reason, str_deltatime(time_since_last_contact, &lcb)); |
83 | } else { |
84 | jam(buf, " (%s)", reason); |
85 | } |
86 | } |
87 | event_schedule(EVENT_v2_LIVENESS, delay, &child->sa); |
88 | } |
89 | |
90 | /* note: this mutates *st by calling get_sa_info */ |
91 | void liveness_check(struct state *st) |
92 | { |
93 | passert(st->st_ike_version == IKEv2){ _Bool assertion__ = st->st_ike_version == IKEv2; if (!assertion__ ) { lsw_passert_fail((where_t) { .func = __func__, .basename = "ikev2_liveness.c" , .line = 93}, "%s", "st->st_ike_version == IKEv2" ); } }; |
94 | struct state *pst = state_by_serialno(st->st_clonedfrom); |
95 | if (pst == NULL((void*)0)) { |
96 | /* |
97 | * When the retransmits timeout the IKE SA gets |
98 | * deleted, but not the child. |
99 | * |
100 | * XXX: might need to tone this down. |
101 | */ |
102 | dbg("liveness: state #%lu has no IKE SA; deleting orphaned child",{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("liveness: state #%lu has no IKE SA; deleting orphaned child" , st->st_serialno); } } |
103 | st->st_serialno){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("liveness: state #%lu has no IKE SA; deleting orphaned child" , st->st_serialno); } }; |
104 | event_delete(EVENT_SO_DISCARD, st); |
105 | event_schedule(EVENT_SO_DISCARD, deltatime(0), st); |
106 | return; |
107 | } |
108 | struct ike_sa *ike = pexpect_ike_sa(pst); |
109 | |
110 | struct child_sa *child = pexpect_child_sa(st); |
111 | if (child == NULL((void*)0)) { |
112 | return; |
113 | } |
114 | struct connection *c = child->sa.st_connection; |
115 | struct v2_msgid_window *our = &ike->sa.st_v2_msgid_windows.initiator; |
116 | /* if nothing else this is when the state was created */ |
117 | pexpect(!is_monotime_epoch(our->last_contact))({ _Bool assertion__ = !is_monotime_epoch(our->last_contact ); if (!assertion__) { log_pexpect((where_t) { .func = __func__ , .basename = "ikev2_liveness.c" , .line = 117}, "%s", "!is_monotime_epoch(our->last_contact)" ); } assertion__; }); |
118 | monotime_t now = mononow(); |
119 | |
120 | /* |
121 | * If the child is lingering (replaced but not yet deleted), |
122 | * don't do liveness. |
123 | */ |
124 | if (c->newest_ipsec_sa != child->sa.st_serialno) { |
125 | dbg("liveness: #%lu was replaced by #%lu so not needed",{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("liveness: #%lu was replaced by #%lu so not needed" , child->sa.st_serialno, c->newest_ipsec_sa); } } |
126 | child->sa.st_serialno, c->newest_ipsec_sa){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("liveness: #%lu was replaced by #%lu so not needed" , child->sa.st_serialno, c->newest_ipsec_sa); } }; |
127 | return; |
128 | } |
129 | |
130 | /* |
131 | * If there's been traffic flowing through the CHILD SA and it |
132 | * was less than .dpd_delay ago then re-schedule the probe. |
133 | * |
134 | * XXX: is this useful? Liveness should be checking |
135 | * round-trip but this is just looking at incoming data - |
136 | * outgoing data could lost and this traffic is all |
137 | * re-transmit requests ... |
138 | */ |
139 | deltatime_t time_since_last_message; |
140 | if (get_sa_info(&child->sa, true1, &time_since_last_message) && |
141 | /* time_since_last_message < .dpd_delay */ |
142 | deltatime_cmp(time_since_last_message, <, c->dpd_delay)(deltatime_cmp_sign(time_since_last_message, c->dpd_delay) < 0)) { |
143 | /* |
144 | * Update .st_liveness_last, with the time of this |
145 | * traffic (unless other traffic is more recent). |
146 | */ |
147 | monotime_t last_contact = monotime_sub(now, time_since_last_message); |
148 | if (monobefore(our->last_contact, last_contact)) { |
149 | monotime_buf m0, m1; |
150 | dbg("liveness: #%lu updating #%lu last contact from %s to %s (last IPsec traffic flow)",{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("liveness: #%lu updating #%lu last contact from %s to %s (last IPsec traffic flow)" , child->sa.st_serialno, ike->sa.st_serialno, str_monotime (our->last_contact, &m0), str_monotime(last_contact, & m1)); } } |
151 | child->sa.st_serialno, ike->sa.st_serialno,{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("liveness: #%lu updating #%lu last contact from %s to %s (last IPsec traffic flow)" , child->sa.st_serialno, ike->sa.st_serialno, str_monotime (our->last_contact, &m0), str_monotime(last_contact, & m1)); } } |
152 | str_monotime(our->last_contact, &m0),{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("liveness: #%lu updating #%lu last contact from %s to %s (last IPsec traffic flow)" , child->sa.st_serialno, ike->sa.st_serialno, str_monotime (our->last_contact, &m0), str_monotime(last_contact, & m1)); } } |
153 | str_monotime(last_contact, &m1)){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("liveness: #%lu updating #%lu last contact from %s to %s (last IPsec traffic flow)" , child->sa.st_serialno, ike->sa.st_serialno, str_monotime (our->last_contact, &m0), str_monotime(last_contact, & m1)); } }; |
154 | our->last_contact = last_contact; |
155 | } |
156 | /* |
157 | * schedule in .dpd_delay seconds, but adjust for: |
158 | * time since last traffic, and min liveness vis |
159 | * |
160 | * max(dpd_delay - time_since_last_message, * deltatime(MIN_LIVENESS)) |
161 | */ |
162 | schedule_liveness(child, time_since_last_message, "recent IPsec traffic"); |
163 | return; |
164 | } |
165 | |
166 | /* |
167 | * If there's already a message request outstanding assume it |
168 | * will succeed - if it doesn't the entire family will be |
169 | * killed. |
170 | * |
171 | * No probe is needed for another .dpd_delay seconds. |
172 | */ |
173 | if (v2_msgid_request_outstanding(ike)) { |
174 | schedule_liveness(child, deltatime(0), "request outstanding"); |
175 | return; |
176 | } |
177 | |
178 | /* |
179 | * If there's an exchange pending; assume it will succeed (for |
180 | * instance last exchange just finished, next exchange about |
181 | * to start), reschedule the probe. |
182 | */ |
183 | if (v2_msgid_request_pending(ike)) { |
184 | schedule_liveness(child, deltatime(0), "request pending"); |
185 | return; |
186 | } |
187 | |
188 | /* |
189 | * If was a successful exchange less than .dpd_delay ago, |
190 | * reschedule the probe. |
191 | */ |
192 | deltatime_t time_since_last_contact = monotimediff(now, our->last_contact); |
193 | if (deltatime_cmp(time_since_last_contact, <, c->dpd_delay)(deltatime_cmp_sign(time_since_last_contact, c->dpd_delay) < 0)) { |
194 | schedule_liveness(child, time_since_last_contact, "successful exchange"); |
195 | return; |
196 | } |
197 | |
198 | endpoint_buf remote_buf; |
199 | struct state *handler = &ike->sa; |
200 | dbg("liveness: #%lu queueing liveness probe for %s using #%lu",{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("liveness: #%lu queueing liveness probe for %s using #%lu" , child->sa.st_serialno, str_endpoint(&child->sa.st_remote_endpoint , &remote_buf), handler->st_serialno); } } |
201 | child->sa.st_serialno,{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("liveness: #%lu queueing liveness probe for %s using #%lu" , child->sa.st_serialno, str_endpoint(&child->sa.st_remote_endpoint , &remote_buf), handler->st_serialno); } } |
202 | str_endpoint(&child->sa.st_remote_endpoint, &remote_buf),{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("liveness: #%lu queueing liveness probe for %s using #%lu" , child->sa.st_serialno, str_endpoint(&child->sa.st_remote_endpoint , &remote_buf), handler->st_serialno); } } |
203 | handler->st_serialno){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("liveness: #%lu queueing liveness probe for %s using #%lu" , child->sa.st_serialno, str_endpoint(&child->sa.st_remote_endpoint , &remote_buf), handler->st_serialno); } }; |
204 | initiate_v2_liveness(child->sa.st_logger, ike); |
205 | |
206 | /* in case above screws up? */ |
207 | schedule_liveness(child, deltatime(0), "backup for liveness probe"); |
208 | } |
209 | |
210 | /* |
211 | * XXX: where to put this? |
212 | */ |
213 | |
214 | static const struct state_v2_microcode v2_liveness_probe = { |
215 | .story = "liveness probe", |
216 | .state = STATE_V2_ESTABLISHED_IKE_SA, |
217 | .next_state = STATE_V2_ESTABLISHED_IKE_SA, |
218 | .send = MESSAGE_REQUEST, |
219 | .processor = send_v2_liveness_request, |
220 | .timeout_event = EVENT_RETAIN, |
221 | .flags = SMF2_SUPPRESS_SUCCESS_LOG, |
222 | }; |
223 | |
224 | void initiate_v2_liveness(struct logger *logger, struct ike_sa *ike) |
225 | { |
226 | const struct state_v2_microcode *transition = &v2_liveness_probe; |
227 | if (ike->sa.st_state->kind != transition->state) { |
228 | log_message(RC_LOG, logger, |
229 | "liveness: #%lu unexpectedly in state %s; should be %s", |
230 | ike->sa.st_serialno, ike->sa.st_state->short_name, |
231 | finite_states[transition->state]->short_name); |
232 | return; |
233 | } |
234 | |
235 | v2_msgid_queue_initiator(ike, &ike->sa, ISAKMP_v2_INFORMATIONAL, |
236 | transition, NULL((void*)0)); |
237 | } |