86f81fcfb7ec6aa037b5816a672b58a72671bf15
[strongswan.git] / src / libcharon / plugins / unity / unity_provider.c
1 /*
2 * Copyright (C) 2013 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
4 *
5 * Copyright (C) 2012 Martin Willi
6 * Copyright (C) 2012 revosec AG
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * for more details.
17 */
18
19 #include "unity_provider.h"
20
21 #include <daemon.h>
22 #include <bio/bio_writer.h>
23
24 typedef struct private_unity_provider_t private_unity_provider_t;
25
26 /**
27 * Private data of an unity_provider_t object.
28 */
29 struct private_unity_provider_t {
30
31 /**
32 * Public unity_provider_t interface.
33 */
34 unity_provider_t public;
35 };
36
37 /**
38 * Attribute enumerator for UNITY_SPLIT_INCLUDE attribute
39 */
40 typedef struct {
41 /** Implements enumerator_t */
42 enumerator_t public;
43 /** list of traffic selectors to enumerate */
44 linked_list_t *list;
45 /** attribute value */
46 chunk_t attr;
47 } attribute_enumerator_t;
48
49 /**
50 * Append data from the given traffic selector to the attribute data
51 */
52 static void append_ts(bio_writer_t *writer, traffic_selector_t *ts)
53 {
54 host_t *net, *mask;
55 chunk_t padding;
56 u_int8_t bits;
57
58 if (!ts->to_subnet(ts, &net, &bits))
59 {
60 return;
61 }
62 mask = host_create_netmask(AF_INET, bits);
63 if (!mask)
64 {
65 net->destroy(net);
66 return;
67 }
68 writer->write_data(writer, net->get_address(net));
69 writer->write_data(writer, mask->get_address(mask));
70 /* the Cisco client parses the "padding" as protocol, src and dst port, the
71 * first two in network order the last in host order - no other clients seem
72 * to support these fields so we don't use them either */
73 padding = writer->skip(writer, 6);
74 memset(padding.ptr, 0, padding.len);
75 mask->destroy(mask);
76 net->destroy(net);
77 }
78
79 METHOD(enumerator_t, attribute_enumerate, bool,
80 attribute_enumerator_t *this, configuration_attribute_type_t *type,
81 chunk_t *attr)
82 {
83 traffic_selector_t *ts;
84 bio_writer_t *writer;
85
86 if (this->list->get_count(this->list) == 0)
87 {
88 return FALSE;
89 }
90
91 writer = bio_writer_create(14);
92 while (this->list->remove_first(this->list, (void**)&ts) == SUCCESS)
93 {
94 append_ts(writer, ts);
95 ts->destroy(ts);
96 }
97
98 *type = UNITY_SPLIT_INCLUDE;
99 *attr = this->attr = writer->extract_buf(writer);
100
101 writer->destroy(writer);
102 return TRUE;
103 }
104
105 METHOD(enumerator_t, attribute_destroy, void,
106 attribute_enumerator_t *this)
107 {
108 this->list->destroy_offset(this->list, offsetof(traffic_selector_t, destroy));
109 chunk_free(&this->attr);
110 free(this);
111 }
112
113 /**
114 * Check if we should send a configured TS as Split-Include attribute
115 */
116 static bool use_ts(traffic_selector_t *ts)
117 {
118 u_int8_t mask;
119 host_t *net;
120
121 if (ts->get_type(ts) != TS_IPV4_ADDR_RANGE)
122 {
123 return FALSE;
124 }
125 if (ts->is_dynamic(ts))
126 {
127 return FALSE;
128 }
129 if (!ts->to_subnet(ts, &net, &mask))
130 {
131 return FALSE;
132 }
133 net->destroy(net);
134 return mask > 0;
135 }
136
137 METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*,
138 private_unity_provider_t *this, linked_list_t *pools, identification_t *id,
139 linked_list_t *vips)
140 {
141 attribute_enumerator_t *attr_enum;
142 enumerator_t *enumerator;
143 linked_list_t *list, *current;
144 traffic_selector_t *ts;
145 ike_sa_t *ike_sa;
146 peer_cfg_t *peer_cfg;
147 child_cfg_t *child_cfg;
148
149 ike_sa = charon->bus->get_sa(charon->bus);
150 if (!ike_sa || ike_sa->get_version(ike_sa) != IKEV1 ||
151 !ike_sa->supports_extension(ike_sa, EXT_CISCO_UNITY) ||
152 !vips->get_count(vips))
153 {
154 return NULL;
155 }
156
157 list = linked_list_create();
158 peer_cfg = ike_sa->get_peer_cfg(ike_sa);
159 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
160 while (enumerator->enumerate(enumerator, &child_cfg))
161 {
162 current = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, NULL);
163 while (current->remove_first(current, (void**)&ts) == SUCCESS)
164 {
165 if (use_ts(ts))
166 {
167 list->insert_last(list, ts);
168 }
169 else
170 {
171 ts->destroy(ts);
172 }
173 }
174 current->destroy(current);
175 }
176 enumerator->destroy(enumerator);
177
178 if (list->get_count(list) == 0)
179 {
180 list->destroy(list);
181 return NULL;
182 }
183 DBG1(DBG_CFG, "sending %N: %#R",
184 configuration_attribute_type_names, UNITY_SPLIT_INCLUDE, list);
185
186 INIT(attr_enum,
187 .public = {
188 .enumerate = (void*)_attribute_enumerate,
189 .destroy = _attribute_destroy,
190 },
191 .list = list,
192 );
193
194 return &attr_enum->public;
195 }
196
197 METHOD(unity_provider_t, destroy, void,
198 private_unity_provider_t *this)
199 {
200 free(this);
201 }
202
203 /**
204 * See header
205 */
206 unity_provider_t *unity_provider_create()
207 {
208 private_unity_provider_t *this;
209
210 INIT(this,
211 .public = {
212 .provider = {
213 .acquire_address = (void*)return_null,
214 .release_address = (void*)return_false,
215 .create_attribute_enumerator = _create_attribute_enumerator,
216 },
217 .destroy = _destroy,
218 },
219 );
220
221 return &this->public;
222 }