Merge branch 'vici-updown'

Documents the ike/child-updown events and adds a ike/child-rekey event
and a new listen() method in the Python VICI bindings to listen for
arbitrary events (similar to the listen_events() method in the Ruby
bindings).
This commit is contained in:
Tobias Brunner
2015-08-17 11:13:37 +02:00
5 changed files with 234 additions and 49 deletions
+59
View File
@@ -738,6 +738,65 @@ information during an active_list-authorities_ command.
} }
} }
### ike-updown ###
The _ike-updown_ event is issued when an IKE_SA is established or terminated.
{
up = <yes or no>
<IKE_SA config name> = {
<same data as in the list-sas event, but without child-sas section>
}
}
### ike-rekey ###
The _ike-rekey_ event is issued when an IKE_SA is rekeyed.
{
<IKE_SA config name> = {
old = {
<same data as in the list-sas event, but without child-sas section>
}
new = {
<same data as in the list-sas event, but without child-sas section>
}
}
}
### child-updown ###
The _child-updown_ event is issued when a CHILD_SA is established or terminated.
{
up = <yes or no>
<IKE_SA config name> = {
<same data as in the list-sas event, but with only the affected
CHILD_SA in the child-sas section>
}
}
### child-rekey ###
The _child-rekey_ event is issued when a CHILD_SA is rekeyed.
{
<IKE_SA config name> = {
<same data as in the list-sas event, but with the child-sas section
as follows>
child-sas = {
<child-sa-name> = {
old = {
<same data as in the list-sas event>
}
new = {
<same data as in the list-sas event>
}
}
}
}
}
# libvici C client library # # libvici C client library #
libvici is the reference implementation of a C client library implementing libvici is the reference implementation of a C client library implementing
@@ -1,4 +1,6 @@
Copyright (c) 2015 Björn Schuberg Copyright (c) 2015 Björn Schuberg
Copyright (c) 2015 Martin Willi
Copyright (c) 2015 Tobias Brunner
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
@@ -8,3 +8,6 @@ class SessionException(Exception):
class CommandException(Exception): class CommandException(Exception):
"""Command result exception.""" """Command result exception."""
class EventUnknownException(Exception):
"""Event unknown exception."""
@@ -1,7 +1,7 @@
import collections import collections
import socket import socket
from .exception import SessionException, CommandException from .exception import SessionException, CommandException, EventUnknownException
from .protocol import Transport, Packet, Message from .protocol import Transport, Packet, Message
@@ -197,6 +197,16 @@ class Session(object):
""" """
return self.handler.request("get-pools") return self.handler.request("get-pools")
def listen(self, event_types):
"""Register and listen for the given events.
:param event_types: event types to register
:type event_types: list
:return: generator for streamed event responses as (event_type, dict)
:rtype: generator
"""
return self.handler.listen(event_types)
class SessionHandler(object): class SessionHandler(object):
"""Handles client command execution requests over vici.""" """Handles client command execution requests over vici."""
@@ -215,6 +225,32 @@ class SessionHandler(object):
self.transport.send(packet) self.transport.send(packet)
return Packet.parse(self.transport.receive()) return Packet.parse(self.transport.receive())
def _register_unregister(self, event_type, register):
"""Register or unregister for the given event.
:param event_type: event to register
:type event_type: str
:param register: whether to register or unregister
:type register: bool
"""
if register:
packet = Packet.register_event(event_type)
else:
packet = Packet.unregister_event(event_type)
response = self._communicate(packet)
if response.response_type == Packet.EVENT_UNKNOWN:
raise EventUnknownException(
"Unknown event type '{event}'".format(event=event_type)
)
elif response.response_type != Packet.EVENT_CONFIRM:
raise SessionException(
"Unexpected response type {type}, "
"expected '{confirm}' (EVENT_CONFIRM)".format(
type=response.response_type,
confirm=Packet.EVENT_CONFIRM,
)
)
def request(self, command, message=None): def request(self, command, message=None):
"""Send request with an optional message. """Send request with an optional message.
@@ -265,57 +301,37 @@ class SessionHandler(object):
if message is not None: if message is not None:
message = Message.serialize(message) message = Message.serialize(message)
# subscribe to event stream self._register_unregister(event_stream_type, True);
packet = Packet.register_event(event_stream_type)
response = self._communicate(packet)
if response.response_type != Packet.EVENT_CONFIRM: try:
raise SessionException( packet = Packet.request(command, message)
"Unexpected response type {type}, " self.transport.send(packet)
"expected '{confirm}' (EVENT_CONFIRM)".format( exited = False
type=response.response_type, while True:
confirm=Packet.EVENT_CONFIRM, response = Packet.parse(self.transport.receive())
) if response.response_type == Packet.EVENT:
) if not exited:
try:
yield Message.deserialize(response.payload)
except GeneratorExit:
exited = True
pass
else:
break
# issue command, and read any event messages if response.response_type == Packet.CMD_RESPONSE:
packet = Packet.request(command, message) command_response = Message.deserialize(response.payload)
self.transport.send(packet)
exited = False
while True:
response = Packet.parse(self.transport.receive())
if response.response_type == Packet.EVENT:
if not exited:
try:
yield Message.deserialize(response.payload)
except GeneratorExit:
exited = True
pass
else: else:
break raise SessionException(
"Unexpected response type {type}, "
if response.response_type == Packet.CMD_RESPONSE: "expected '{response}' (CMD_RESPONSE)".format(
command_response = Message.deserialize(response.payload) type=response.response_type,
else: response=Packet.CMD_RESPONSE
raise SessionException( )
"Unexpected response type {type}, "
"expected '{response}' (CMD_RESPONSE)".format(
type=response.response_type,
response=Packet.CMD_RESPONSE
) )
)
# unsubscribe from event stream finally:
packet = Packet.unregister_event(event_stream_type) self._register_unregister(event_stream_type, False);
response = self._communicate(packet)
if response.response_type != Packet.EVENT_CONFIRM:
raise SessionException(
"Unexpected response type {type}, "
"expected '{confirm}' (EVENT_CONFIRM)".format(
type=response.response_type,
confirm=Packet.EVENT_CONFIRM,
)
)
# evaluate command result, if any # evaluate command result, if any
if "success" in command_response: if "success" in command_response:
@@ -325,3 +341,27 @@ class SessionHandler(object):
errmsg=command_response["errmsg"] errmsg=command_response["errmsg"]
) )
) )
def listen(self, event_types):
"""Register and listen for the given events.
:param event_types: event types to register
:type event_types: list
:return: generator for streamed event responses as (event_type, dict)
:rtype: generator
"""
for event_type in event_types:
self._register_unregister(event_type, True)
try:
while True:
response = Packet.parse(self.transport.receive())
if response.response_type == Packet.EVENT:
try:
yield response.event_type, Message.deserialize(response.payload)
except GeneratorExit:
break
finally:
for event_type in event_types:
self._register_unregister(event_type, False)
+83 -2
View File
@@ -1031,7 +1031,9 @@ static void manage_commands(private_vici_query_t *this, bool reg)
this->dispatcher->manage_event(this->dispatcher, "list-conn", reg); this->dispatcher->manage_event(this->dispatcher, "list-conn", reg);
this->dispatcher->manage_event(this->dispatcher, "list-cert", reg); this->dispatcher->manage_event(this->dispatcher, "list-cert", reg);
this->dispatcher->manage_event(this->dispatcher, "ike-updown", reg); this->dispatcher->manage_event(this->dispatcher, "ike-updown", reg);
this->dispatcher->manage_event(this->dispatcher, "ike-rekey", reg);
this->dispatcher->manage_event(this->dispatcher, "child-updown", reg); this->dispatcher->manage_event(this->dispatcher, "child-updown", reg);
this->dispatcher->manage_event(this->dispatcher, "child-rekey", reg);
manage_command(this, "list-sas", list_sas, reg); manage_command(this, "list-sas", list_sas, reg);
manage_command(this, "list-policies", list_policies, reg); manage_command(this, "list-policies", list_policies, reg);
manage_command(this, "list-conns", list_conns, reg); manage_command(this, "list-conns", list_conns, reg);
@@ -1054,10 +1056,14 @@ METHOD(listener_t, ike_updown, bool,
now = time_monotonic(NULL); now = time_monotonic(NULL);
b = vici_builder_create(); b = vici_builder_create();
if (up)
{
b->add_kv(b, "up", "yes");
}
b->begin_section(b, ike_sa->get_name(ike_sa)); b->begin_section(b, ike_sa->get_name(ike_sa));
list_ike(this, b, ike_sa, now); list_ike(this, b, ike_sa, now);
b->begin_section(b, "child-sas");
b->end_section(b);
b->end_section(b); b->end_section(b);
this->dispatcher->raise_event(this->dispatcher, this->dispatcher->raise_event(this->dispatcher,
@@ -1066,6 +1072,35 @@ METHOD(listener_t, ike_updown, bool,
return TRUE; return TRUE;
} }
METHOD(listener_t, ike_rekey, bool,
private_vici_query_t *this, ike_sa_t *old, ike_sa_t *new)
{
vici_builder_t *b;
time_t now;
if (!this->dispatcher->has_event_listeners(this->dispatcher, "ike-rekey"))
{
return TRUE;
}
now = time_monotonic(NULL);
b = vici_builder_create();
b->begin_section(b, old->get_name(old));
b->begin_section(b, "old");
list_ike(this, b, old, now);
b->end_section(b);
b->begin_section(b, "new");
list_ike(this, b, new, now);
b->end_section(b);
b->end_section(b);
this->dispatcher->raise_event(this->dispatcher,
"ike-rekey", 0, b->finalize(b));
return TRUE;
}
METHOD(listener_t, child_updown, bool, METHOD(listener_t, child_updown, bool,
private_vici_query_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa, bool up) private_vici_query_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa, bool up)
{ {
@@ -1080,6 +1115,11 @@ METHOD(listener_t, child_updown, bool,
now = time_monotonic(NULL); now = time_monotonic(NULL);
b = vici_builder_create(); b = vici_builder_create();
if (up)
{
b->add_kv(b, "up", "yes");
}
b->begin_section(b, ike_sa->get_name(ike_sa)); b->begin_section(b, ike_sa->get_name(ike_sa));
list_ike(this, b, ike_sa, now); list_ike(this, b, ike_sa, now);
b->begin_section(b, "child-sas"); b->begin_section(b, "child-sas");
@@ -1097,6 +1137,45 @@ METHOD(listener_t, child_updown, bool,
return TRUE; return TRUE;
} }
METHOD(listener_t, child_rekey, bool,
private_vici_query_t *this, ike_sa_t *ike_sa, child_sa_t *old,
child_sa_t *new)
{
vici_builder_t *b;
time_t now;
if (!this->dispatcher->has_event_listeners(this->dispatcher, "child-rekey"))
{
return TRUE;
}
now = time_monotonic(NULL);
b = vici_builder_create();
b->begin_section(b, ike_sa->get_name(ike_sa));
list_ike(this, b, ike_sa, now);
b->begin_section(b, "child-sas");
b->begin_section(b, old->get_name(old));
b->begin_section(b, "old");
list_child(this, b, old, now);
b->end_section(b);
b->begin_section(b, "new");
list_child(this, b, new, now);
b->end_section(b);
b->end_section(b);
b->end_section(b);
b->end_section(b);
this->dispatcher->raise_event(this->dispatcher,
"child-rekey", 0, b->finalize(b));
return TRUE;
}
METHOD(vici_query_t, destroy, void, METHOD(vici_query_t, destroy, void,
private_vici_query_t *this) private_vici_query_t *this)
{ {
@@ -1115,7 +1194,9 @@ vici_query_t *vici_query_create(vici_dispatcher_t *dispatcher)
.public = { .public = {
.listener = { .listener = {
.ike_updown = _ike_updown, .ike_updown = _ike_updown,
.ike_rekey = _ike_rekey,
.child_updown = _child_updown, .child_updown = _child_updown,
.child_rekey = _child_rekey,
}, },
.destroy = _destroy, .destroy = _destroy,
}, },