2 * Copyright (C) 2012-2013 Tobias Brunner
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>. *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 #include "kernel_libipsec_ipsec.h"
16 #include "kernel_libipsec_router.h"
21 #include <networking/tun_device.h>
22 #include <threading/mutex.h>
23 #include <utils/debug.h>
25 typedef struct private_kernel_libipsec_ipsec_t private_kernel_libipsec_ipsec_t
;
27 struct private_kernel_libipsec_ipsec_t
{
30 * Public libipsec_ipsec interface
32 kernel_libipsec_ipsec_t
public;
35 * Listener for lifetime expire events
37 ipsec_event_listener_t ipsec_listener
;
40 * Mutex to lock access to various lists
45 * List of installed policies (policy_entry_t)
47 linked_list_t
*policies
;
50 * List of exclude routes (exclude_route_t)
52 linked_list_t
*excludes
;
55 * Whether the remote TS may equal the IKE peer
60 typedef struct exclude_route_t exclude_route_t
;
63 * Exclude route definition
65 struct exclude_route_t
{
66 /** Destination address to exclude */
68 /** Source address for route */
70 /** Nexthop exclude has been installed */
72 /** References to this route */
77 * Clean up an exclude route entry
79 static void exclude_route_destroy(exclude_route_t
*this)
81 this->dst
->destroy(this->dst
);
82 this->src
->destroy(this->src
);
83 this->gtw
->destroy(this->gtw
);
88 * Find an exclude route entry by destination address
90 static bool exclude_route_match(exclude_route_t
*current
,
93 return dst
->ip_equals(dst
, current
->dst
);
96 typedef struct route_entry_t route_entry_t
;
99 * Installed routing entry
101 struct route_entry_t
{
102 /** Name of the interface the route is bound to */
104 /** Source IP of the route */
106 /** Gateway of the route */
108 /** Destination net */
110 /** Destination net prefixlen */
112 /** Reference to exclude route, if any */
113 exclude_route_t
*exclude
;
117 * Destroy a route_entry_t object
119 static void route_entry_destroy(route_entry_t
*this)
122 DESTROY_IF(this->src_ip
);
123 DESTROY_IF(this->gateway
);
124 chunk_free(&this->dst_net
);
129 * Compare two route_entry_t objects
131 static bool route_entry_equals(route_entry_t
*a
, route_entry_t
*b
)
133 if ((!a
->src_ip
&& !b
->src_ip
) || (a
->src_ip
&& b
->src_ip
&&
134 a
->src_ip
->ip_equals(a
->src_ip
, b
->src_ip
)))
136 if ((!a
->gateway
&& !b
->gateway
) || (a
->gateway
&& b
->gateway
&&
137 a
->gateway
->ip_equals(a
->gateway
, b
->gateway
)))
139 return a
->if_name
&& b
->if_name
&& streq(a
->if_name
, b
->if_name
) &&
140 chunk_equals(a
->dst_net
, b
->dst_net
) &&
141 a
->prefixlen
== b
->prefixlen
;
147 typedef struct policy_entry_t policy_entry_t
;
152 struct policy_entry_t
{
153 /** Direction of this policy: in, out, forward */
155 /** Parameters of installed policy */
157 /** Subnet and port */
164 /** Associated route installed for this policy */
165 route_entry_t
*route
;
166 /** References to this policy */
171 * Create a policy_entry_t object
173 static policy_entry_t
*create_policy_entry(traffic_selector_t
*src_ts
,
174 traffic_selector_t
*dst_ts
,
177 policy_entry_t
*this;
182 src_ts
->to_subnet(src_ts
, &this->src
.net
, &this->src
.mask
);
183 dst_ts
->to_subnet(dst_ts
, &this->dst
.net
, &this->dst
.mask
);
185 /* src or dest proto may be "any" (0), use more restrictive one */
186 this->src
.proto
= max(src_ts
->get_protocol(src_ts
),
187 dst_ts
->get_protocol(dst_ts
));
188 this->src
.proto
= this->src
.proto ?
this->src
.proto
: 0;
189 this->dst
.proto
= this->src
.proto
;
194 * Destroy a policy_entry_t object
196 static void policy_entry_destroy(policy_entry_t
*this)
200 route_entry_destroy(this->route
);
202 DESTROY_IF(this->src
.net
);
203 DESTROY_IF(this->dst
.net
);
208 * Compare two policy_entry_t objects
210 static inline bool policy_entry_equals(policy_entry_t
*a
,
213 return a
->direction
== b
->direction
&&
214 a
->src
.proto
== b
->src
.proto
&&
215 a
->dst
.proto
== b
->dst
.proto
&&
216 a
->src
.mask
== b
->src
.mask
&&
217 a
->dst
.mask
== b
->dst
.mask
&&
218 a
->src
.net
->equals(a
->src
.net
, b
->src
.net
) &&
219 a
->dst
.net
->equals(a
->dst
.net
, b
->dst
.net
);
223 * Expiration callback
225 static void expire(u_int8_t protocol
, u_int32_t spi
, host_t
*dst
, bool hard
)
227 hydra
->kernel_interface
->expire(hydra
->kernel_interface
, protocol
,
231 METHOD(kernel_ipsec_t
, get_features
, kernel_feature_t
,
232 private_kernel_libipsec_ipsec_t
*this)
234 return KERNEL_REQUIRE_UDP_ENCAPSULATION
| KERNEL_ESP_V3_TFC
;
237 METHOD(kernel_ipsec_t
, get_spi
, status_t
,
238 private_kernel_libipsec_ipsec_t
*this, host_t
*src
, host_t
*dst
,
239 u_int8_t protocol
, u_int32_t
*spi
)
241 return ipsec
->sas
->get_spi(ipsec
->sas
, src
, dst
, protocol
, spi
);
244 METHOD(kernel_ipsec_t
, get_cpi
, status_t
,
245 private_kernel_libipsec_ipsec_t
*this, host_t
*src
, host_t
*dst
,
248 return NOT_SUPPORTED
;
251 METHOD(kernel_ipsec_t
, add_sa
, status_t
,
252 private_kernel_libipsec_ipsec_t
*this, host_t
*src
, host_t
*dst
,
253 u_int32_t spi
, u_int8_t protocol
, u_int32_t reqid
, mark_t mark
,
254 u_int32_t tfc
, lifetime_cfg_t
*lifetime
, u_int16_t enc_alg
, chunk_t enc_key
,
255 u_int16_t int_alg
, chunk_t int_key
, ipsec_mode_t mode
,
256 u_int16_t ipcomp
, u_int16_t cpi
, u_int32_t replay_window
,
257 bool initiator
, bool encap
, bool esn
, bool inbound
, bool update
,
258 linked_list_t
*src_ts
, linked_list_t
*dst_ts
)
260 return ipsec
->sas
->add_sa(ipsec
->sas
, src
, dst
, spi
, protocol
, reqid
, mark
,
261 tfc
, lifetime
, enc_alg
, enc_key
, int_alg
, int_key
,
262 mode
, ipcomp
, cpi
, initiator
, encap
, esn
, inbound
);
265 METHOD(kernel_ipsec_t
, update_sa
, status_t
,
266 private_kernel_libipsec_ipsec_t
*this, u_int32_t spi
, u_int8_t protocol
,
267 u_int16_t cpi
, host_t
*src
, host_t
*dst
, host_t
*new_src
, host_t
*new_dst
,
268 bool encap
, bool new_encap
, mark_t mark
)
270 return NOT_SUPPORTED
;
273 METHOD(kernel_ipsec_t
, query_sa
, status_t
,
274 private_kernel_libipsec_ipsec_t
*this, host_t
*src
, host_t
*dst
,
275 u_int32_t spi
, u_int8_t protocol
, mark_t mark
, u_int64_t
*bytes
,
276 u_int64_t
*packets
, time_t *time
)
278 return ipsec
->sas
->query_sa(ipsec
->sas
, src
, dst
, spi
, protocol
, mark
,
279 bytes
, packets
, time
);
282 METHOD(kernel_ipsec_t
, del_sa
, status_t
,
283 private_kernel_libipsec_ipsec_t
*this, host_t
*src
, host_t
*dst
,
284 u_int32_t spi
, u_int8_t protocol
, u_int16_t cpi
, mark_t mark
)
286 return ipsec
->sas
->del_sa(ipsec
->sas
, src
, dst
, spi
, protocol
, cpi
, mark
);
289 METHOD(kernel_ipsec_t
, flush_sas
, status_t
,
290 private_kernel_libipsec_ipsec_t
*this)
292 return ipsec
->sas
->flush_sas(ipsec
->sas
);
296 * Add an explicit exclude route to a routing entry
298 static void add_exclude_route(private_kernel_libipsec_ipsec_t
*this,
299 route_entry_t
*route
, host_t
*src
, host_t
*dst
)
301 exclude_route_t
*exclude
;
304 if (this->excludes
->find_first(this->excludes
,
305 (linked_list_match_t
)exclude_route_match
,
306 (void**)&exclude
, dst
) == SUCCESS
)
308 route
->exclude
= exclude
;
314 DBG2(DBG_KNL
, "installing new exclude route for %H src %H", dst
, src
);
315 gtw
= hydra
->kernel_interface
->get_nexthop(hydra
->kernel_interface
,
319 char *if_name
= NULL
;
321 if (hydra
->kernel_interface
->get_interface(
322 hydra
->kernel_interface
, src
, &if_name
) &&
323 hydra
->kernel_interface
->add_route(hydra
->kernel_interface
,
324 dst
->get_address(dst
),
325 dst
->get_family(dst
) == AF_INET ?
32 : 128,
326 gtw
, src
, if_name
) == SUCCESS
)
329 .dst
= dst
->clone(dst
),
330 .src
= src
->clone(src
),
331 .gtw
= gtw
->clone(gtw
),
334 route
->exclude
= exclude
;
335 this->excludes
->insert_last(this->excludes
, exclude
);
339 DBG1(DBG_KNL
, "installing exclude route for %H failed", dst
);
346 DBG1(DBG_KNL
, "gateway lookup for %H failed", dst
);
352 * Remove an exclude route attached to a routing entry
354 static void remove_exclude_route(private_kernel_libipsec_ipsec_t
*this,
355 route_entry_t
*route
)
357 char *if_name
= NULL
;
360 if (!route
->exclude
|| --route
->exclude
->refs
> 0)
364 this->excludes
->remove(this->excludes
, route
->exclude
, NULL
);
366 dst
= route
->exclude
->dst
;
367 DBG2(DBG_KNL
, "uninstalling exclude route for %H src %H",
368 dst
, route
->exclude
->src
);
369 if (hydra
->kernel_interface
->get_interface(
370 hydra
->kernel_interface
,
371 route
->exclude
->src
, &if_name
) &&
372 hydra
->kernel_interface
->del_route(hydra
->kernel_interface
,
373 dst
->get_address(dst
),
374 dst
->get_family(dst
) == AF_INET ?
32 : 128,
375 route
->exclude
->gtw
, route
->exclude
->src
,
378 DBG1(DBG_KNL
, "uninstalling exclude route for %H failed", dst
);
380 exclude_route_destroy(route
->exclude
);
381 route
->exclude
= NULL
;
386 * Install a route for the given policy
388 * this->mutex is released by this function
390 static bool install_route(private_kernel_libipsec_ipsec_t
*this,
391 host_t
*src
, host_t
*dst
, traffic_selector_t
*src_ts
,
392 traffic_selector_t
*dst_ts
, policy_entry_t
*policy
)
394 route_entry_t
*route
, *old
;
398 if (policy
->direction
!= POLICY_OUT
)
400 this->mutex
->unlock(this->mutex
);
404 if (hydra
->kernel_interface
->get_address_by_ts(hydra
->kernel_interface
,
405 src_ts
, &src_ip
, &is_virtual
) != SUCCESS
)
407 traffic_selector_t
*multicast
, *broadcast
= NULL
;
410 this->mutex
->unlock(this->mutex
);
411 switch (src_ts
->get_type(src_ts
))
413 case TS_IPV4_ADDR_RANGE
:
414 multicast
= traffic_selector_create_from_cidr("224.0.0.0/4",
416 broadcast
= traffic_selector_create_from_cidr("255.255.255.255/32",
419 case TS_IPV6_ADDR_RANGE
:
420 multicast
= traffic_selector_create_from_cidr("ff00::/8",
426 ignore
= src_ts
->is_contained_in(src_ts
, multicast
);
427 ignore
|= broadcast
&& src_ts
->is_contained_in(src_ts
, broadcast
);
428 multicast
->destroy(multicast
);
429 DESTROY_IF(broadcast
);
432 DBG1(DBG_KNL
, "error installing route with policy %R === %R %N",
433 src_ts
, dst_ts
, policy_dir_names
, policy
->direction
);
439 .if_name
= router
->get_tun_name(router
, is_virtual ? src_ip
: NULL
),
441 .dst_net
= chunk_clone(policy
->dst
.net
->get_address(policy
->dst
.net
)),
442 .prefixlen
= policy
->dst
.mask
,
445 /* on Linux we cant't install a gateway */
446 route
->gateway
= hydra
->kernel_interface
->get_nexthop(
447 hydra
->kernel_interface
, dst
, -1, src
);
454 if (route_entry_equals(old
, route
))
455 { /* such a route already exists */
456 route_entry_destroy(route
);
457 this->mutex
->unlock(this->mutex
);
460 /* uninstall previously installed route */
461 if (hydra
->kernel_interface
->del_route(hydra
->kernel_interface
,
462 old
->dst_net
, old
->prefixlen
, old
->gateway
,
463 old
->src_ip
, old
->if_name
) != SUCCESS
)
465 DBG1(DBG_KNL
, "error uninstalling route installed with policy "
466 "%R === %R %N", src_ts
, dst_ts
, policy_dir_names
,
469 route_entry_destroy(old
);
470 policy
->route
= NULL
;
473 if (!this->allow_peer_ts
&& dst_ts
->is_host(dst_ts
, dst
))
475 DBG1(DBG_KNL
, "can't install route for %R === %R %N, conflicts with "
476 "IKE traffic", src_ts
, dst_ts
, policy_dir_names
,
478 route_entry_destroy(route
);
479 this->mutex
->unlock(this->mutex
);
482 /* if remote traffic selector covers the IKE peer, add an exclude route */
483 if (!this->allow_peer_ts
&& dst_ts
->includes(dst_ts
, dst
))
485 /* add exclude route for peer */
486 add_exclude_route(this, route
, src
, dst
);
489 DBG2(DBG_KNL
, "installing route: %R src %H dev %s",
490 dst_ts
, route
->src_ip
, route
->if_name
);
492 switch (hydra
->kernel_interface
->add_route(hydra
->kernel_interface
,
493 route
->dst_net
, route
->prefixlen
, route
->gateway
,
494 route
->src_ip
, route
->if_name
))
497 /* route exists, do not uninstall */
498 remove_exclude_route(this, route
);
499 route_entry_destroy(route
);
500 this->mutex
->unlock(this->mutex
);
503 /* cache the installed route */
504 policy
->route
= route
;
505 this->mutex
->unlock(this->mutex
);
508 DBG1(DBG_KNL
, "installing route failed: %R src %H dev %s",
509 dst_ts
, route
->src_ip
, route
->if_name
);
510 remove_exclude_route(this, route
);
511 route_entry_destroy(route
);
512 this->mutex
->unlock(this->mutex
);
517 METHOD(kernel_ipsec_t
, add_policy
, status_t
,
518 private_kernel_libipsec_ipsec_t
*this, host_t
*src
, host_t
*dst
,
519 traffic_selector_t
*src_ts
, traffic_selector_t
*dst_ts
,
520 policy_dir_t direction
, policy_type_t type
, ipsec_sa_cfg_t
*sa
, mark_t mark
,
521 policy_priority_t priority
)
523 policy_entry_t
*policy
, *found
= NULL
;
526 status
= ipsec
->policies
->add_policy(ipsec
->policies
, src
, dst
, src_ts
,
527 dst_ts
, direction
, type
, sa
, mark
, priority
);
528 if (status
!= SUCCESS
)
532 /* we track policies in order to install routes */
533 policy
= create_policy_entry(src_ts
, dst_ts
, direction
);
535 this->mutex
->lock(this->mutex
);
536 if (this->policies
->find_first(this->policies
,
537 (linked_list_match_t
)policy_entry_equals
,
538 (void**)&found
, policy
) == SUCCESS
)
540 policy_entry_destroy(policy
);
544 { /* use the new one, if we have no such policy */
545 this->policies
->insert_last(this->policies
, policy
);
549 if (!install_route(this, src
, dst
, src_ts
, dst_ts
, policy
))
556 METHOD(kernel_ipsec_t
, query_policy
, status_t
,
557 private_kernel_libipsec_ipsec_t
*this, traffic_selector_t
*src_ts
,
558 traffic_selector_t
*dst_ts
, policy_dir_t direction
, mark_t mark
,
561 return NOT_SUPPORTED
;
564 METHOD(kernel_ipsec_t
, del_policy
, status_t
,
565 private_kernel_libipsec_ipsec_t
*this, traffic_selector_t
*src_ts
,
566 traffic_selector_t
*dst_ts
, policy_dir_t direction
, u_int32_t reqid
,
567 mark_t mark
, policy_priority_t priority
)
569 policy_entry_t
*policy
, *found
= NULL
;
572 status
= ipsec
->policies
->del_policy(ipsec
->policies
, src_ts
, dst_ts
,
573 direction
, reqid
, mark
, priority
);
575 policy
= create_policy_entry(src_ts
, dst_ts
, direction
);
577 this->mutex
->lock(this->mutex
);
578 if (this->policies
->find_first(this->policies
,
579 (linked_list_match_t
)policy_entry_equals
,
580 (void**)&found
, policy
) != SUCCESS
)
582 policy_entry_destroy(policy
);
583 this->mutex
->unlock(this->mutex
);
586 policy_entry_destroy(policy
);
589 if (--policy
->refs
> 0)
590 { /* policy is still in use */
591 this->mutex
->unlock(this->mutex
);
597 route_entry_t
*route
= policy
->route
;
599 if (hydra
->kernel_interface
->del_route(hydra
->kernel_interface
,
600 route
->dst_net
, route
->prefixlen
, route
->gateway
, route
->src_ip
,
601 route
->if_name
) != SUCCESS
)
603 DBG1(DBG_KNL
, "error uninstalling route installed with "
604 "policy %R === %R %N", src_ts
, dst_ts
,
605 policy_dir_names
, direction
);
607 remove_exclude_route(this, route
);
609 this->policies
->remove(this->policies
, policy
, NULL
);
610 policy_entry_destroy(policy
);
611 this->mutex
->unlock(this->mutex
);
615 METHOD(kernel_ipsec_t
, flush_policies
, status_t
,
616 private_kernel_libipsec_ipsec_t
*this)
621 status
= ipsec
->policies
->flush_policies(ipsec
->policies
);
623 this->mutex
->lock(this->mutex
);
624 while (this->policies
->remove_first(this->policies
, (void*)&pol
) == SUCCESS
)
628 route_entry_t
*route
= pol
->route
;
630 hydra
->kernel_interface
->del_route(hydra
->kernel_interface
,
631 route
->dst_net
, route
->prefixlen
, route
->gateway
,
632 route
->src_ip
, route
->if_name
);
633 remove_exclude_route(this, route
);
635 policy_entry_destroy(pol
);
637 this->mutex
->unlock(this->mutex
);
641 METHOD(kernel_ipsec_t
, bypass_socket
, bool,
642 private_kernel_libipsec_ipsec_t
*this, int fd
, int family
)
644 /* we use exclude routes for this */
645 return NOT_SUPPORTED
;
648 METHOD(kernel_ipsec_t
, enable_udp_decap
, bool,
649 private_kernel_libipsec_ipsec_t
*this, int fd
, int family
, u_int16_t port
)
651 return NOT_SUPPORTED
;
654 METHOD(kernel_ipsec_t
, destroy
, void,
655 private_kernel_libipsec_ipsec_t
*this)
657 ipsec
->events
->unregister_listener(ipsec
->events
, &this->ipsec_listener
);
658 this->policies
->destroy_function(this->policies
, (void*)policy_entry_destroy
);
659 this->excludes
->destroy(this->excludes
);
660 this->mutex
->destroy(this->mutex
);
665 * Described in header.
667 kernel_libipsec_ipsec_t
*kernel_libipsec_ipsec_create()
669 private_kernel_libipsec_ipsec_t
*this;
674 .get_features
= _get_features
,
678 .update_sa
= _update_sa
,
679 .query_sa
= _query_sa
,
681 .flush_sas
= _flush_sas
,
682 .add_policy
= _add_policy
,
683 .query_policy
= _query_policy
,
684 .del_policy
= _del_policy
,
685 .flush_policies
= _flush_policies
,
686 .bypass_socket
= _bypass_socket
,
687 .enable_udp_decap
= _enable_udp_decap
,
694 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
695 .policies
= linked_list_create(),
696 .excludes
= linked_list_create(),
697 .allow_peer_ts
= lib
->settings
->get_bool(lib
->settings
,
698 "%s.plugins.kernel-libipsec.allow_peer_ts", FALSE
, lib
->ns
),
701 ipsec
->events
->register_listener(ipsec
->events
, &this->ipsec_listener
);
703 return &this->public;