kernel-interface: Add interface name to local subnet enumerator
[strongswan.git] / src / libcharon / plugins / bypass_lan / bypass_lan_listener.c
1 /*
2 * Copyright (C) 2016 Tobias Brunner
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 "bypass_lan_listener.h"
17
18 #include <collections/hashtable.h>
19 #include <threading/mutex.h>
20 #include <processing/jobs/callback_job.h>
21
22 #include <daemon.h>
23
24 typedef struct private_bypass_lan_listener_t private_bypass_lan_listener_t;
25
26 /**
27 * Private data
28 */
29 struct private_bypass_lan_listener_t {
30
31 /**
32 * Public interface.
33 */
34 bypass_lan_listener_t public;
35
36 /**
37 * Currently installed bypass policies, bypass_policy_t*
38 */
39 hashtable_t *policies;
40
41 /**
42 * Mutex to access list of policies
43 */
44 mutex_t *mutex;
45 };
46
47 /**
48 * Data for bypass policies
49 */
50 typedef struct {
51 private_bypass_lan_listener_t *listener;
52 host_t *net;
53 uint8_t mask;
54 child_cfg_t *cfg;
55 } bypass_policy_t;
56
57 /**
58 * Destroy a bypass policy
59 */
60 static void bypass_policy_destroy(bypass_policy_t *this)
61 {
62 traffic_selector_t *ts;
63
64 if (this->cfg)
65 {
66 ts = traffic_selector_create_from_subnet(this->net->clone(this->net),
67 this->mask, 0, 0, 65535);
68 DBG1(DBG_IKE, "uninstalling bypass policy for %R", ts);
69 charon->shunts->uninstall(charon->shunts,
70 this->cfg->get_name(this->cfg));
71 this->cfg->destroy(this->cfg);
72 ts->destroy(ts);
73 }
74 this->net->destroy(this->net);
75 free(this);
76 }
77
78 /**
79 * Hash a bypass policy
80 */
81 static u_int policy_hash(bypass_policy_t *policy)
82 {
83 return chunk_hash_inc(policy->net->get_address(policy->net),
84 chunk_hash(chunk_from_thing(policy->mask)));
85 }
86
87 /**
88 * Compare bypass policy
89 */
90 static bool policy_equals(bypass_policy_t *a, bypass_policy_t *b)
91 {
92 return a->mask == b->mask && a->net->equals(a->net, b->net);
93 }
94
95 /**
96 * Job updating bypass policies
97 */
98 static job_requeue_t update_bypass(private_bypass_lan_listener_t *this)
99 {
100 enumerator_t *enumerator;
101 hashtable_t *seen;
102 bypass_policy_t *found, *lookup;
103 host_t *net;
104 uint8_t mask;
105 char *iface;
106
107 seen = hashtable_create((hashtable_hash_t)policy_hash,
108 (hashtable_equals_t)policy_equals, 4);
109
110 this->mutex->lock(this->mutex);
111
112 enumerator = charon->kernel->create_local_subnet_enumerator(charon->kernel);
113 while (enumerator->enumerate(enumerator, &net, &mask, &iface))
114 {
115 INIT(lookup,
116 .net = net->clone(net),
117 .mask = mask,
118 );
119 seen->put(seen, lookup, lookup);
120
121 found = this->policies->get(this->policies, lookup);
122 if (!found)
123 {
124 child_cfg_create_t child = {
125 .mode = MODE_PASS,
126 };
127 child_cfg_t *cfg;
128 traffic_selector_t *ts;
129 char name[128];
130
131 ts = traffic_selector_create_from_subnet(net->clone(net), mask,
132 0, 0, 65535);
133 snprintf(name, sizeof(name), "Bypass LAN %R", ts);
134
135 cfg = child_cfg_create(name, &child);
136 cfg->add_traffic_selector(cfg, FALSE, ts->clone(ts));
137 cfg->add_traffic_selector(cfg, TRUE, ts);
138 charon->shunts->install(charon->shunts, cfg);
139 DBG1(DBG_IKE, "installed bypass policy for %R", ts);
140
141 INIT(found,
142 .net = net->clone(net),
143 .mask = mask,
144 .cfg = cfg,
145 );
146 this->policies->put(this->policies, found, found);
147 }
148 }
149 enumerator->destroy(enumerator);
150
151 enumerator = this->policies->create_enumerator(this->policies);
152 while (enumerator->enumerate(enumerator, NULL, &lookup))
153 {
154 if (!seen->get(seen, lookup))
155 {
156 this->policies->remove_at(this->policies, enumerator);
157 bypass_policy_destroy(lookup);
158 }
159 }
160 enumerator->destroy(enumerator);
161 this->mutex->unlock(this->mutex);
162
163 seen->destroy_function(seen, (void*)bypass_policy_destroy);
164 return JOB_REQUEUE_NONE;
165 }
166
167 METHOD(kernel_listener_t, roam, bool,
168 private_bypass_lan_listener_t *this, bool address)
169 {
170 lib->processor->queue_job(lib->processor,
171 (job_t*)callback_job_create((callback_job_cb_t)update_bypass, this,
172 NULL, (callback_job_cancel_t)return_false));
173 return TRUE;
174 }
175
176 METHOD(bypass_lan_listener_t, destroy, void,
177 private_bypass_lan_listener_t *this)
178 {
179 enumerator_t *enumerator;
180 bypass_policy_t *policy;
181
182 enumerator = this->policies->create_enumerator(this->policies);
183 while (enumerator->enumerate(enumerator, NULL, &policy))
184 {
185 bypass_policy_destroy(policy);
186 }
187 enumerator->destroy(enumerator);
188 this->policies->destroy(this->policies);
189 this->mutex->destroy(this->mutex);
190 free(this);
191 }
192
193 /*
194 * See header
195 */
196 bypass_lan_listener_t *bypass_lan_listener_create()
197 {
198 private_bypass_lan_listener_t *this;
199
200 INIT(this,
201 .public = {
202 .listener = {
203 .roam = _roam,
204 },
205 .destroy = _destroy,
206 },
207 .policies = hashtable_create((hashtable_hash_t)policy_hash,
208 (hashtable_equals_t)policy_equals, 4),
209 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
210 );
211
212 /* FIXME: schedule this? */
213 lib->processor->queue_job(lib->processor,
214 (job_t*)callback_job_create((callback_job_cb_t)update_bypass, this,
215 NULL, (callback_job_cancel_t)return_false));
216 return &this->public;
217 }