2 * Copyright (C) 2007 Martin Willi
3 * Hochschule fuer Technik Rapperswil
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>.
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
18 #define DBUS_API_SUBJECT_TO_CHANGE
19 #include <dbus/dbus.h>
20 #include <NetworkManager/NetworkManager.h>
21 #include <NetworkManager/NetworkManagerVPN.h>
28 #include <processing/jobs/callback_job.h>
31 #define NM_DBUS_SERVICE_STRONG "org.freedesktop.NetworkManager.strongswan"
32 #define NM_dbus_STRONG "org.freedesktop.NetworkManager.strongswan"
33 #define NM_DBUS_PATH_STRONG "/org/freedesktop/NetworkManager/strongswan"
35 typedef struct private_dbus_t private_dbus_t
;
38 * Private data of an dbus_t object.
40 struct private_dbus_t
{
43 * Public part of dbus_t object.
53 * error value used here and there
63 * job accepting stroke messages
68 * name of the currently active connection
74 * set daemon state and send StateChange signal to the bus
76 static void set_state(private_dbus_t
*this, NMVPNState state
)
80 msg
= dbus_message_new_signal(NM_DBUS_PATH_STRONG
, NM_dbus_STRONG
, NM_DBUS_VPN_SIGNAL_STATE_CHANGE
);
82 if (!dbus_message_append_args(msg
, DBUS_TYPE_UINT32
, &this->state
,
83 DBUS_TYPE_UINT32
, &state
, DBUS_TYPE_INVALID
) ||
84 !dbus_connection_send(this->conn
, msg
, NULL
))
86 DBG1(DBG_CFG
, "unable to send DBUS StateChange signal");
88 dbus_connection_flush(this->conn
);
89 dbus_message_unref(msg
);
95 * get the child_cfg with the same name as the peer cfg
97 static child_cfg_t
* get_child_from_peer(peer_cfg_t
*peer_cfg
, char *name
)
99 child_cfg_t
*current
, *found
= NULL
;
100 iterator_t
*iterator
;
102 iterator
= peer_cfg
->create_child_cfg_iterator(peer_cfg
);
103 while (iterator
->iterate(iterator
, (void**)¤t
))
105 if (streq(current
->get_name(current
), name
))
108 found
->get_ref(found
);
112 iterator
->destroy(iterator
);
118 * process NetworkManagers startConnection method call
120 static bool start_connection(private_dbus_t
*this, DBusMessage
* msg
)
122 DBusMessage
*reply
, *signal
;
123 char *name
, *user
, **data
, **passwords
, **routes
;
124 int data_count
, passwords_count
, routes_count
;
125 u_int32_t me
, other
, p2p
, netmask
, mss
;
126 char *dev
, *domain
, *banner
;
127 const dbus_int32_t array
[] = {};
128 const dbus_int32_t
*varray
= array
;
129 peer_cfg_t
*peer_cfg
;
130 child_cfg_t
*child_cfg
;
131 status_t status
= FAILED
;
133 dbus_error_free(&this->err
);
135 if (!dbus_message_get_args(msg
, &this->err
,
136 DBUS_TYPE_STRING
, &name
, DBUS_TYPE_STRING
, &user
,
137 DBUS_TYPE_ARRAY
, DBUS_TYPE_STRING
, &passwords
, &passwords_count
,
138 DBUS_TYPE_ARRAY
, DBUS_TYPE_STRING
, &data
, &data_count
,
139 DBUS_TYPE_ARRAY
, DBUS_TYPE_STRING
, &routes
, &routes_count
,
144 set_state(this, NM_VPN_STATE_STARTING
);
146 peer_cfg
= charon
->backends
->get_peer_cfg_by_name(charon
->backends
, name
);
150 this->name
= strdup(peer_cfg
->get_name(peer_cfg
));
151 child_cfg
= get_child_from_peer(peer_cfg
, name
);
154 status
= charon
->controller
->initiate(charon
->controller
,
155 peer_cfg
, child_cfg
, controller_cb_empty
, NULL
);
159 peer_cfg
->destroy(peer_cfg
);
162 reply
= dbus_message_new_method_return(msg
);
163 dbus_connection_send(this->conn
, reply
, NULL
);
164 dbus_message_unref(reply
);
166 if (status
== SUCCESS
)
169 set_state(this, NM_VPN_STATE_STARTED
);
170 signal
= dbus_message_new_signal(NM_DBUS_PATH_STRONG
,
172 NM_DBUS_VPN_SIGNAL_IP4_CONFIG
);
173 me
= other
= p2p
= mss
= netmask
= 0;
174 dev
= domain
= banner
= "";
175 if (dbus_message_append_args(signal
,
176 DBUS_TYPE_UINT32
, &other
,
177 DBUS_TYPE_STRING
, &dev
,
178 DBUS_TYPE_UINT32
, &me
,
179 DBUS_TYPE_UINT32
, &p2p
,
180 DBUS_TYPE_UINT32
, &netmask
,
181 DBUS_TYPE_ARRAY
, DBUS_TYPE_UINT32
, &varray
, 0,
182 DBUS_TYPE_ARRAY
, DBUS_TYPE_UINT32
, &varray
, 0,
183 DBUS_TYPE_UINT32
, &mss
,
184 DBUS_TYPE_STRING
, &domain
,
185 DBUS_TYPE_STRING
, &banner
, DBUS_TYPE_INVALID
))
187 dbus_connection_send(this->conn
, signal
, NULL
);
189 dbus_message_unref(signal
);
193 set_state(this, NM_VPN_STATE_STOPPED
);
196 dbus_connection_flush(this->conn
);
201 * process NetworkManagers stopConnection method call
203 static bool stop_connection(private_dbus_t
*this, DBusMessage
* msg
)
206 iterator_t
*iterator
;
209 if (this->name
== NULL
)
214 dbus_error_free(&this->err
);
216 set_state(this, NM_VPN_STATE_STOPPING
);
218 iterator
= charon
->controller
->create_ike_sa_iterator(charon
->controller
);
219 while (iterator
->iterate(iterator
, (void**)&ike_sa
))
221 child_sa_t
*child_sa
;
222 iterator_t
*children
;
224 if (this->name
&& streq(this->name
, ike_sa
->get_name(ike_sa
)))
226 id
= ike_sa
->get_unique_id(ike_sa
);
227 iterator
->destroy(iterator
);
228 charon
->controller
->terminate_ike(charon
->controller
, id
, NULL
, NULL
);
229 set_state(this, NM_VPN_STATE_STOPPED
);
232 children
= ike_sa
->create_child_sa_iterator(ike_sa
);
233 while (children
->iterate(children
, (void**)&child_sa
))
235 if (this->name
&& streq(this->name
, child_sa
->get_name(child_sa
)))
237 id
= child_sa
->get_reqid(child_sa
);
238 children
->destroy(children
);
239 iterator
->destroy(iterator
);
240 charon
->controller
->terminate_child(charon
->controller
, id
, NULL
, NULL
);
241 set_state(this, NM_VPN_STATE_STOPPED
);
245 children
->destroy(children
);
247 iterator
->destroy(iterator
);
248 set_state(this, NM_VPN_STATE_STOPPED
);
253 * process NetworkManagers getState method call
255 static bool get_state(private_dbus_t
*this, DBusMessage
* msg
)
258 reply
= dbus_message_new_method_return(msg
);
259 if (!reply
|| !dbus_message_append_args(reply
,
260 DBUS_TYPE_UINT32
, &this->state
,
265 dbus_connection_send(this->conn
, reply
, NULL
);
270 * Handle incoming messages
272 static DBusHandlerResult
message_handler(DBusConnection
*con
, DBusMessage
*msg
,
273 private_dbus_t
*this)
277 if (dbus_message_is_method_call(msg
, NM_dbus_STRONG
,
280 handled
= start_connection(this, msg
);
282 else if (dbus_message_is_method_call(msg
, NM_dbus_STRONG
,
285 handled
= stop_connection(this, msg
);
287 else if (dbus_message_is_method_call(msg
, NM_dbus_STRONG
,
290 handled
= get_state(this, msg
);
294 DBG1(DBG_CFG
, "ignoring DBUS message %s.%s",
295 dbus_message_get_interface(msg
), dbus_message_get_member(msg
));
301 return DBUS_HANDLER_RESULT_HANDLED
;
303 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
307 * Handle received signals
309 static DBusHandlerResult signal_handler(DBusConnection *con, DBusMessage *msg,
310 private_dbus_t *this)
314 if (dbus_message_is_signal(msg, NM_dbus, "VPNConnectionStateChange"))
319 if (dbus_message_get_args(msg, &this->err, DBUS_TYPE_STRING, &name,
320 DBUS_TYPE_UINT32, &state, DBUS_TYPE_INVALID))
322 DBG1(DBG_CFG, "got state %d for %s", state, name);
328 DBG1(DBG_CFG, "ignoring DBUS signal %s.%s",
329 dbus_message_get_interface(msg), dbus_message_get_member(msg));
334 return DBUS_HANDLER_RESULT_HANDLED;
336 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
340 * dispatcher function processed by a seperate thread
342 static job_requeue_t
dispatch(private_dbus_t
*this)
344 if (dbus_connection_read_write_dispatch(this->conn
, -1))
346 return JOB_REQUEUE_DIRECT
;
348 return JOB_REQUEUE_NONE
;
352 * Implementation of interface_t.destroy.
354 static void destroy(private_dbus_t
*this)
356 this->job
->cancel(this->job
);
357 dbus_connection_close(this->conn
);
358 dbus_error_free(&this->err
);
365 * Described in header file
367 plugin_t
*plugin_create()
370 DBusObjectPathVTable v
= {NULL
, (void*)&message_handler
, NULL
, NULL
, NULL
, NULL
};
371 private_dbus_t
*this = malloc_thing(private_dbus_t
);
373 this->public.plugin
.destroy
= (void (*)(plugin_t
*))destroy
;
375 dbus_error_init(&this->err
);
376 this->conn
= dbus_bus_get(DBUS_BUS_SYSTEM
, &this->err
);
377 if (dbus_error_is_set(&this->err
))
379 DBG1(DBG_CFG
, "unable to open DBUS connection: %s", this->err
.message
);
380 charon
->kill(charon
, "DBUS initialization failed");
382 dbus_connection_set_exit_on_disconnect(this->conn
, FALSE
);
384 ret
= dbus_bus_request_name(this->conn
, NM_DBUS_SERVICE_STRONG
,
385 DBUS_NAME_FLAG_REPLACE_EXISTING
, &this->err
);
386 if (dbus_error_is_set(&this->err
))
388 DBG1(DBG_CFG
, "unable to set DBUS name: %s", this->err
.message
);
389 charon
->kill(charon
, "unable to set DBUS name");
391 if (ret
!= DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER
)
393 charon
->kill(charon
, "DBUS name already owned");
395 if (!dbus_connection_register_object_path(this->conn
, NM_DBUS_PATH_STRONG
, &v
, this))
397 charon
->kill(charon
, "unable to register DBUS message handler");
400 if (!dbus_connection_add_filter(this->conn, (void*)signal_handler, this, NULL))
402 charon->kill(charon, "unable to register DBUS signal handler");
405 dbus_bus_add_match(this->conn, "type='signal', "
406 "interface='" NM_dbus_VPN "',"
407 "path='" NM_DBUS_PATH_VPN "'", &this->err);
408 if (dbus_error_is_set (&this->err))
410 charon->kill(charon, "unable to add DBUS signal match");
414 this->state
= NM_VPN_STATE_INIT
;
415 set_state(this, NM_VPN_STATE_STOPPED
);
417 this->job
= callback_job_create((callback_job_cb_t
)dispatch
, this, NULL
, NULL
);
418 charon
->processor
->queue_job(charon
->processor
, (job_t
*)this->job
);
420 return &this->public.plugin
;