Merge branch 'nm-gtk4'
[strongswan.git] / src / libcharon / plugins / dhcp / dhcp_provider.c
1 /*
2 * Copyright (C) 2010 Martin Willi
3 * Copyright (C) 2010 revosec AG
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 "dhcp_provider.h"
17
18 #include <collections/hashtable.h>
19 #include <threading/mutex.h>
20
21 typedef struct private_dhcp_provider_t private_dhcp_provider_t;
22
23 /**
24 * Private data of an dhcp_provider_t object.
25 */
26 struct private_dhcp_provider_t {
27
28 /**
29 * Public dhcp_provider_t interface.
30 */
31 dhcp_provider_t public;
32
33 /**
34 * Completed DHCP transactions
35 */
36 hashtable_t *transactions;
37
38 /**
39 * Lock for transactions
40 */
41 mutex_t *mutex;
42
43 /**
44 * DHCP communication socket
45 */
46 dhcp_socket_t *socket;
47 };
48
49 /**
50 * Hash ID and host to a key
51 */
52 static uintptr_t hash_id_host(identification_t *id, host_t *host)
53 {
54 return chunk_hash_inc(id->get_encoding(id),
55 chunk_hash(host->get_address(host)));
56 }
57
58 /**
59 * Hash a DHCP transaction to a key, using address and id
60 */
61 static uintptr_t hash_transaction(dhcp_transaction_t *transaction)
62 {
63 return hash_id_host(transaction->get_identity(transaction),
64 transaction->get_address(transaction));
65 }
66
67 METHOD(attribute_provider_t, acquire_address, host_t*,
68 private_dhcp_provider_t *this, linked_list_t *pools,
69 ike_sa_t *ike_sa, host_t *requested)
70 {
71 dhcp_transaction_t *transaction, *old;
72 enumerator_t *enumerator;
73 identification_t *id;
74 char *pool;
75 host_t *vip = NULL;
76
77 if (requested->get_family(requested) != AF_INET)
78 {
79 return NULL;
80 }
81 id = ike_sa->get_other_eap_id(ike_sa);
82 enumerator = pools->create_enumerator(pools);
83 while (enumerator->enumerate(enumerator, &pool))
84 {
85 if (!streq(pool, "dhcp"))
86 {
87 continue;
88 }
89 transaction = this->socket->enroll(this->socket, id);
90 if (!transaction)
91 {
92 continue;
93 }
94 vip = transaction->get_address(transaction);
95 vip = vip->clone(vip);
96 this->mutex->lock(this->mutex);
97 old = this->transactions->put(this->transactions,
98 (void*)hash_transaction(transaction), transaction);
99 this->mutex->unlock(this->mutex);
100 DESTROY_IF(old);
101 break;
102 }
103 enumerator->destroy(enumerator);
104 return vip;
105 }
106
107 METHOD(attribute_provider_t, release_address, bool,
108 private_dhcp_provider_t *this, linked_list_t *pools,
109 host_t *address, ike_sa_t *ike_sa)
110 {
111 dhcp_transaction_t *transaction;
112 enumerator_t *enumerator;
113 identification_t *id;
114 bool found = FALSE;
115 char *pool;
116
117 if (address->get_family(address) != AF_INET)
118 {
119 return FALSE;
120 }
121 id = ike_sa->get_other_eap_id(ike_sa);
122 enumerator = pools->create_enumerator(pools);
123 while (enumerator->enumerate(enumerator, &pool))
124 {
125 if (!streq(pool, "dhcp"))
126 {
127 continue;
128 }
129 this->mutex->lock(this->mutex);
130 transaction = this->transactions->remove(this->transactions,
131 (void*)hash_id_host(id, address));
132 this->mutex->unlock(this->mutex);
133 if (transaction)
134 {
135 this->socket->release(this->socket, transaction);
136 transaction->destroy(transaction);
137 found = TRUE;
138 break;
139 }
140 }
141 enumerator->destroy(enumerator);
142 return found;
143 }
144
145 METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*,
146 private_dhcp_provider_t *this, linked_list_t *pools, ike_sa_t *ike_sa,
147 linked_list_t *vips)
148 {
149 dhcp_transaction_t *transaction = NULL;
150 enumerator_t *enumerator;
151 identification_t *id;
152 host_t *vip;
153
154 if (!pools->find_first(pools, linked_list_match_str, NULL, "dhcp"))
155 {
156 return NULL;
157 }
158
159 id = ike_sa->get_other_eap_id(ike_sa);
160 this->mutex->lock(this->mutex);
161 enumerator = vips->create_enumerator(vips);
162 while (enumerator->enumerate(enumerator, &vip))
163 {
164 transaction = this->transactions->get(this->transactions,
165 (void*)hash_id_host(id, vip));
166 if (transaction)
167 {
168 break;
169 }
170 }
171 enumerator->destroy(enumerator);
172 if (!transaction)
173 {
174 this->mutex->unlock(this->mutex);
175 return NULL;
176 }
177 return enumerator_create_cleaner(
178 transaction->create_attribute_enumerator(transaction),
179 (void*)this->mutex->unlock, this->mutex);
180 }
181
182 METHOD(dhcp_provider_t, destroy, void,
183 private_dhcp_provider_t *this)
184 {
185 enumerator_t *enumerator;
186 dhcp_transaction_t *value;
187 void *key;
188
189 enumerator = this->transactions->create_enumerator(this->transactions);
190 while (enumerator->enumerate(enumerator, &key, &value))
191 {
192 value->destroy(value);
193 }
194 enumerator->destroy(enumerator);
195 this->transactions->destroy(this->transactions);
196 this->mutex->destroy(this->mutex);
197 free(this);
198 }
199
200 /**
201 * See header
202 */
203 dhcp_provider_t *dhcp_provider_create(dhcp_socket_t *socket)
204 {
205 private_dhcp_provider_t *this;
206
207 INIT(this,
208 .public = {
209 .provider = {
210 .acquire_address = _acquire_address,
211 .release_address = _release_address,
212 .create_attribute_enumerator = _create_attribute_enumerator,
213 },
214 .destroy = _destroy,
215 },
216 .socket = socket,
217 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
218 .transactions = hashtable_create(hashtable_hash_ptr,
219 hashtable_equals_ptr, 8),
220 );
221
222 return &this->public;
223 }