libimcv: Implementation of RFC 8412 SWIMA
[strongswan.git] / src / libimcv / ietf / swima / ietf_swima_attr_sw_ev.c
1 /*
2 * Copyright (C) 2017 Andreas Steffen
3 * HSR 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 "ietf_swima_attr_sw_ev.h"
17 #include "swima/swima_event.h"
18
19 #include <pa_tnc/pa_tnc_msg.h>
20 #include <bio/bio_writer.h>
21 #include <bio/bio_reader.h>
22 #include <utils/debug.h>
23
24 #define SW_EV_TIMESTAMP_SIZE 20
25
26 typedef struct private_ietf_swima_attr_sw_ev_t private_ietf_swima_attr_sw_ev_t;
27
28 /**
29 * Software [Identifier] Events
30 * see sections 5.9/5.11 of RFC 8412 SWIMA
31 *
32 * 1 2 3
33 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
34 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
35 * | Flags | Software Identifier Count |
36 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
37 * | Request ID Copy / Subscription ID |
38 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
39 * | EID Epoch |
40 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
41 * | Last EID |
42 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
43 * | Last Consulted EID |
44 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
45 * | EID |
46 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
47 * | Timestamp |
48 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
49 * | Timestamp |
50 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
51 * | Timestamp |
52 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
53 * | Timestamp |
54 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
55 * | Timestamp |
56 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
57 * | Record Identifier |
58 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
59 * | Data Model Type PEN |Data Model Type|
60 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
61 * | Source ID Num | Action | Software Identifier Length |
62 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
63 * | Software Identifier (Variable Length) |
64 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
65 * | Software Locator Length | Software Locator (Var. Len) |
66 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
67 *
68 * Software Event only
69 * see section 5.11 of IETF SW Inventory Message and Attributes for PA-TNC
70 *
71 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
72 * | Record Length |
73 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
74 * | Record (Variable length) |
75 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
76 */
77
78 /**
79 * Private data of an ietf_swima_attr_sw_ev_t object.
80 */
81 struct private_ietf_swima_attr_sw_ev_t {
82
83 /**
84 * Public members of ietf_swima_attr_sw_ev_t
85 */
86 ietf_swima_attr_sw_ev_t public;
87
88 /**
89 * Vendor-specific attribute type
90 */
91 pen_type_t type;
92
93 /**
94 * Length of attribute value
95 */
96 size_t length;
97
98 /**
99 * Offset up to which attribute value has been processed
100 */
101 size_t offset;
102
103 /**
104 * Current position of attribute value pointer
105 */
106 chunk_t value;
107
108 /**
109 * Contains complete attribute or current segment
110 */
111 chunk_t segment;
112
113 /**
114 * Noskip flag
115 */
116 bool noskip_flag;
117
118 /**
119 * Request ID
120 */
121 uint32_t request_id;
122
123 /**
124 * Attribute flags
125 */
126 uint8_t flags;
127
128 /**
129 * Number of unprocessed software events in attribute
130 */
131 uint32_t event_count;
132
133 /**
134 * Event list
135 */
136 swima_events_t *events;
137
138 /**
139 * Reference count
140 */
141 refcount_t ref;
142 };
143
144 METHOD(pa_tnc_attr_t, get_type, pen_type_t,
145 private_ietf_swima_attr_sw_ev_t *this)
146 {
147 return this->type;
148 }
149
150 METHOD(pa_tnc_attr_t, get_value, chunk_t,
151 private_ietf_swima_attr_sw_ev_t *this)
152 {
153 return this->value;
154 }
155
156 METHOD(pa_tnc_attr_t, get_noskip_flag, bool,
157 private_ietf_swima_attr_sw_ev_t *this)
158 {
159 return this->noskip_flag;
160 }
161
162 METHOD(pa_tnc_attr_t, set_noskip_flag,void,
163 private_ietf_swima_attr_sw_ev_t *this, bool noskip)
164 {
165 this->noskip_flag = noskip;
166 }
167
168 /**
169 * This function is shared with ietf_swima_attr_sw_inv.c
170 **/
171 void ietf_swima_attr_sw_ev_build_sw_record(bio_writer_t *writer,
172 uint8_t action, swima_record_t *sw_record, bool has_record)
173 {
174 pen_type_t data_model;
175 chunk_t sw_locator;
176
177 data_model = sw_record->get_data_model(sw_record);
178
179 writer->write_uint32(writer, sw_record->get_record_id(sw_record));
180 writer->write_uint24(writer, data_model.vendor_id);
181 writer->write_uint8 (writer, data_model.type);
182 writer->write_uint8 (writer, sw_record->get_source_id(sw_record));
183 writer->write_uint8 (writer, action);
184 writer->write_data16(writer, sw_record->get_sw_id(sw_record, &sw_locator));
185 writer->write_data16(writer, sw_locator);
186
187 if (has_record)
188 {
189 writer->write_data32(writer, sw_record->get_record(sw_record));
190 }
191 }
192
193 METHOD(pa_tnc_attr_t, build, void,
194 private_ietf_swima_attr_sw_ev_t *this)
195 {
196 bio_writer_t *writer;
197 swima_event_t *sw_event;
198 swima_record_t *sw_record;
199 chunk_t timestamp;
200 uint32_t last_eid, last_consulted_eid, eid_epoch;
201 uint8_t action;
202 enumerator_t *enumerator;
203
204 if (this->value.ptr)
205 {
206 return;
207 }
208 last_consulted_eid = this->events->get_eid(this->events, &eid_epoch,
209 &last_eid);
210
211 writer = bio_writer_create(IETF_SWIMA_SW_EV_MIN_SIZE);
212 writer->write_uint8 (writer, this->flags);
213 writer->write_uint24(writer, this->events->get_count(this->events));
214 writer->write_uint32(writer, this->request_id);
215 writer->write_uint32(writer, eid_epoch);
216 writer->write_uint32(writer, last_eid);
217 writer->write_uint32(writer, last_consulted_eid);
218
219 enumerator = this->events->create_enumerator(this->events);
220 while (enumerator->enumerate(enumerator, &sw_event))
221 {
222 action = sw_event->get_action(sw_event);
223 sw_record = sw_event->get_sw_record(sw_event);
224
225 writer->write_uint32(writer, sw_event->get_eid(sw_event, &timestamp));
226 writer->write_data (writer, timestamp);
227
228 ietf_swima_attr_sw_ev_build_sw_record(writer, action, sw_record,
229 this->type.type == IETF_ATTR_SW_EVENTS);
230 }
231 enumerator->destroy(enumerator);
232
233 this->value = writer->extract_buf(writer);
234 this->segment = this->value;
235 this->length = this->value.len;
236 writer->destroy(writer);
237 }
238
239 /**
240 * This function is shared with ietf_swima_attr_sw_inv.c
241 **/
242 bool ietf_swima_attr_sw_ev_process_sw_record(bio_reader_t *reader,
243 uint8_t *action, swima_record_t **sw_record, bool has_record)
244 {
245 pen_type_t data_model;
246 swima_record_t *sw_rec;
247 uint32_t data_model_pen, record_id;
248 uint8_t data_model_type, source_id, reserved;
249 chunk_t sw_id, sw_locator, record = chunk_empty;
250
251 if (!reader->read_uint32(reader, &record_id) ||
252 !reader->read_uint24(reader, &data_model_pen) ||
253 !reader->read_uint8 (reader, &data_model_type) ||
254 !reader->read_uint8 (reader, &source_id) ||
255 !reader->read_uint8 (reader, &reserved) ||
256 !reader->read_data16(reader, &sw_id) ||
257 !reader->read_data16(reader, &sw_locator))
258 {
259 return FALSE;
260 }
261
262 if (action)
263 {
264 *action = reserved;
265 }
266
267 if (has_record && !reader->read_data32(reader, &record))
268 {
269 return FALSE;
270 }
271
272 data_model = pen_type_create(data_model_pen, data_model_type);
273 sw_rec = swima_record_create(record_id, sw_id, sw_locator);
274 sw_rec->set_data_model(sw_rec, data_model);
275 sw_rec->set_source_id(sw_rec, source_id);
276 sw_rec->set_record(sw_rec, record);
277 *sw_record = sw_rec;
278
279 return TRUE;
280 }
281
282 METHOD(pa_tnc_attr_t, process, status_t,
283 private_ietf_swima_attr_sw_ev_t *this, uint32_t *offset)
284 {
285 bio_reader_t *reader;
286 uint32_t eid, eid_epoch, last_eid, last_consulted_eid;
287 uint8_t action;
288 chunk_t timestamp;
289 swima_event_t *sw_event;
290 swima_record_t *sw_record;
291 status_t status = NEED_MORE;
292
293 if (this->offset == 0)
294 {
295 if (this->length < IETF_SWIMA_SW_EV_MIN_SIZE)
296 {
297 DBG1(DBG_TNC, "insufficient data for %N/%N", pen_names, PEN_IETF,
298 ietf_attr_names, this->type.type);
299 *offset = this->offset;
300 return FAILED;
301 }
302 if (this->value.len < IETF_SWIMA_SW_EV_MIN_SIZE)
303 {
304 return NEED_MORE;
305 }
306 reader = bio_reader_create(this->value);
307 reader->read_uint8 (reader, &this->flags);
308 reader->read_uint24(reader, &this->event_count);
309 reader->read_uint32(reader, &this->request_id);
310 reader->read_uint32(reader, &eid_epoch);
311 reader->read_uint32(reader, &last_eid);
312 reader->read_uint32(reader, &last_consulted_eid);
313 this->offset = IETF_SWIMA_SW_EV_MIN_SIZE;
314 this->events->set_eid(this->events, last_consulted_eid, eid_epoch);
315 this->events->set_last_eid(this->events, last_eid);
316 this->value = reader->peek(reader);
317 reader->destroy(reader);
318 }
319
320 reader = bio_reader_create(this->value);
321
322 while (this->event_count)
323 {
324 if (!reader->read_uint32(reader, &eid) ||
325 !reader->read_data (reader, SW_EV_TIMESTAMP_SIZE, &timestamp) ||
326 !ietf_swima_attr_sw_ev_process_sw_record(reader, &action, &sw_record,
327 this->type.type == IETF_ATTR_SW_EVENTS))
328 {
329 goto end;
330 }
331
332 if (action == SWIMA_EVENT_ACTION_NONE ||
333 action > SWIMA_EVENT_ACTION_LAST)
334 {
335 DBG1(DBG_TNC, "invalid event action value for %N/%N", pen_names,
336 PEN_IETF, ietf_attr_names, this->type.type);
337 *offset = this->offset;
338 sw_record->destroy(sw_record);
339 reader->destroy(reader);
340
341 return FAILED;
342 }
343
344 sw_event = swima_event_create(eid, timestamp, action, sw_record);
345 this->events->add(this->events, sw_event);
346 this->offset += this->value.len - reader->remaining(reader);
347 this->value = reader->peek(reader);
348
349 /* at least one software event was processed */
350 status = SUCCESS;
351 this->event_count--;
352 }
353
354 if (this->length == this->offset)
355 {
356 status = SUCCESS;
357 }
358 else
359 {
360 DBG1(DBG_TNC, "inconsistent length for %N/%N", pen_names, PEN_IETF,
361 ietf_attr_names, this->type.type);
362 *offset = this->offset;
363 status = FAILED;
364 }
365
366 end:
367 reader->destroy(reader);
368 return status;
369 }
370
371 METHOD(pa_tnc_attr_t, add_segment, void,
372 private_ietf_swima_attr_sw_ev_t *this, chunk_t segment)
373 {
374 this->value = chunk_cat("cc", this->value, segment);
375 chunk_free(&this->segment);
376 this->segment = this->value;
377 }
378
379 METHOD(pa_tnc_attr_t, get_ref, pa_tnc_attr_t*,
380 private_ietf_swima_attr_sw_ev_t *this)
381 {
382 ref_get(&this->ref);
383 return &this->public.pa_tnc_attribute;
384 }
385
386 METHOD(pa_tnc_attr_t, destroy, void,
387 private_ietf_swima_attr_sw_ev_t *this)
388 {
389 if (ref_put(&this->ref))
390 {
391 this->events->destroy(this->events);
392 free(this->segment.ptr);
393 free(this);
394 }
395 }
396
397 METHOD(ietf_swima_attr_sw_ev_t, get_flags, uint8_t,
398 private_ietf_swima_attr_sw_ev_t *this)
399 {
400 return this->flags;
401 }
402
403 METHOD(ietf_swima_attr_sw_ev_t, get_request_id, uint32_t,
404 private_ietf_swima_attr_sw_ev_t *this)
405 {
406 return this->request_id;
407 }
408
409 METHOD(ietf_swima_attr_sw_ev_t, get_event_count, uint32_t,
410 private_ietf_swima_attr_sw_ev_t *this)
411 {
412 return this->event_count;
413 }
414
415 METHOD(ietf_swima_attr_sw_ev_t, set_events, void,
416 private_ietf_swima_attr_sw_ev_t *this, swima_events_t *events)
417 {
418 this->events->destroy(this->events);
419 this->events = events->get_ref(events);
420 }
421
422 METHOD(ietf_swima_attr_sw_ev_t, get_events, swima_events_t*,
423 private_ietf_swima_attr_sw_ev_t *this)
424 {
425 return this->events;
426 }
427
428 METHOD(ietf_swima_attr_sw_ev_t, clear_events, void,
429 private_ietf_swima_attr_sw_ev_t *this)
430 {
431 this->events->clear(this->events);
432 }
433
434 /**
435 * Described in header.
436 */
437 pa_tnc_attr_t *ietf_swima_attr_sw_ev_create(uint8_t flags, uint32_t request_id,
438 bool sw_id_only)
439 {
440 private_ietf_swima_attr_sw_ev_t *this;
441 ietf_attr_t type;
442
443 type = sw_id_only ? IETF_ATTR_SW_ID_EVENTS : IETF_ATTR_SW_EVENTS;
444
445 INIT(this,
446 .public = {
447 .pa_tnc_attribute = {
448 .get_type = _get_type,
449 .get_value = _get_value,
450 .get_noskip_flag = _get_noskip_flag,
451 .set_noskip_flag = _set_noskip_flag,
452 .build = _build,
453 .process = _process,
454 .add_segment = _add_segment,
455 .get_ref = _get_ref,
456 .destroy = _destroy,
457 },
458 .get_flags = _get_flags,
459 .get_request_id = _get_request_id,
460 .get_event_count = _get_event_count,
461 .set_events = _set_events,
462 .get_events = _get_events,
463 .clear_events = _clear_events,
464 },
465 .type = { PEN_IETF, type },
466 .flags = flags,
467 .request_id = request_id,
468 .events = swima_events_create(),
469 .ref = 1,
470 );
471
472 return &this->public.pa_tnc_attribute;
473 }
474
475
476 /**
477 * Described in header.
478 */
479 pa_tnc_attr_t *ietf_swima_attr_sw_ev_create_from_data(size_t length,
480 chunk_t data, bool sw_id_only)
481 {
482 private_ietf_swima_attr_sw_ev_t *this;
483 ietf_attr_t type;
484
485 type = sw_id_only ? IETF_ATTR_SW_ID_EVENTS : IETF_ATTR_SW_EVENTS;
486
487 INIT(this,
488 .public = {
489 .pa_tnc_attribute = {
490 .get_type = _get_type,
491 .get_value = _get_value,
492 .get_noskip_flag = _get_noskip_flag,
493 .set_noskip_flag = _set_noskip_flag,
494 .build = _build,
495 .process = _process,
496 .add_segment = _add_segment,
497 .get_ref = _get_ref,
498 .destroy = _destroy,
499 },
500 .get_flags = _get_flags,
501 .get_request_id = _get_request_id,
502 .get_event_count = _get_event_count,
503 .set_events = _set_events,
504 .get_events = _get_events,
505 .clear_events = _clear_events,
506 },
507 .type = { PEN_IETF, type },
508 .length = length,
509 .segment = chunk_clone(data),
510 .events = swima_events_create(),
511 .ref = 1,
512 );
513
514 /* received either complete attribute value or first segment */
515 this->value = this->segment;
516
517 return &this->public.pa_tnc_attribute;
518 }