]> git.ipfire.org Git - thirdparty/strongswan.git/blame - src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c
kernel-interface: Optionally pass CPU ID for which an acquire was triggered
[thirdparty/strongswan.git] / src / libcharon / plugins / kernel_wfp / kernel_wfp_ipsec.c
CommitLineData
8d91eee3
MW
1/*
2 * Copyright (C) 2013 Martin Willi
19ef2aec
TB
3 *
4 * Copyright (C) secunet Security Networks AG
8d91eee3
MW
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
ebb9362d
MW
17/* Windows 7, for some fwpmu.h functionality */
18#define _WIN32_WINNT 0x0601
19
b1ba0a66 20#include "kernel_wfp_compat.h"
8d91eee3
MW
21#include "kernel_wfp_ipsec.h"
22
23#include <daemon.h>
96ab7a80
MW
24#include <threading/mutex.h>
25#include <collections/array.h>
26#include <collections/hashtable.h>
af098b50 27#include <processing/jobs/callback_job.h>
8d91eee3 28
9d240b07 29#ifndef IPPROTO_IPIP
a8142a17 30#define IPPROTO_IPIP 4
9d240b07
TB
31#endif
32#ifndef IPPROTO_IPV6
a8142a17 33#define IPPROTO_IPV6 41
9d240b07 34#endif
ebb9362d 35
8d91eee3
MW
36typedef struct private_kernel_wfp_ipsec_t private_kernel_wfp_ipsec_t;
37
38struct private_kernel_wfp_ipsec_t {
39
40 /**
41 * Public interface
42 */
43 kernel_wfp_ipsec_t public;
96ab7a80
MW
44
45 /**
46 * Next SPI to allocate
47 */
48 refcount_t nextspi;
49
bbe42a1f
MW
50 /**
51 * Mix value to distribute SPI allocation randomly
52 */
b12c53ce 53 uint32_t mixspi;
bbe42a1f 54
f206e069
MW
55 /**
56 * IKE bypass filters, as UINT64 filter LUID
57 */
58 array_t *bypass;
59
96ab7a80 60 /**
f351d9ef 61 * Temporary SAD/SPD entries referenced reqid, as uintptr_t => entry_t
96ab7a80 62 */
f351d9ef 63 hashtable_t *tsas;
96ab7a80
MW
64
65 /**
f351d9ef 66 * SAD/SPD entries referenced by inbound SA, as sa_entry_t => entry_t
96ab7a80 67 */
f351d9ef
MW
68 hashtable_t *isas;
69
70 /**
71 * SAD/SPD entries referenced by outbound SA, as sa_entry_t => entry_t
72 */
73 hashtable_t *osas;
96ab7a80 74
b714746e
MW
75 /**
76 * Installed routes, as route_t => route_t
77 */
78 hashtable_t *routes;
79
c6f189e4
MW
80 /**
81 * Installed traps, as trap_t => trap_t
82 */
83 hashtable_t *traps;
84
96ab7a80
MW
85 /**
86 * Mutex for accessing entries
87 */
88 mutex_t *mutex;
ebb9362d
MW
89
90 /**
91 * WFP session handle
92 */
93 HANDLE handle;
f5ddda7f
MW
94
95 /**
96 * Provider charon registers as
97 */
98 FWPM_PROVIDER0 provider;
28683140
MW
99
100 /**
101 * Event handle
102 */
103 HANDLE event;
8d91eee3
MW
104};
105
96ab7a80
MW
106/**
107 * Security association entry
108 */
109typedef struct {
110 /** SPI for this SA */
b12c53ce 111 uint32_t spi;
f351d9ef 112 /** protocol, IPPROTO_ESP/IPPROTO_AH */
b12c53ce 113 uint8_t protocol;
b3f90915 114 /** hard lifetime of SA */
b12c53ce 115 uint32_t lifetime;
96ab7a80
MW
116 /** destination host address for this SPI */
117 host_t *dst;
96ab7a80
MW
118 struct {
119 /** algorithm */
b12c53ce 120 uint16_t alg;
96ab7a80
MW
121 /** key */
122 chunk_t key;
123 } integ, encr;
124} sa_entry_t;
125
96ab7a80
MW
126/**
127 * Hash function for sas lookup table
128 */
129static u_int hash_sa(sa_entry_t *key)
130{
131 return chunk_hash_inc(chunk_from_thing(key->spi),
132 chunk_hash(key->dst->get_address(key->dst)));
133}
134
135/**
136 * equals function for sas lookup table
137 */
138static bool equals_sa(sa_entry_t *a, sa_entry_t *b)
139{
140 return a->spi == b->spi && a->dst->ip_equals(a->dst, b->dst);
141}
142
143/**
144 * Security policy entry
145 */
146typedef struct {
147 /** policy source addresses */
148 traffic_selector_t *src;
b3ab7a48 149 /** policy destination addresses */
96ab7a80 150 traffic_selector_t *dst;
4b512803 151 /** WFP allocated LUID for inbound filter ID */
b12c53ce 152 uint64_t policy_in;
4b512803 153 /** WFP allocated LUID for outbound filter ID */
b12c53ce 154 uint64_t policy_out;
4b512803 155 /** WFP allocated LUID for forward inbound filter ID, tunnel mode only */
b12c53ce 156 uint64_t policy_fwd_in;
4b512803 157 /** WFP allocated LUID for forward outbound filter ID, tunnel mode only */
b12c53ce 158 uint64_t policy_fwd_out;
4b512803
MW
159 /** have installed a route for it? */
160 bool route;
96ab7a80
MW
161} sp_entry_t;
162
163/**
164 * Destroy an SP entry
165 */
166static void sp_entry_destroy(sp_entry_t *sp)
167{
168 sp->src->destroy(sp->src);
169 sp->dst->destroy(sp->dst);
170 free(sp);
171}
172
173/**
174 * Collection of SA/SP database entries for a reqid
175 */
176typedef struct {
177 /** reqid of entry */
b12c53ce 178 uint32_t reqid;
96ab7a80
MW
179 /** outer address on local host */
180 host_t *local;
181 /** outer address on remote host */
182 host_t *remote;
f351d9ef
MW
183 /** inbound SA entry */
184 sa_entry_t isa;
185 /** outbound SA entry */
186 sa_entry_t osa;
187 /** associated (outbound) policies, as sp_entry_t* */
96ab7a80 188 array_t *sps;
96ab7a80
MW
189 /** IPsec mode, tunnel|transport */
190 ipsec_mode_t mode;
191 /** UDP encapsulation */
192 bool encap;
1678f0a9 193 /** provider context, for tunnel mode only */
b12c53ce 194 uint64_t provider;
149fc48e 195 /** WFP allocated LUID for SA context */
b12c53ce 196 uint64_t sa_id;
e1a44831 197 /** WFP allocated LUID for tunnel mode IP-IPv4 inbound filter */
b12c53ce 198 uint64_t ip_ipv4_in;
e1a44831 199 /** WFP allocated LUID for tunnel mode IP-IPv4 outbound filter */
b12c53ce 200 uint64_t ip_ipv4_out;
e1a44831 201 /** WFP allocated LUID for tunnel mode IP-IPv6 inbound filter */
b12c53ce 202 uint64_t ip_ipv6_in;
e1a44831 203 /** WFP allocated LUID for tunnel mode IP-IPv6 outbound filter */
b12c53ce 204 uint64_t ip_ipv6_out;
96ab7a80
MW
205} entry_t;
206
b714746e
MW
207/**
208 * Installed route
209 */
210typedef struct {
211 /** destination net of route */
212 host_t *dst;
213 /** prefix length of dst */
b12c53ce 214 uint8_t mask;
b714746e
MW
215 /** source address for route */
216 host_t *src;
217 /** gateway of route, NULL if directly attached */
218 host_t *gtw;
219 /** references for route */
220 u_int refs;
221} route_t;
222
223/**
224 * Destroy a route_t
225 */
226static void destroy_route(route_t *this)
227{
228 this->dst->destroy(this->dst);
229 this->src->destroy(this->src);
230 DESTROY_IF(this->gtw);
231 free(this);
232}
233
234/**
235 * Hashtable equals function for routes
236 */
237static bool equals_route(route_t *a, route_t *b)
238{
239 return a->mask == b->mask &&
240 a->dst->ip_equals(a->dst, b->dst) &&
241 a->src->ip_equals(a->src, b->src);
242}
243
244/**
245 * Hashtable hash function for routes
246 */
247static u_int hash_route(route_t *route)
248{
249 return chunk_hash_inc(route->src->get_address(route->src),
250 chunk_hash_inc(route->dst->get_address(route->dst), route->mask));
251}
252
253/** forward declaration */
254static bool manage_routes(private_kernel_wfp_ipsec_t *this, entry_t *entry,
255 bool add);
256
4a8b8568
MW
257/**
258 * Remove policies associated to an entry from kernel
259 */
260static void cleanup_policies(private_kernel_wfp_ipsec_t *this, entry_t *entry)
261{
4b512803
MW
262 enumerator_t *enumerator;
263 sp_entry_t *sp;
264
b714746e
MW
265 if (entry->mode == MODE_TUNNEL)
266 {
267 manage_routes(this, entry, FALSE);
4b512803
MW
268 }
269
270 enumerator = array_create_enumerator(entry->sps);
271 while (enumerator->enumerate(enumerator, &sp))
272 {
273 if (sp->policy_in)
6de78870 274 {
4b512803
MW
275 FwpmFilterDeleteById0(this->handle, sp->policy_in);
276 sp->policy_in = 0;
6de78870 277 }
4b512803 278 if (sp->policy_out)
6de78870 279 {
4b512803
MW
280 FwpmFilterDeleteById0(this->handle, sp->policy_out);
281 sp->policy_out = 0;
282 }
283 if (sp->policy_fwd_in)
284 {
285 FwpmFilterDeleteById0(this->handle, sp->policy_fwd_in);
286 sp->policy_fwd_in = 0;
287 }
288 if (sp->policy_fwd_out)
289 {
290 FwpmFilterDeleteById0(this->handle, sp->policy_fwd_out);
291 sp->policy_fwd_out = 0;
6de78870 292 }
b714746e 293 }
4b512803 294 enumerator->destroy(enumerator);
4a8b8568
MW
295}
296
297/**
298 * Destroy a SA/SP entry set
299 */
300static void entry_destroy(private_kernel_wfp_ipsec_t *this, entry_t *entry)
301{
e1a44831 302 if (entry->ip_ipv4_in)
a8142a17 303 {
e1a44831 304 FwpmFilterDeleteById0(this->handle, entry->ip_ipv4_in);
a8142a17 305 }
e1a44831 306 if (entry->ip_ipv4_out)
a8142a17 307 {
e1a44831
MW
308 FwpmFilterDeleteById0(this->handle, entry->ip_ipv4_out);
309 }
310 if (entry->ip_ipv6_in)
311 {
312 FwpmFilterDeleteById0(this->handle, entry->ip_ipv6_in);
313 }
314 if (entry->ip_ipv6_out)
315 {
316 FwpmFilterDeleteById0(this->handle, entry->ip_ipv6_out);
a8142a17 317 }
4a8b8568
MW
318 if (entry->sa_id)
319 {
320 IPsecSaContextDeleteById0(this->handle, entry->sa_id);
149fc48e 321 }
1678f0a9
MW
322 if (entry->provider)
323 {
324 FwpmProviderContextDeleteById0(this->handle, entry->provider);
325 }
4a8b8568 326 cleanup_policies(this, entry);
f351d9ef 327 array_destroy_function(entry->sps, (void*)sp_entry_destroy, NULL);
96ab7a80
MW
328 entry->local->destroy(entry->local);
329 entry->remote->destroy(entry->remote);
f351d9ef
MW
330 chunk_clear(&entry->isa.integ.key);
331 chunk_clear(&entry->isa.encr.key);
332 chunk_clear(&entry->osa.integ.key);
333 chunk_clear(&entry->osa.encr.key);
96ab7a80
MW
334 free(entry);
335}
336
149fc48e
MW
337/**
338 * Append/Realloc a filter condition to an existing condition set
339 */
340static FWPM_FILTER_CONDITION0 *append_condition(FWPM_FILTER_CONDITION0 *conds[],
341 int *count)
342{
343 FWPM_FILTER_CONDITION0 *cond;
344
345 (*count)++;
346 *conds = realloc(*conds, *count * sizeof(*cond));
347 cond = *conds + *count - 1;
348 memset(cond, 0, sizeof(*cond));
349
350 return cond;
351}
352
353/**
354 * Convert an IPv4 prefix to a host order subnet mask
355 */
b12c53ce 356static uint32_t prefix2mask(uint8_t prefix)
149fc48e 357{
b12c53ce 358 uint8_t netmask[4] = {};
149fc48e
MW
359 int i;
360
361 for (i = 0; i < sizeof(netmask); i++)
362 {
363 if (prefix < 8)
364 {
365 netmask[i] = 0xFF << (8 - prefix);
366 break;
367 }
368 netmask[i] = 0xFF;
369 prefix -= 8;
370 }
371 return untoh32(netmask);
372}
373
374/**
375 * Convert a 16-bit range to a WFP condition
376 */
377static void range2cond(FWPM_FILTER_CONDITION0 *cond,
b12c53ce 378 uint16_t from, uint16_t to)
149fc48e
MW
379{
380 if (from == to)
381 {
382 cond->matchType = FWP_MATCH_EQUAL;
383 cond->conditionValue.type = FWP_UINT16;
384 cond->conditionValue.uint16 = from;
385 }
386 else
387 {
388 cond->matchType = FWP_MATCH_RANGE;
389 cond->conditionValue.type = FWP_RANGE_TYPE;
390 cond->conditionValue.rangeValue = calloc(1, sizeof(FWP_RANGE0));
391 cond->conditionValue.rangeValue->valueLow.type = FWP_UINT16;
392 cond->conditionValue.rangeValue->valueLow.uint16 = from;
393 cond->conditionValue.rangeValue->valueHigh.type = FWP_UINT16;
394 cond->conditionValue.rangeValue->valueHigh.uint16 = to;
395 }
396}
397
398/**
399 * (Re-)allocate filter conditions for given local or remote traffic selector
400 */
6de78870 401static bool ts2condition(traffic_selector_t *ts, const GUID *target,
149fc48e
MW
402 FWPM_FILTER_CONDITION0 *conds[], int *count)
403{
404 FWPM_FILTER_CONDITION0 *cond;
405 FWP_BYTE_ARRAY16 *addr;
406 FWP_RANGE0 *range;
b12c53ce 407 uint16_t from_port, to_port;
149fc48e 408 void *from, *to;
b12c53ce 409 uint8_t proto;
149fc48e 410 host_t *net;
b12c53ce 411 uint8_t prefix;
149fc48e
MW
412
413 from = ts->get_from_address(ts).ptr;
414 to = ts->get_to_address(ts).ptr;
415 from_port = ts->get_from_port(ts);
416 to_port = ts->get_to_port(ts);
417
418 cond = append_condition(conds, count);
6de78870 419 cond->fieldKey = *target;
149fc48e
MW
420 if (ts->is_host(ts, NULL))
421 {
422 cond->matchType = FWP_MATCH_EQUAL;
423 switch (ts->get_type(ts))
424 {
425 case TS_IPV4_ADDR_RANGE:
426 cond->conditionValue.type = FWP_UINT32;
427 cond->conditionValue.uint32 = untoh32(from);
428 break;
429 case TS_IPV6_ADDR_RANGE:
430 cond->conditionValue.type = FWP_BYTE_ARRAY16_TYPE;
431 cond->conditionValue.byteArray16 = addr = malloc(sizeof(*addr));
432 memcpy(addr, from, sizeof(*addr));
433 break;
434 default:
435 return FALSE;
436 }
437 }
438 else if (ts->to_subnet(ts, &net, &prefix))
439 {
440 FWP_V6_ADDR_AND_MASK *m6;
441 FWP_V4_ADDR_AND_MASK *m4;
442
443 cond->matchType = FWP_MATCH_EQUAL;
444 switch (net->get_family(net))
445 {
446 case AF_INET:
447 cond->conditionValue.type = FWP_V4_ADDR_MASK;
448 cond->conditionValue.v4AddrMask = m4 = calloc(1, sizeof(*m4));
449 m4->addr = untoh32(from);
450 m4->mask = prefix2mask(prefix);
451 break;
452 case AF_INET6:
453 cond->conditionValue.type = FWP_V6_ADDR_MASK;
454 cond->conditionValue.v6AddrMask = m6 = calloc(1, sizeof(*m6));
455 memcpy(m6->addr, from, sizeof(m6->addr));
456 m6->prefixLength = prefix;
457 break;
458 default:
459 net->destroy(net);
460 return FALSE;
461 }
462 net->destroy(net);
463 }
464 else
465 {
466 cond->matchType = FWP_MATCH_RANGE;
467 cond->conditionValue.type = FWP_RANGE_TYPE;
468 cond->conditionValue.rangeValue = range = calloc(1, sizeof(*range));
469 switch (ts->get_type(ts))
470 {
471 case TS_IPV4_ADDR_RANGE:
472 range->valueLow.type = FWP_UINT32;
473 range->valueLow.uint32 = untoh32(from);
474 range->valueHigh.type = FWP_UINT32;
475 range->valueHigh.uint32 = untoh32(to);
476 break;
477 case TS_IPV6_ADDR_RANGE:
478 range->valueLow.type = FWP_BYTE_ARRAY16_TYPE;
479 range->valueLow.byteArray16 = addr = malloc(sizeof(*addr));
480 memcpy(addr, from, sizeof(*addr));
481 range->valueHigh.type = FWP_BYTE_ARRAY16_TYPE;
482 range->valueHigh.byteArray16 = addr = malloc(sizeof(*addr));
483 memcpy(addr, to, sizeof(*addr));
484 break;
485 default:
486 return FALSE;
487 }
488 }
489
490 proto = ts->get_protocol(ts);
6de78870 491 if (proto && target == &FWPM_CONDITION_IP_LOCAL_ADDRESS)
149fc48e
MW
492 {
493 cond = append_condition(conds, count);
494 cond->fieldKey = FWPM_CONDITION_IP_PROTOCOL;
495 cond->matchType = FWP_MATCH_EQUAL;
496 cond->conditionValue.type = FWP_UINT8;
497 cond->conditionValue.uint8 = proto;
498 }
499
500 if (proto == IPPROTO_ICMP)
501 {
6de78870 502 if (target == &FWPM_CONDITION_IP_LOCAL_ADDRESS)
149fc48e 503 {
b12c53ce 504 uint8_t from_type, to_type, from_code, to_code;
149fc48e
MW
505
506 from_type = traffic_selector_icmp_type(from_port);
507 to_type = traffic_selector_icmp_type(to_port);
508 from_code = traffic_selector_icmp_code(from_port);
509 to_code = traffic_selector_icmp_code(to_port);
510
511 if (from_type != 0 || to_type != 0xFF)
512 {
513 cond = append_condition(conds, count);
514 cond->fieldKey = FWPM_CONDITION_ICMP_TYPE;
515 range2cond(cond, from_type, to_type);
516 }
517 if (from_code != 0 || to_code != 0xFF)
518 {
519 cond = append_condition(conds, count);
520 cond->fieldKey = FWPM_CONDITION_ICMP_CODE;
521 range2cond(cond, from_code, to_code);
522 }
523 }
524 }
525 else if (from_port != 0 || to_port != 0xFFFF)
526 {
6de78870 527 if (target == &FWPM_CONDITION_IP_LOCAL_ADDRESS)
149fc48e 528 {
6de78870 529 cond = append_condition(conds, count);
149fc48e 530 cond->fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
6de78870 531 range2cond(cond, from_port, to_port);
149fc48e 532 }
6de78870 533 if (target == &FWPM_CONDITION_IP_REMOTE_ADDRESS)
149fc48e 534 {
6de78870 535 cond = append_condition(conds, count);
149fc48e 536 cond->fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
6de78870 537 range2cond(cond, from_port, to_port);
149fc48e 538 }
149fc48e
MW
539 }
540 return TRUE;
541}
542
543/**
544 * Free memory associated to a single condition
545 */
546static void free_condition(FWP_DATA_TYPE type, void *value)
547{
548 FWP_RANGE0 *range;
549
550 switch (type)
551 {
552 case FWP_BYTE_ARRAY16_TYPE:
553 case FWP_V4_ADDR_MASK:
554 case FWP_V6_ADDR_MASK:
555 free(value);
556 break;
557 case FWP_RANGE_TYPE:
558 range = value;
559 free_condition(range->valueLow.type, range->valueLow.sd);
560 free_condition(range->valueHigh.type, range->valueHigh.sd);
561 free(range);
562 break;
563 default:
564 break;
565 }
566}
567
568/**
569 * Free memory used by a set of conditions
570 */
571static void free_conditions(FWPM_FILTER_CONDITION0 *conds, int count)
572{
573 int i;
574
575 for (i = 0; i < count; i++)
576 {
577 free_condition(conds[i].conditionValue.type, conds[i].conditionValue.sd);
578 }
579 free(conds);
580}
581
6de78870
MW
582/**
583 * Find the callout GUID for given parameters
584 */
585static bool find_callout(bool tunnel, bool v6, bool inbound, bool forward,
a8142a17 586 bool ale, GUID *layer, GUID *sublayer, GUID *callout)
6de78870
MW
587{
588 struct {
589 bool tunnel;
590 bool v6;
591 bool inbound;
592 bool forward;
a8142a17 593 bool ale;
6de78870 594 const GUID *layer;
4a8ba369 595 const GUID *sublayer;
6de78870
MW
596 const GUID *callout;
597 } map[] = {
a8142a17
MW
598 { 0, 0, 0, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V4, NULL,
599 &FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4 },
600 { 0, 0, 1, 0, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V4, NULL,
601 &FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4 },
602 { 0, 1, 0, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V6, NULL,
603 &FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V6 },
604 { 0, 1, 1, 0, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V6, NULL,
605 &FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V6 },
606 { 1, 0, 0, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V4,
607 &FWPM_SUBLAYER_IPSEC_TUNNEL,
608 &FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V4 },
609 { 1, 0, 0, 1, 0, &FWPM_LAYER_IPFORWARD_V4,
610 &FWPM_SUBLAYER_IPSEC_FORWARD_OUTBOUND_TUNNEL,
611 &FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V4 },
612 { 1, 0, 1, 0, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V4,
613 &FWPM_SUBLAYER_IPSEC_TUNNEL,
614 &FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V4 },
615 { 1, 0, 1, 1, 0, &FWPM_LAYER_IPFORWARD_V4,
616 &FWPM_SUBLAYER_IPSEC_TUNNEL,
617 &FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V4 },
618 { 1, 0, 0, 0, 1, &FWPM_LAYER_ALE_AUTH_CONNECT_V4, NULL,
619 &FWPM_CALLOUT_IPSEC_ALE_CONNECT_V4 },
620 { 1, 0, 1, 0, 1, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4, NULL,
621 &FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_ALE_ACCEPT_V4},
622 { 1, 1, 0, 0, 0, &FWPM_LAYER_OUTBOUND_TRANSPORT_V6,
623 &FWPM_SUBLAYER_IPSEC_TUNNEL,
624 &FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V6 },
625 { 1, 1, 0, 1, 0, &FWPM_LAYER_IPFORWARD_V6,
626 &FWPM_SUBLAYER_IPSEC_FORWARD_OUTBOUND_TUNNEL,
627 &FWPM_CALLOUT_IPSEC_FORWARD_OUTBOUND_TUNNEL_V6 },
628 { 1, 1, 1, 0, 0, &FWPM_LAYER_INBOUND_TRANSPORT_V6,
629 &FWPM_SUBLAYER_IPSEC_TUNNEL,
630 &FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V6 },
631 { 1, 1, 1, 1, 0, &FWPM_LAYER_IPFORWARD_V6,
632 &FWPM_SUBLAYER_IPSEC_TUNNEL,
633 &FWPM_CALLOUT_IPSEC_FORWARD_INBOUND_TUNNEL_V6 },
634 { 1, 1, 0, 0, 1, &FWPM_LAYER_ALE_AUTH_CONNECT_V6, NULL,
635 &FWPM_CALLOUT_IPSEC_ALE_CONNECT_V6 },
636 { 1, 1, 1, 0, 1, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, NULL,
637 &FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_ALE_ACCEPT_V6},
6de78870
MW
638 };
639 int i;
640
641 for (i = 0; i < countof(map); i++)
642 {
643 if (tunnel == map[i].tunnel &&
644 v6 == map[i].v6 &&
645 inbound == map[i].inbound &&
a8142a17
MW
646 forward == map[i].forward &&
647 ale == map[i].ale)
6de78870
MW
648 {
649 *callout = *map[i].callout;
650 *layer = *map[i].layer;
4a8ba369
MW
651 if (map[i].sublayer)
652 {
653 *sublayer = *map[i].sublayer;
654 }
6de78870
MW
655 return TRUE;
656 }
657 }
658 return FALSE;
659}
660
149fc48e 661/**
1678f0a9 662 * Install a single policy in to the kernel
149fc48e 663 */
6de78870
MW
664static bool install_sp(private_kernel_wfp_ipsec_t *this, sp_entry_t *sp,
665 GUID *context, bool inbound, bool fwd, UINT64 *filter_id)
149fc48e
MW
666{
667 FWPM_FILTER_CONDITION0 *conds = NULL;
149fc48e 668 traffic_selector_t *local, *remote;
6de78870
MW
669 const GUID *ltarget, *rtarget;
670 int count = 0;
671 bool v6;
149fc48e
MW
672 DWORD res;
673 FWPM_FILTER0 filter = {
674 .displayData = {
1678f0a9 675 .name = L"charon IPsec policy",
149fc48e
MW
676 },
677 .action = {
678 .type = FWP_ACTION_CALLOUT_TERMINATING,
149fc48e 679 },
149fc48e
MW
680 };
681
1678f0a9
MW
682 if (context)
683 {
1678f0a9 684 filter.flags |= FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT;
6de78870
MW
685 filter.providerKey = (GUID*)&this->provider.providerKey;
686 filter.providerContextKey = *context;
687 }
688
689 v6 = sp->src->get_type(sp->src) == TS_IPV6_ADDR_RANGE;
a8142a17 690 if (!find_callout(context != NULL, v6, inbound, fwd, FALSE,
4a8ba369
MW
691 &filter.layerKey, &filter.subLayerKey,
692 &filter.action.calloutKey))
6de78870
MW
693 {
694 return FALSE;
695 }
696
697 if (inbound && fwd)
698 {
699 local = sp->dst;
700 remote = sp->src;
1678f0a9
MW
701 }
702 else
703 {
6de78870
MW
704 local = sp->src;
705 remote = sp->dst;
706 }
707 if (fwd)
708 {
709 ltarget = &FWPM_CONDITION_IP_SOURCE_ADDRESS;
710 rtarget = &FWPM_CONDITION_IP_DESTINATION_ADDRESS;
711 }
712 else
713 {
714 ltarget = &FWPM_CONDITION_IP_LOCAL_ADDRESS;
715 rtarget = &FWPM_CONDITION_IP_REMOTE_ADDRESS;
1678f0a9 716 }
6de78870
MW
717 if (!ts2condition(local, ltarget, &conds, &count) ||
718 !ts2condition(remote, rtarget, &conds, &count))
719 {
720 free_conditions(conds, count);
721 return FALSE;
722 }
723
724 filter.numFilterConditions = count;
725 filter.filterCondition = conds;
726
727 res = FwpmFilterAdd0(this->handle, &filter, NULL, filter_id);
728 free_conditions(conds, count);
729 if (res != ERROR_SUCCESS)
730 {
a8142a17
MW
731 DBG1(DBG_KNL, "installing IPv%d %s%sbound %s WFP filter failed: 0x%08x",
732 v6 ? 6 : 4, fwd ? "forward " : "", inbound ? "in" : "out",
733 context ? "tunnel" : "transport", res);
734 return FALSE;
735 }
736 return TRUE;
737}
738
739/**
740 * Install an IP-IP allow filter for SA specific hosts
741 */
742static bool install_ipip_ale(private_kernel_wfp_ipsec_t *this,
743 host_t *local, host_t *remote, GUID *context,
b12c53ce 744 bool inbound, int proto, uint64_t *filter_id)
a8142a17
MW
745{
746 traffic_selector_t *lts, *rts;
747 FWPM_FILTER_CONDITION0 *conds = NULL;
748 int count = 0;
749 bool v6;
750 DWORD res;
751 FWPM_FILTER0 filter = {
752 .displayData = {
753 .name = L"charon IPsec IP-in-IP ALE policy",
754 },
755 .action = {
756 .type = FWP_ACTION_CALLOUT_TERMINATING,
757 },
a8142a17
MW
758 };
759
e1a44831
MW
760 if (context)
761 {
762 filter.flags |= FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT;
763 filter.providerKey = (GUID*)&this->provider.providerKey;
764 filter.providerContextKey = *context;
765 }
766
a8142a17 767 v6 = local->get_family(local) == AF_INET6;
e1a44831 768 if (!find_callout(TRUE, v6, inbound, FALSE, TRUE, &filter.layerKey,
a8142a17
MW
769 &filter.subLayerKey, &filter.action.calloutKey))
770 {
771 return FALSE;
772 }
773
774 lts = traffic_selector_create_from_subnet(local->clone(local),
775 v6 ? 128 : 32 , proto, 0, 65535);
776 rts = traffic_selector_create_from_subnet(remote->clone(remote),
777 v6 ? 128 : 32 , proto, 0, 65535);
778 if (!ts2condition(lts, &FWPM_CONDITION_IP_LOCAL_ADDRESS, &conds, &count) ||
779 !ts2condition(rts, &FWPM_CONDITION_IP_REMOTE_ADDRESS, &conds, &count))
780 {
781 free_conditions(conds, count);
782 lts->destroy(lts);
783 rts->destroy(rts);
784 return FALSE;
785 }
786 lts->destroy(lts);
787 rts->destroy(rts);
788
789 filter.numFilterConditions = count;
790 filter.filterCondition = conds;
791
792 res = FwpmFilterAdd0(this->handle, &filter, NULL, filter_id);
793 free_conditions(conds, count);
794 if (res != ERROR_SUCCESS)
795 {
e1a44831
MW
796 DBG1(DBG_KNL, "installing IP-IPv%d %s ALE WFP filter failed: 0x%08x",
797 v6 ? 6 : 4, inbound ? "inbound" : "outbound", res);
6de78870
MW
798 return FALSE;
799 }
800 return TRUE;
801}
802
803/**
804 * Install a set of policies in to the kernel
805 */
806static bool install_sps(private_kernel_wfp_ipsec_t *this,
807 entry_t *entry, GUID *context)
808{
809 enumerator_t *enumerator;
810 sp_entry_t *sp;
a8142a17 811 bool has_v4 = FALSE, has_v6 = FALSE;
1678f0a9 812
149fc48e
MW
813 enumerator = array_create_enumerator(entry->sps);
814 while (enumerator->enumerate(enumerator, &sp))
815 {
a8142a17
MW
816 switch (sp->src->get_type(sp->src))
817 {
818 case TS_IPV4_ADDR_RANGE:
819 has_v4 = TRUE;
820 break;
821 case TS_IPV6_ADDR_RANGE:
822 has_v6 = TRUE;
823 break;
d71d181d
TB
824 default:
825 continue;
a8142a17
MW
826 }
827
6de78870 828 /* inbound policy */
4b512803 829 if (!install_sp(this, sp, context, TRUE, FALSE, &sp->policy_in))
149fc48e 830 {
6de78870
MW
831 enumerator->destroy(enumerator);
832 return FALSE;
149fc48e 833 }
6de78870 834 /* outbound policy */
4b512803 835 if (!install_sp(this, sp, context, FALSE, FALSE, &sp->policy_out))
149fc48e 836 {
149fc48e
MW
837 enumerator->destroy(enumerator);
838 return FALSE;
839 }
a8142a17 840
6de78870
MW
841 if (context)
842 {
843 if (!sp->src->is_host(sp->src, entry->local) ||
844 !sp->dst->is_host(sp->dst, entry->remote))
845 {
846 /* inbound forward policy, from decapsulation */
a8142a17
MW
847 if (!install_sp(this, sp, context, TRUE, TRUE,
848 &sp->policy_fwd_in))
6de78870
MW
849 {
850 enumerator->destroy(enumerator);
851 return FALSE;
852 }
853 /* outbound forward policy, to encapsulate */
a8142a17
MW
854 if (!install_sp(this, sp, context, FALSE, TRUE,
855 &sp->policy_fwd_out))
6de78870
MW
856 {
857 enumerator->destroy(enumerator);
858 return FALSE;
859 }
860 }
861 }
149fc48e
MW
862 }
863 enumerator->destroy(enumerator);
864
a8142a17
MW
865 if (context)
866 {
867 /* In tunnel mode, Windows does firewall filtering on decrypted but
868 * non-unwrapped packets: It sees them as IP-in-IP packets. When using
869 * a default-drop policy, we need to allow such packets explicitly. */
870 if (has_v4)
871 {
872 if (!install_ipip_ale(this, entry->local, entry->remote, context,
e1a44831
MW
873 TRUE, IPPROTO_IPIP, &entry->ip_ipv4_in))
874 {
875 return FALSE;
876 }
877 if (!install_ipip_ale(this, entry->local, entry->remote, NULL,
878 FALSE, IPPROTO_IPIP, &entry->ip_ipv4_out))
a8142a17
MW
879 {
880 return FALSE;
881 }
882 }
883 if (has_v6)
884 {
885 if (!install_ipip_ale(this, entry->local, entry->remote, context,
e1a44831
MW
886 TRUE, IPPROTO_IPV6, &entry->ip_ipv6_in))
887 {
888 return FALSE;
889 }
890 if (!install_ipip_ale(this, entry->local, entry->remote, NULL,
891 FALSE, IPPROTO_IPV6, &entry->ip_ipv6_out))
a8142a17
MW
892 {
893 return FALSE;
894 }
895 }
896 }
149fc48e
MW
897 return TRUE;
898}
899
900/**
901 * Convert a chunk_t to a WFP FWP_BYTE_BLOB
902 */
903static inline FWP_BYTE_BLOB chunk2blob(chunk_t chunk)
904{
905 return (FWP_BYTE_BLOB){
906 .size = chunk.len,
907 .data = chunk.ptr,
908 };
909}
910
911/**
912 * Convert an integrity_algorithm_t to a WFP IPSEC_AUTH_TRANFORM_ID0
913 */
914static bool alg2auth(integrity_algorithm_t alg,
915 IPSEC_SA_AUTH_INFORMATION0 *info)
916{
917 struct {
918 integrity_algorithm_t alg;
919 IPSEC_AUTH_TRANSFORM_ID0 transform;
920 } map[] = {
921 { AUTH_HMAC_MD5_96, IPSEC_AUTH_TRANSFORM_ID_HMAC_MD5_96 },
922 { AUTH_HMAC_SHA1_96, IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_1_96 },
923 { AUTH_HMAC_SHA2_256_128, IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_256_128},
924 { AUTH_AES_128_GMAC, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_128 },
925 { AUTH_AES_192_GMAC, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_192 },
926 { AUTH_AES_256_GMAC, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_256 },
927 };
928 int i;
929
930 for (i = 0; i < countof(map); i++)
931 {
932 if (map[i].alg == alg)
933 {
934 info->authTransform.authTransformId = map[i].transform;
935 return TRUE;
936 }
937 }
938 return FALSE;
939}
940
941/**
942 * Convert an encryption_algorithm_t to a WFP IPSEC_CIPHER_TRANFORM_ID0
943 */
944static bool alg2cipher(encryption_algorithm_t alg, int keylen,
945 IPSEC_SA_CIPHER_INFORMATION0 *info)
946{
947 struct {
948 encryption_algorithm_t alg;
949 int keylen;
950 IPSEC_CIPHER_TRANSFORM_ID0 transform;
951 } map[] = {
952 { ENCR_DES, 8, IPSEC_CIPHER_TRANSFORM_ID_CBC_DES },
953 { ENCR_3DES, 24, IPSEC_CIPHER_TRANSFORM_ID_CBC_3DES },
954 { ENCR_AES_CBC, 16, IPSEC_CIPHER_TRANSFORM_ID_AES_128 },
955 { ENCR_AES_CBC, 24, IPSEC_CIPHER_TRANSFORM_ID_AES_192 },
956 { ENCR_AES_CBC, 32, IPSEC_CIPHER_TRANSFORM_ID_AES_256 },
957 { ENCR_AES_GCM_ICV16, 20, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_128 },
958 { ENCR_AES_GCM_ICV16, 28, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_192 },
959 { ENCR_AES_GCM_ICV16, 36, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_256 },
960 };
961 int i;
962
963 for (i = 0; i < countof(map); i++)
964 {
965 if (map[i].alg == alg && map[i].keylen == keylen)
966 {
967 info->cipherTransform.cipherTransformId = map[i].transform;
968 return TRUE;
969 }
970 }
971 return FALSE;
972}
973
974/**
975 * Get the integrity algorithm used for an AEAD transform
976 */
977static integrity_algorithm_t encr2integ(encryption_algorithm_t encr, int keylen)
978{
979 struct {
980 encryption_algorithm_t encr;
981 int keylen;
982 integrity_algorithm_t integ;
983 } map[] = {
984 { ENCR_NULL_AUTH_AES_GMAC, 20, AUTH_AES_128_GMAC },
985 { ENCR_NULL_AUTH_AES_GMAC, 28, AUTH_AES_192_GMAC },
986 { ENCR_NULL_AUTH_AES_GMAC, 36, AUTH_AES_256_GMAC },
987 { ENCR_AES_GCM_ICV16, 20, AUTH_AES_128_GMAC },
988 { ENCR_AES_GCM_ICV16, 28, AUTH_AES_192_GMAC },
989 { ENCR_AES_GCM_ICV16, 36, AUTH_AES_256_GMAC },
990 };
991 int i;
992
993 for (i = 0; i < countof(map); i++)
994 {
995 if (map[i].encr == encr && map[i].keylen == keylen)
996 {
997 return map[i].integ;
998 }
999 }
1000 return AUTH_UNDEFINED;
1001}
1002
1003/**
4a8b8568 1004 * Install a single SA
149fc48e 1005 */
f351d9ef
MW
1006static bool install_sa(private_kernel_wfp_ipsec_t *this, entry_t *entry,
1007 bool inbound, sa_entry_t *sa, FWP_IP_VERSION version)
149fc48e
MW
1008{
1009 IPSEC_SA_AUTH_AND_CIPHER_INFORMATION0 info = {};
1010 IPSEC_SA0 ipsec = {
1011 .spi = ntohl(sa->spi),
1012 };
1013 IPSEC_SA_BUNDLE0 bundle = {
b3f90915
MW
1014 .lifetime = {
1015 .lifetimeSeconds = inbound ? entry->isa.lifetime
1016 : entry->osa.lifetime,
1017 },
149fc48e
MW
1018 .saList = &ipsec,
1019 .numSAs = 1,
1020 .ipVersion = version,
1021 };
1022 struct {
b12c53ce 1023 uint16_t alg;
149fc48e
MW
1024 chunk_t key;
1025 } integ = {}, encr = {};
1026 DWORD res;
1027
f351d9ef 1028 switch (sa->protocol)
149fc48e
MW
1029 {
1030 case IPPROTO_AH:
1031 ipsec.saTransformType = IPSEC_TRANSFORM_AH;
1032 ipsec.ahInformation = &info.saAuthInformation;
1033 integ.key = sa->integ.key;
1034 integ.alg = sa->integ.alg;
1035 break;
1036 case IPPROTO_ESP:
1037 if (sa->encr.alg == ENCR_NULL ||
1038 sa->encr.alg == ENCR_NULL_AUTH_AES_GMAC)
1039 {
1040 ipsec.saTransformType = IPSEC_TRANSFORM_ESP_AUTH;
1041 ipsec.espAuthInformation = &info.saAuthInformation;
1042 }
1043 else
1044 {
1045 ipsec.saTransformType = IPSEC_TRANSFORM_ESP_AUTH_AND_CIPHER;
1046 ipsec.espAuthAndCipherInformation = &info;
1047 encr.key = sa->encr.key;
1048 encr.alg = sa->encr.alg;
1049 }
1050 if (encryption_algorithm_is_aead(sa->encr.alg))
1051 {
1052 integ.alg = encr2integ(sa->encr.alg, sa->encr.key.len);
1053 integ.key = sa->encr.key;
1054 }
1055 else
1056 {
1057 integ.alg = sa->integ.alg;
1058 integ.key = sa->integ.key;
1059 }
1060 break;
1061 default:
1062 return FALSE;
1063 }
1064
1065 if (integ.alg)
1066 {
1067 info.saAuthInformation.authKey = chunk2blob(integ.key);
1068 if (!alg2auth(integ.alg, &info.saAuthInformation))
1069 {
1070 DBG1(DBG_KNL, "integrity algorithm %N not supported by WFP",
1071 integrity_algorithm_names, integ.alg);
1072 return FALSE;
1073 }
1074 }
1075 if (encr.alg)
1076 {
1077 info.saCipherInformation.cipherKey = chunk2blob(encr.key);
1078 if (!alg2cipher(encr.alg, encr.key.len, &info.saCipherInformation))
1079 {
1080 DBG1(DBG_KNL, "encryption algorithm %N not supported by WFP",
1081 encryption_algorithm_names, encr.alg);
1082 return FALSE;
1083 }
1084 }
1085
f351d9ef 1086 if (inbound)
149fc48e
MW
1087 {
1088 res = IPsecSaContextAddInbound0(this->handle, entry->sa_id, &bundle);
1089 }
1090 else
1091 {
a4f3b363 1092 bundle.flags |= IPSEC_SA_BUNDLE_FLAG_ASSUME_UDP_CONTEXT_OUTBOUND;
149fc48e
MW
1093 res = IPsecSaContextAddOutbound0(this->handle, entry->sa_id, &bundle);
1094 }
1095 if (res != ERROR_SUCCESS)
1096 {
1097 DBG1(DBG_KNL, "adding %sbound WFP SA failed: 0x%08x",
f351d9ef 1098 inbound ? "in" : "out", res);
149fc48e
MW
1099 return FALSE;
1100 }
1101 return TRUE;
1102}
1103
6aaa4327
MW
1104/**
1105 * Convert an IPv6 host address to WFP representation
1106 */
1107static void host2address6(host_t *host, void *out)
1108{
b12c53ce 1109 uint32_t *src, *dst = out;
6aaa4327 1110
b12c53ce 1111 src = (uint32_t*)host->get_address(host).ptr;
6aaa4327
MW
1112
1113 dst[0] = untoh32(&src[3]);
1114 dst[1] = untoh32(&src[2]);
1115 dst[2] = untoh32(&src[1]);
1116 dst[3] = untoh32(&src[0]);
1117}
1118
9b5c9564
MW
1119/**
1120 * Fill in traffic structure from entry addresses
1121 */
1122static bool hosts2traffic(private_kernel_wfp_ipsec_t *this,
1123 host_t *l, host_t *r, IPSEC_TRAFFIC1 *traffic)
1124{
1125 if (l->get_family(l) != r->get_family(r))
1126 {
1127 return FALSE;
1128 }
1129 switch (l->get_family(l))
1130 {
1131 case AF_INET:
1132 traffic->ipVersion = FWP_IP_VERSION_V4;
1133 traffic->localV4Address = untoh32(l->get_address(l).ptr);
1134 traffic->remoteV4Address = untoh32(r->get_address(r).ptr);
1135 return TRUE;
1136 case AF_INET6:
1137 traffic->ipVersion = FWP_IP_VERSION_V6;
6aaa4327
MW
1138 host2address6(l, &traffic->localV6Address);
1139 host2address6(r, &traffic->remoteV6Address);
9b5c9564
MW
1140 return TRUE;
1141 default:
1142 return FALSE;
1143 }
1144}
1145
149fc48e 1146/**
4a8b8568 1147 * Install SAs to the kernel
149fc48e 1148 */
4a8b8568
MW
1149static bool install_sas(private_kernel_wfp_ipsec_t *this, entry_t *entry,
1150 IPSEC_TRAFFIC_TYPE type)
149fc48e 1151{
9b5c9564 1152 IPSEC_TRAFFIC1 traffic = {
4a8b8568 1153 .trafficType = type,
149fc48e
MW
1154 };
1155 IPSEC_GETSPI1 spi = {
1156 .inboundIpsecTraffic = {
4a8b8568 1157 .trafficType = type,
149fc48e
MW
1158 },
1159 };
4b512803
MW
1160 enumerator_t *enumerator;
1161 sp_entry_t *sp;
149fc48e
MW
1162 DWORD res;
1163
4a8b8568
MW
1164 if (type == IPSEC_TRAFFIC_TYPE_TRANSPORT)
1165 {
4b512803
MW
1166 enumerator = array_create_enumerator(entry->sps);
1167 if (enumerator->enumerate(enumerator, &sp))
1168 {
1169 traffic.ipsecFilterId = sp->policy_out;
1170 spi.inboundIpsecTraffic.ipsecFilterId = sp->policy_in;
1171 }
1172 else
1173 {
1174 enumerator->destroy(enumerator);
1175 return FALSE;
1176 }
1177 enumerator->destroy(enumerator);
4a8b8568
MW
1178 }
1179 else
1180 {
1678f0a9
MW
1181 traffic.tunnelPolicyId = entry->provider;
1182 spi.inboundIpsecTraffic.tunnelPolicyId = entry->provider;
4a8b8568
MW
1183 }
1184
9b5c9564 1185 if (!hosts2traffic(this, entry->local, entry->remote, &traffic))
149fc48e 1186 {
9b5c9564 1187 return FALSE;
149fc48e
MW
1188 }
1189
9b5c9564
MW
1190 res = IPsecSaContextCreate1(this->handle, &traffic, NULL, NULL,
1191 &entry->sa_id);
149fc48e
MW
1192 if (res != ERROR_SUCCESS)
1193 {
1194 DBG1(DBG_KNL, "creating WFP SA context failed: 0x%08x", res);
1195 return FALSE;
1196 }
1197
149fc48e
MW
1198 memcpy(spi.inboundIpsecTraffic.localV6Address, traffic.localV6Address,
1199 sizeof(traffic.localV6Address));
1200 memcpy(spi.inboundIpsecTraffic.remoteV6Address, traffic.remoteV6Address,
1201 sizeof(traffic.remoteV6Address));
1202 spi.ipVersion = traffic.ipVersion;
1203
f351d9ef
MW
1204 res = IPsecSaContextSetSpi0(this->handle, entry->sa_id, &spi,
1205 ntohl(entry->isa.spi));
149fc48e
MW
1206 if (res != ERROR_SUCCESS)
1207 {
1208 DBG1(DBG_KNL, "setting WFP SA SPI failed: 0x%08x", res);
1209 IPsecSaContextDeleteById0(this->handle, entry->sa_id);
1210 entry->sa_id = 0;
1211 return FALSE;
1212 }
1213
f351d9ef
MW
1214 if (!install_sa(this, entry, TRUE, &entry->isa, spi.ipVersion) ||
1215 !install_sa(this, entry, FALSE, &entry->osa, spi.ipVersion))
149fc48e 1216 {
f351d9ef
MW
1217 IPsecSaContextDeleteById0(this->handle, entry->sa_id);
1218 entry->sa_id = 0;
1219 return FALSE;
149fc48e 1220 }
149fc48e 1221
1987b709
MW
1222 if (entry->encap)
1223 {
1224 IPSEC_V4_UDP_ENCAPSULATION0 encap = {
1225 .localUdpEncapPort = entry->local->get_port(entry->local),
1226 .remoteUdpEncapPort = entry->remote->get_port(entry->remote),
1227 };
1228 IPSEC_SA_CONTEXT1 *ctx;
1229
1230 res = IPsecSaContextGetById1(this->handle, entry->sa_id, &ctx);
1231 if (res != ERROR_SUCCESS)
1232 {
1233 DBG1(DBG_KNL, "getting WFP SA for UDP encap failed: 0x%08x", res);
1234 IPsecSaContextDeleteById0(this->handle, entry->sa_id);
1235 entry->sa_id = 0;
1236 return FALSE;
1237 }
1238 ctx->inboundSa->udpEncapsulation = &encap;
1239 ctx->outboundSa->udpEncapsulation = &encap;
1240
1241 res = IPsecSaContextUpdate0(this->handle,
1242 IPSEC_SA_DETAILS_UPDATE_UDP_ENCAPSULATION, ctx);
1243 FwpmFreeMemory0((void**)&ctx);
1244 if (res != ERROR_SUCCESS)
1245 {
1246 DBG1(DBG_KNL, "enable WFP UDP encap failed: 0x%08x", res);
1247 IPsecSaContextDeleteById0(this->handle, entry->sa_id);
1248 entry->sa_id = 0;
1249 return FALSE;
1250 }
1251 }
1252
149fc48e
MW
1253 return TRUE;
1254}
1255
1256/**
1257 * Install a transport mode SA/SP set to the kernel
1258 */
1259static bool install_transport(private_kernel_wfp_ipsec_t *this, entry_t *entry)
1260{
6de78870 1261 if (install_sps(this, entry, NULL) &&
4a8b8568 1262 install_sas(this, entry, IPSEC_TRAFFIC_TYPE_TRANSPORT))
149fc48e
MW
1263 {
1264 return TRUE;
1265 }
4a8b8568
MW
1266 cleanup_policies(this, entry);
1267 return FALSE;
1268}
1269
1270/**
1271 * Generate a new GUID, random
1272 */
1273static bool generate_guid(private_kernel_wfp_ipsec_t *this, GUID *guid)
1274{
1275 bool ok;
1276 rng_t *rng;
1277
1278 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
1279 if (!rng)
149fc48e 1280 {
4a8b8568 1281 return FALSE;
149fc48e 1282 }
b12c53ce 1283 ok = rng->get_bytes(rng, sizeof(GUID), (uint8_t*)guid);
4a8b8568
MW
1284 rng->destroy(rng);
1285 return ok;
1286}
1287
1288/**
6de78870 1289 * Register a dummy tunnel provider to associate tunnel filters to
4a8b8568 1290 */
6de78870
MW
1291static bool add_tunnel_provider(private_kernel_wfp_ipsec_t *this,
1292 entry_t *entry, GUID *guid, UINT64 *luid)
4a8b8568 1293{
4a8b8568
MW
1294 DWORD res;
1295
1296 IPSEC_AUTH_TRANSFORM0 transform = {
1297 /* Create any valid proposal. This is actually not used, as we
1298 * don't create an SA from this information. */
1299 .authTransformId = IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_1_96,
1300 };
1301 IPSEC_SA_TRANSFORM0 transforms = {
1302 .ipsecTransformType = IPSEC_TRANSFORM_ESP_AUTH,
1303 .espAuthTransform = &transform,
1304 };
1305 IPSEC_PROPOSAL0 proposal = {
1306 .lifetime = {
1307 /* We need a valid lifetime, even if we don't create any SA
1308 * from these values. Pick some values accepted. */
1309 .lifetimeSeconds = 0xFFFF,
1310 .lifetimeKilobytes = 0xFFFFFFFF,
1311 .lifetimePackets = 0xFFFFFFFF,
1312 },
1313 .numSaTransforms = 1,
1314 .saTransforms = &transforms,
1315 };
1316 IPSEC_TUNNEL_POLICY0 policy = {
1317 .numIpsecProposals = 1,
1318 .ipsecProposals = &proposal,
1319 .saIdleTimeout = {
1320 /* not used, set to lifetime for maximum */
1321 .idleTimeoutSeconds = proposal.lifetime.lifetimeSeconds,
1322 .idleTimeoutSecondsFailOver = proposal.lifetime.lifetimeSeconds,
1323 },
1324 };
1678f0a9 1325 FWPM_PROVIDER_CONTEXT0 qm = {
4a8b8568 1326 .displayData = {
6de78870 1327 .name = L"charon tunnel provider",
4a8b8568
MW
1328 },
1329 .providerKey = (GUID*)&this->provider.providerKey,
1330 .type = FWPM_IPSEC_IKE_QM_TUNNEL_CONTEXT,
1331 .ikeQmTunnelPolicy = &policy,
1332 };
1333
1334 switch (entry->local->get_family(entry->local))
149fc48e 1335 {
4a8b8568
MW
1336 case AF_INET:
1337 policy.tunnelEndpoints.ipVersion = FWP_IP_VERSION_V4;
1338 policy.tunnelEndpoints.localV4Address =
1339 untoh32(entry->local->get_address(entry->local).ptr);
1340 policy.tunnelEndpoints.remoteV4Address =
1341 untoh32(entry->remote->get_address(entry->remote).ptr);
1342 break;
1343 case AF_INET6:
1344 policy.tunnelEndpoints.ipVersion = FWP_IP_VERSION_V6;
6de78870
MW
1345 host2address6(entry->local, &policy.tunnelEndpoints.localV6Address);
1346 host2address6(entry->remote, &policy.tunnelEndpoints.remoteV6Address);
4a8b8568
MW
1347 break;
1348 default:
1349 return FALSE;
1350 }
1351
1352 if (!generate_guid(this, &qm.providerContextKey))
1353 {
1354 return FALSE;
1355 }
1356
6de78870 1357 res = FwpmProviderContextAdd0(this->handle, &qm, NULL, luid);
4a8b8568
MW
1358 if (res != ERROR_SUCCESS)
1359 {
1678f0a9 1360 DBG1(DBG_KNL, "adding provider context failed: 0x%08x", res);
4a8b8568
MW
1361 return FALSE;
1362 }
6de78870
MW
1363 *guid = qm.providerContextKey;
1364 return TRUE;
1365}
4a8b8568 1366
6de78870
MW
1367/**
1368 * Install tunnel mode SPs to the kernel
1369 */
1370static bool install_tunnel_sps(private_kernel_wfp_ipsec_t *this, entry_t *entry)
1371{
1372 GUID guid;
1373
1374 if (!add_tunnel_provider(this, entry, &guid, &entry->provider))
1375 {
1376 return FALSE;
1377 }
1378 if (!install_sps(this, entry, &guid))
4a8b8568 1379 {
4a8b8568
MW
1380 return FALSE;
1381 }
4a8b8568
MW
1382 return TRUE;
1383}
1384
b714746e
MW
1385/**
1386 * Reduce refcount, or uninstall a route if all refs gone
1387 */
1388static bool uninstall_route(private_kernel_wfp_ipsec_t *this,
b12c53ce 1389 host_t *dst, uint8_t mask, host_t *src, host_t *gtw)
b714746e
MW
1390{
1391 route_t *route, key = {
1392 .dst = dst,
1393 .mask = mask,
1394 .src = src,
1395 };
1396 char *name;
1397 bool res = FALSE;
1398
1399 this->mutex->lock(this->mutex);
1400 route = this->routes->get(this->routes, &key);
1401 if (route)
1402 {
1403 if (--route->refs == 0)
1404 {
8394ea2a 1405 if (charon->kernel->get_interface(charon->kernel, src, &name))
b714746e 1406 {
8394ea2a 1407 res = charon->kernel->del_route(charon->kernel,
09f4bccf
NK
1408 dst->get_address(dst), mask, gtw, src,
1409 name, FALSE) == SUCCESS;
b714746e
MW
1410 free(name);
1411 }
1412 route = this->routes->remove(this->routes, route);
1413 if (route)
1414 {
1415 destroy_route(route);
1416 }
1417 }
1418 else
1419 {
1420 res = TRUE;
1421 }
1422 }
1423 this->mutex->unlock(this->mutex);
1424
1425 return res;
1426}
1427
1428/**
1429 * Install a single route, or refcount if exists
1430 */
1431static bool install_route(private_kernel_wfp_ipsec_t *this,
b12c53ce 1432 host_t *dst, uint8_t mask, host_t *src, host_t *gtw)
b714746e
MW
1433{
1434 route_t *route, key = {
1435 .dst = dst,
1436 .mask = mask,
1437 .src = src,
1438 };
1439 char *name;
1440 bool res = FALSE;
1441
1442 this->mutex->lock(this->mutex);
1443 route = this->routes->get(this->routes, &key);
1444 if (route)
1445 {
1446 route->refs++;
1447 res = TRUE;
1448 }
1449 else
1450 {
8394ea2a 1451 if (charon->kernel->get_interface(charon->kernel, src, &name))
b714746e 1452 {
09f4bccf
NK
1453 if (charon->kernel->add_route(charon->kernel, dst->get_address(dst),
1454 mask, gtw, src, name, FALSE) == SUCCESS)
b714746e
MW
1455 {
1456 INIT(route,
1457 .dst = dst->clone(dst),
1458 .mask = mask,
1459 .src = src->clone(src),
1460 .gtw = gtw ? gtw->clone(gtw) : NULL,
1461 .refs = 1,
1462 );
1463 route = this->routes->put(this->routes, route, route);
1464 if (route)
1465 {
1466 destroy_route(route);
1467 }
1468 res = TRUE;
1469 }
1470 free(name);
1471 }
1472 }
1473 this->mutex->unlock(this->mutex);
1474
1475 return res;
1476}
1477
e36d1d41
MW
1478/**
1479 * (Un)-install a single route
1480 */
1481static bool manage_route(private_kernel_wfp_ipsec_t *this,
1482 host_t *local, host_t *remote,
1483 traffic_selector_t *src_ts, traffic_selector_t *dst_ts,
1484 bool add)
1485{
1486 host_t *src, *dst, *gtw;
b12c53ce 1487 uint8_t mask;
e36d1d41
MW
1488 bool done;
1489
1490 if (!dst_ts->to_subnet(dst_ts, &dst, &mask))
1491 {
1492 return FALSE;
1493 }
8394ea2a
TB
1494 if (charon->kernel->get_address_by_ts(charon->kernel, src_ts, &src,
1495 NULL) != SUCCESS)
e36d1d41
MW
1496 {
1497 dst->destroy(dst);
1498 return FALSE;
1499 }
99a57aa5 1500 gtw = charon->kernel->get_nexthop(charon->kernel, remote, -1, local, NULL);
e36d1d41
MW
1501 if (add)
1502 {
1503 done = install_route(this, dst, mask, src, gtw);
1504 }
1505 else
1506 {
1507 done = uninstall_route(this, dst, mask, src, gtw);
1508 }
1509 dst->destroy(dst);
1510 src->destroy(src);
1511 DESTROY_IF(gtw);
1512
1513 if (!done)
1514 {
1515 DBG1(DBG_KNL, "%sinstalling route for policy %R === %R failed",
1516 add ? "" : "un", src_ts, dst_ts);
1517 }
1518 return done;
1519}
1520
b714746e
MW
1521/**
1522 * (Un)-install routes for IPsec policies
1523 */
1524static bool manage_routes(private_kernel_wfp_ipsec_t *this, entry_t *entry,
1525 bool add)
1526{
1527 enumerator_t *enumerator;
b714746e 1528 sp_entry_t *sp;
b714746e
MW
1529
1530 enumerator = array_create_enumerator(entry->sps);
1531 while (enumerator->enumerate(enumerator, &sp))
1532 {
4b512803
MW
1533 if (add && sp->route)
1534 {
1535 continue;
1536 }
1537 if (!add && !sp->route)
1538 {
1539 continue;
1540 }
e36d1d41
MW
1541 if (manage_route(this, entry->local, entry->remote,
1542 sp->src, sp->dst, add))
b714746e 1543 {
e36d1d41 1544 sp->route = add;
b714746e 1545 }
b714746e
MW
1546 }
1547 enumerator->destroy(enumerator);
1548
1549 return TRUE;
1550}
1551
4a8b8568
MW
1552/**
1553 * Install a tunnel mode SA/SP set to the kernel
1554 */
1555static bool install_tunnel(private_kernel_wfp_ipsec_t *this, entry_t *entry)
1556{
1557 if (install_tunnel_sps(this, entry) &&
b714746e 1558 manage_routes(this, entry, TRUE) &&
4a8b8568
MW
1559 install_sas(this, entry, IPSEC_TRAFFIC_TYPE_TUNNEL))
1560 {
1561 return TRUE;
149fc48e 1562 }
4a8b8568 1563 cleanup_policies(this, entry);
149fc48e
MW
1564 return FALSE;
1565}
1566
1567/**
1568 * Install a SA/SP set to the kernel
1569 */
1570static bool install(private_kernel_wfp_ipsec_t *this, entry_t *entry)
1571{
1572 switch (entry->mode)
1573 {
1574 case MODE_TRANSPORT:
1575 return install_transport(this, entry);
1576 case MODE_TUNNEL:
4a8b8568 1577 return install_tunnel(this, entry);
149fc48e
MW
1578 case MODE_BEET:
1579 default:
1580 return FALSE;
1581 }
1582}
1583
c6f189e4
MW
1584/**
1585 * Installed trap entry
1586 */
1587typedef struct {
1588 /** reqid this trap is installed for */
b12c53ce 1589 uint32_t reqid;
6de78870
MW
1590 /** is this a forward policy trap for tunnel mode? */
1591 bool fwd;
78bde29a
MW
1592 /** do we have installed a route for this trap policy? */
1593 bool route;
1594 /** local address of associated route */
1595 host_t *local;
1596 /** remote address of associated route */
1597 host_t *remote;
6de78870
MW
1598 /** src traffic selector */
1599 traffic_selector_t *src;
1600 /** dst traffic selector */
1601 traffic_selector_t *dst;
c6f189e4
MW
1602 /** LUID of installed tunnel policy filter */
1603 UINT64 filter_id;
1604} trap_t;
1605
1606/**
1607 * Destroy a trap entry
1608 */
1609static void destroy_trap(trap_t *this)
1610{
78bde29a
MW
1611 this->local->destroy(this->local);
1612 this->remote->destroy(this->remote);
6de78870
MW
1613 this->src->destroy(this->src);
1614 this->dst->destroy(this->dst);
c6f189e4
MW
1615 free(this);
1616}
1617
1618/**
1619 * Hashtable equals function for traps
1620 */
1621static bool equals_trap(trap_t *a, trap_t *b)
1622{
1623 return a->filter_id == b->filter_id;
1624}
1625
1626/**
1627 * Hashtable hash function for traps
1628 */
1629static u_int hash_trap(trap_t *trap)
1630{
1631 return chunk_hash(chunk_from_thing(trap->filter_id));
1632}
1633
1634/**
1635 * Send an acquire for an installed trap filter
1636 */
1637static void acquire(private_kernel_wfp_ipsec_t *this, UINT64 filter_id,
1638 traffic_selector_t *src, traffic_selector_t *dst)
1639{
df7cd8a2
TB
1640 kernel_acquire_data_t data = {
1641 .cpu = CPU_ID_MAX,
1642 };
b12c53ce 1643 uint32_t reqid = 0;
c6f189e4
MW
1644 trap_t *trap, key = {
1645 .filter_id = filter_id,
1646 };
1647
1648 this->mutex->lock(this->mutex);
1649 trap = this->traps->get(this->traps, &key);
1650 if (trap)
1651 {
1652 reqid = trap->reqid;
1653 }
1654 this->mutex->unlock(this->mutex);
1655
1656 if (reqid)
1657 {
3b699c72
TB
1658 data.src = src ? src->clone(src) : NULL;
1659 data.dst = dst ? dst->clone(dst) : NULL;
1660
1661 charon->kernel->acquire(charon->kernel, reqid, &data);
1662
1663 DESTROY_IF(data.src);
1664 DESTROY_IF(data.dst);
c6f189e4
MW
1665 }
1666}
1667
1668/**
1669 * Create a single host traffic selector from an FWP address definition
1670 */
1671static traffic_selector_t *addr2ts(FWP_IP_VERSION version, void *data,
b12c53ce 1672 uint8_t protocol, uint16_t from_port, uint16_t to_port)
c6f189e4
MW
1673{
1674 ts_type_t type;
1675 UINT32 ints[4];
1676 chunk_t addr;
1677
1678 switch (version)
1679 {
1680 case FWP_IP_VERSION_V4:
1681 ints[0] = untoh32(data);
1682 addr = chunk_from_thing(ints[0]);
1683 type = TS_IPV4_ADDR_RANGE;
1684 break;
1685 case FWP_IP_VERSION_V6:
1686 ints[3] = untoh32(data);
1687 ints[2] = untoh32(data + 4);
1688 ints[1] = untoh32(data + 8);
1689 ints[0] = untoh32(data + 12);
1690 addr = chunk_from_thing(ints);
1691 type = TS_IPV6_ADDR_RANGE;
1692 break;
1693 default:
1694 return NULL;
1695 }
1696 return traffic_selector_create_from_bytes(protocol, type, addr, from_port,
1697 addr, to_port);
1698}
1699
28683140
MW
1700/**
1701 * FwpmNetEventSubscribe0() callback
1702 */
cab59c73 1703static void WINAPI event_callback(void *user, const FWPM_NET_EVENT1 *event)
28683140 1704{
cab59c73 1705 private_kernel_wfp_ipsec_t *this = user;
c6f189e4 1706 traffic_selector_t *local = NULL, *remote = NULL;
b12c53ce
AS
1707 uint8_t protocol = 0;
1708 uint16_t from_local = 0, to_local = 65535;
1709 uint16_t from_remote = 0, to_remote = 65535;
c6f189e4
MW
1710
1711 if ((event->header.flags & FWPM_NET_EVENT_FLAG_LOCAL_ADDR_SET) &&
1712 (event->header.flags & FWPM_NET_EVENT_FLAG_REMOTE_ADDR_SET))
1713 {
1714 if (event->header.flags & FWPM_NET_EVENT_FLAG_LOCAL_PORT_SET)
1715 {
1716 from_local = to_local = event->header.localPort;
1717 }
1718 if (event->header.flags & FWPM_NET_EVENT_FLAG_LOCAL_PORT_SET)
1719 {
1720 from_remote = to_remote = event->header.remotePort;
1721 }
1722 if (event->header.flags & FWPM_NET_EVENT_FLAG_IP_PROTOCOL_SET)
1723 {
1724 protocol = event->header.ipProtocol;
1725 }
1726
1727 local = addr2ts(event->header.ipVersion,
1728 (void*)&event->header.localAddrV6,
1729 protocol, from_local, to_local);
1730 remote = addr2ts(event->header.ipVersion,
1731 (void*)&event->header.remoteAddrV6,
1732 protocol, from_remote, to_remote);
1733 }
1734
1735 switch (event->type)
1736 {
1737 case FWPM_NET_EVENT_TYPE_CLASSIFY_DROP:
1738 acquire(this, event->classifyDrop->filterId, local, remote);
1739 break;
1740 case FWPM_NET_EVENT_TYPE_IKEEXT_MM_FAILURE:
4d48dfd6
MW
1741 DBG1(DBG_KNL, "WFP MM failure: %R === %R, 0x%08x, filterId %llu",
1742 local, remote, event->ikeMmFailure->failureErrorCode,
1743 event->ikeMmFailure->mmFilterId);
1744 break;
c6f189e4 1745 case FWPM_NET_EVENT_TYPE_IKEEXT_QM_FAILURE:
4d48dfd6
MW
1746 DBG1(DBG_KNL, "WFP QM failure: %R === %R, 0x%08x, filterId %llu",
1747 local, remote, event->ikeQmFailure->failureErrorCode,
1748 event->ikeQmFailure->qmFilterId);
1749 break;
c6f189e4 1750 case FWPM_NET_EVENT_TYPE_IKEEXT_EM_FAILURE:
4d48dfd6
MW
1751 DBG1(DBG_KNL, "WFP EM failure: %R === %R, 0x%08x, filterId %llu",
1752 local, remote, event->ikeEmFailure->failureErrorCode,
1753 event->ikeEmFailure->qmFilterId);
1754 break;
c6f189e4 1755 case FWPM_NET_EVENT_TYPE_IPSEC_KERNEL_DROP:
c7d30c2a
MW
1756 DBG1(DBG_KNL, "IPsec kernel drop: %R === %R, error 0x%08x, "
1757 "SPI 0x%08x, %s filterId %llu", local, remote,
1758 event->ipsecDrop->failureStatus, event->ipsecDrop->spi,
1759 event->ipsecDrop->direction ? "in" : "out",
1760 event->ipsecDrop->filterId);
1761 break;
c6f189e4
MW
1762 case FWPM_NET_EVENT_TYPE_IPSEC_DOSP_DROP:
1763 default:
1764 break;
1765 }
1766
1767 DESTROY_IF(local);
1768 DESTROY_IF(remote);
28683140
MW
1769}
1770
1771/**
1772 * Register for net events
1773 */
1774static bool register_events(private_kernel_wfp_ipsec_t *this)
1775{
1776 FWPM_NET_EVENT_SUBSCRIPTION0 subscription = {};
1777 DWORD res;
1778
1779 res = FwpmNetEventSubscribe0(this->handle, &subscription,
cab59c73 1780 event_callback, this, &this->event);
28683140
MW
1781 if (res != ERROR_SUCCESS)
1782 {
1783 DBG1(DBG_KNL, "registering for WFP events failed: 0x%08x", res);
1784 return FALSE;
1785 }
1786 return TRUE;
1787}
1788
c6f189e4
MW
1789/**
1790 * Install a trap policy to kernel
1791 */
1792static bool install_trap(private_kernel_wfp_ipsec_t *this, trap_t *trap)
1793{
1794 FWPM_FILTER_CONDITION0 *conds = NULL;
1795 int count = 0;
1796 DWORD res;
6de78870 1797 const GUID *starget, *dtarget;
c6f189e4
MW
1798 UINT64 weight = 0x000000000000ff00;
1799 FWPM_FILTER0 filter = {
1800 .displayData = {
1801 .name = L"charon IPsec trap",
1802 },
1803 .action = {
1804 .type = FWP_ACTION_BLOCK,
1805 },
1806 .weight = {
1807 .type = FWP_UINT64,
1808 .uint64 = &weight,
1809 },
1810 };
1811
6de78870 1812 if (trap->fwd)
c6f189e4 1813 {
6de78870
MW
1814 if (trap->src->get_type(trap->src) == TS_IPV4_ADDR_RANGE)
1815 {
1816 filter.layerKey = FWPM_LAYER_IPFORWARD_V4;
1817 }
1818 else
1819 {
1820 filter.layerKey = FWPM_LAYER_IPFORWARD_V6;
1821 }
1822 starget = &FWPM_CONDITION_IP_SOURCE_ADDRESS;
1823 dtarget = &FWPM_CONDITION_IP_DESTINATION_ADDRESS;
1824 }
1825 else
1826 {
1827 if (trap->src->get_type(trap->src) == TS_IPV4_ADDR_RANGE)
1828 {
c6f189e4 1829 filter.layerKey = FWPM_LAYER_OUTBOUND_TRANSPORT_V4;
6de78870
MW
1830 }
1831 else
1832 {
c6f189e4 1833 filter.layerKey = FWPM_LAYER_OUTBOUND_TRANSPORT_V6;
6de78870
MW
1834 }
1835 starget = &FWPM_CONDITION_IP_LOCAL_ADDRESS;
1836 dtarget = &FWPM_CONDITION_IP_REMOTE_ADDRESS;
c6f189e4
MW
1837 }
1838
6de78870
MW
1839 if (!ts2condition(trap->src, starget, &conds, &count) ||
1840 !ts2condition(trap->dst, dtarget, &conds, &count))
c6f189e4
MW
1841 {
1842 free_conditions(conds, count);
1843 return FALSE;
1844 }
1845
1846 filter.numFilterConditions = count;
1847 filter.filterCondition = conds;
1848
1849 res = FwpmFilterAdd0(this->handle, &filter, NULL, &trap->filter_id);
1850 free_conditions(conds, count);
1851 if (res != ERROR_SUCCESS)
1852 {
6de78870 1853 DBG1(DBG_KNL, "installing WFP trap filter failed: 0x%08x", res);
c6f189e4
MW
1854 return FALSE;
1855 }
1856 return TRUE;
1857}
1858
1859/**
1860 * Uninstall a trap policy from kernel
1861 */
1862static bool uninstall_trap(private_kernel_wfp_ipsec_t *this, trap_t *trap)
1863{
1864 DWORD res;
1865
1866 res = FwpmFilterDeleteById0(this->handle, trap->filter_id);
1867 if (res != ERROR_SUCCESS)
1868 {
6de78870 1869 DBG1(DBG_KNL, "uninstalling WFP trap filter failed: 0x%08x", res);
c6f189e4
MW
1870 return FALSE;
1871 }
1872 return TRUE;
1873}
1874
1875/**
1876 * Create and install a new trap entry
1877 */
6de78870 1878static bool add_trap(private_kernel_wfp_ipsec_t *this,
b12c53ce 1879 uint32_t reqid, bool fwd, host_t *local, host_t *remote,
6de78870 1880 traffic_selector_t *src, traffic_selector_t *dst)
c6f189e4
MW
1881{
1882 trap_t *trap;
1883
1884 INIT(trap,
1885 .reqid = reqid,
6de78870
MW
1886 .fwd = fwd,
1887 .src = src->clone(src),
1888 .dst = dst->clone(dst),
78bde29a
MW
1889 .local = local->clone(local),
1890 .remote = remote->clone(remote),
c6f189e4
MW
1891 );
1892
1893 if (!install_trap(this, trap))
1894 {
1895 destroy_trap(trap);
1896 return FALSE;
1897 }
78bde29a
MW
1898
1899 trap->route = manage_route(this, local, remote, src, dst, TRUE);
1900
c6f189e4
MW
1901 this->mutex->lock(this->mutex);
1902 this->traps->put(this->traps, trap, trap);
1903 this->mutex->unlock(this->mutex);
1904 return TRUE;
1905}
1906
1907/**
1908 * Uninstall and remove a new trap entry
1909 */
6de78870 1910static bool remove_trap(private_kernel_wfp_ipsec_t *this,
b12c53ce 1911 uint32_t reqid, bool fwd,
6de78870 1912 traffic_selector_t *src, traffic_selector_t *dst)
c6f189e4
MW
1913{
1914 enumerator_t *enumerator;
1915 trap_t *trap, *found = NULL;
1916
1917 this->mutex->lock(this->mutex);
1918 enumerator = this->traps->create_enumerator(this->traps);
1919 while (enumerator->enumerate(enumerator, NULL, &trap))
1920 {
1921 if (reqid == trap->reqid &&
6de78870
MW
1922 fwd == trap->fwd &&
1923 src->equals(src, trap->src) &&
1924 dst->equals(dst, trap->dst))
c6f189e4
MW
1925 {
1926 this->traps->remove_at(this->traps, enumerator);
1927 found = trap;
1928 break;
1929 }
1930 }
1931 enumerator->destroy(enumerator);
1932 this->mutex->unlock(this->mutex);
1933
1934 if (found)
1935 {
78bde29a
MW
1936 if (trap->route)
1937 {
1938 trap->route = !manage_route(this, trap->local, trap->remote,
1939 src, dst, FALSE);
1940 }
c6f189e4
MW
1941 uninstall_trap(this, found);
1942 destroy_trap(found);
1943 return TRUE;
1944 }
1945 return FALSE;
1946}
1947
8d91eee3
MW
1948METHOD(kernel_ipsec_t, get_features, kernel_feature_t,
1949 private_kernel_wfp_ipsec_t *this)
1950{
b9349298 1951 return KERNEL_ESP_V3_TFC | KERNEL_NO_POLICY_UPDATES;
8d91eee3
MW
1952}
1953
bbe42a1f
MW
1954/**
1955 * Initialize seeds for SPI generation
1956 */
1957static bool init_spi(private_kernel_wfp_ipsec_t *this)
1958{
1959 bool ok = TRUE;
1960 rng_t *rng;
1961
1962 rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
1963 if (!rng)
1964 {
1965 return FALSE;
1966 }
b12c53ce 1967 ok = rng->get_bytes(rng, sizeof(this->nextspi), (uint8_t*)&this->nextspi);
bbe42a1f
MW
1968 if (ok)
1969 {
b12c53ce 1970 ok = rng->get_bytes(rng, sizeof(this->mixspi), (uint8_t*)&this->mixspi);
bbe42a1f
MW
1971 }
1972 rng->destroy(rng);
1973 return ok;
1974}
1975
1976/**
1977 * Map an integer x with a one-to-one function using quadratic residues.
1978 */
1979static u_int permute(u_int x, u_int p)
1980{
1981 u_int qr;
1982
1983 x = x % p;
b12c53ce 1984 qr = ((uint64_t)x * x) % p;
bbe42a1f
MW
1985 if (x <= p / 2)
1986 {
1987 return qr;
1988 }
1989 return p - qr;
1990}
1991
8d91eee3
MW
1992METHOD(kernel_ipsec_t, get_spi, status_t,
1993 private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
b12c53ce 1994 uint8_t protocol, uint32_t *spi)
8d91eee3 1995{
2db6d5b8 1996 /* To avoid sequential SPIs, we use a one-to-one permutation function on
bbe42a1f
MW
1997 * an incrementing counter, that is a full period PRNG for the range we
1998 * allocate SPIs in. We add some randomness using a fixed XOR and start
1999 * the counter at random position. This is not cryptographically safe,
2000 * but that is actually not required.
2001 * The selected prime should be smaller than the range we allocate SPIs
2002 * in, and it must satisfy p % 4 == 3 to map x > p/2 using p - qr. */
2003 static const u_int p = 268435399, offset = 0xc0000000;
2004
2005 *spi = htonl(offset + permute(ref_get(&this->nextspi) ^ this->mixspi, p));
96ab7a80 2006 return SUCCESS;
8d91eee3
MW
2007}
2008
2009METHOD(kernel_ipsec_t, get_cpi, status_t,
2010 private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
b12c53ce 2011 uint16_t *cpi)
8d91eee3
MW
2012{
2013 return NOT_SUPPORTED;
2014}
2015
af098b50
MW
2016/**
2017 * Data for an expire callback job
2018 */
2019typedef struct {
2020 /* backref to kernel backend */
2021 private_kernel_wfp_ipsec_t *this;
2022 /* SPI of expiring SA */
b12c53ce 2023 uint32_t spi;
af098b50
MW
2024 /* destination address of expiring SA */
2025 host_t *dst;
2026 /* is this a hard expire, or a rekey request? */
2027 bool hard;
2028} expire_data_t;
2029
2030/**
2031 * Clean up expire data
2032 */
2033static void expire_data_destroy(expire_data_t *data)
2034{
2035 data->dst->destroy(data->dst);
2036 free(data);
2037}
2038
2039/**
2040 * Callback job for SA expiration
2041 */
2042static job_requeue_t expire_job(expire_data_t *data)
2043{
2044 private_kernel_wfp_ipsec_t *this = data->this;
b12c53ce 2045 uint8_t protocol;
f81a9497 2046 entry_t *entry = NULL;
af098b50
MW
2047 sa_entry_t key = {
2048 .spi = data->spi,
2049 .dst = data->dst,
2050 };
2051
2052 if (data->hard)
2053 {
2054 this->mutex->lock(this->mutex);
2055 entry = this->isas->remove(this->isas, &key);
2056 this->mutex->unlock(this->mutex);
2057 if (entry)
2058 {
2059 protocol = entry->isa.protocol;
af098b50
MW
2060 if (entry->osa.dst)
2061 {
2062 key.dst = entry->osa.dst;
2063 key.spi = entry->osa.spi;
2064 this->osas->remove(this->osas, &key);
2065 }
2066 entry_destroy(this, entry);
2067 }
2068 }
2069 else
2070 {
2071 this->mutex->lock(this->mutex);
2072 entry = this->isas->get(this->isas, &key);
2073 if (entry)
2074 {
2075 protocol = entry->isa.protocol;
af098b50
MW
2076 }
2077 this->mutex->unlock(this->mutex);
2078 }
2079
f81a9497 2080 if (entry)
af098b50 2081 {
8394ea2a
TB
2082 charon->kernel->expire(charon->kernel, protocol, data->spi, data->dst,
2083 data->hard);
af098b50
MW
2084 }
2085
2086 return JOB_REQUEUE_NONE;
2087}
2088
2089/**
2090 * Schedule an expire event for an SA
2091 */
b12c53ce
AS
2092static void schedule_expire(private_kernel_wfp_ipsec_t *this, uint32_t spi,
2093 host_t *dst, uint32_t lifetime, bool hard)
af098b50
MW
2094{
2095 expire_data_t *data;
2096
2097 INIT(data,
2098 .this = this,
2099 .spi = spi,
2100 .dst = dst->clone(dst),
2101 .hard = hard,
2102 );
2103
2104 lib->scheduler->schedule_job(lib->scheduler, (job_t*)
2105 callback_job_create((void*)expire_job, data,
2106 (void*)expire_data_destroy, NULL),
2107 lifetime);
2108}
2109
8d91eee3 2110METHOD(kernel_ipsec_t, add_sa, status_t,
89da06ac
TB
2111 private_kernel_wfp_ipsec_t *this, kernel_ipsec_sa_id_t *id,
2112 kernel_ipsec_add_sa_t *data)
8d91eee3 2113{
96ab7a80
MW
2114 host_t *local, *remote;
2115 entry_t *entry;
96ab7a80 2116
89da06ac 2117 if (data->inbound)
96ab7a80 2118 {
f351d9ef 2119 /* comes first, create new entry */
89da06ac
TB
2120 local = id->dst->clone(id->dst);
2121 remote = id->src->clone(id->src);
f351d9ef
MW
2122
2123 INIT(entry,
89da06ac 2124 .reqid = data->reqid,
f351d9ef 2125 .isa = {
89da06ac 2126 .spi = id->spi,
f351d9ef 2127 .dst = local,
89da06ac
TB
2128 .protocol = id->proto,
2129 .lifetime = data->lifetime->time.life,
f351d9ef 2130 .encr = {
89da06ac
TB
2131 .alg = data->enc_alg,
2132 .key = chunk_clone(data->enc_key),
f351d9ef
MW
2133 },
2134 .integ = {
89da06ac
TB
2135 .alg = data->int_alg,
2136 .key = chunk_clone(data->int_key),
f351d9ef
MW
2137 },
2138 },
2139 .sps = array_create(0, 0),
2140 .local = local,
2141 .remote = remote,
89da06ac
TB
2142 .mode = data->mode,
2143 .encap = data->encap,
f351d9ef
MW
2144 );
2145
89da06ac 2146 if (data->lifetime->time.life)
af098b50 2147 {
89da06ac
TB
2148 schedule_expire(this, id->spi, local,
2149 data->lifetime->time.life, TRUE);
af098b50 2150 }
89da06ac
TB
2151 if (data->lifetime->time.rekey &&
2152 data->lifetime->time.rekey != data->lifetime->time.life)
af098b50 2153 {
89da06ac
TB
2154 schedule_expire(this, id->spi, local,
2155 data->lifetime->time.rekey, FALSE);
af098b50
MW
2156 }
2157
f351d9ef 2158 this->mutex->lock(this->mutex);
89da06ac 2159 this->tsas->put(this->tsas, (void*)(uintptr_t)data->reqid, entry);
f351d9ef
MW
2160 this->isas->put(this->isas, &entry->isa, entry);
2161 this->mutex->unlock(this->mutex);
96ab7a80
MW
2162 }
2163 else
2164 {
f351d9ef
MW
2165 /* comes after inbound, update entry */
2166 this->mutex->lock(this->mutex);
89da06ac 2167 entry = this->tsas->remove(this->tsas, (void*)(uintptr_t)data->reqid);
f351d9ef 2168 this->mutex->unlock(this->mutex);
96ab7a80 2169
f351d9ef
MW
2170 if (!entry)
2171 {
2172 DBG1(DBG_KNL, "adding outbound SA failed, no inbound SA found "
89da06ac 2173 "for reqid %u ", data->reqid);
f351d9ef
MW
2174 return NOT_FOUND;
2175 }
2176 /* TODO: should we check for local/remote, mode etc.? */
2177
2178 entry->osa = (sa_entry_t){
89da06ac 2179 .spi = id->spi,
f351d9ef 2180 .dst = entry->remote,
89da06ac
TB
2181 .protocol = id->proto,
2182 .lifetime = data->lifetime->time.life,
96ab7a80 2183 .encr = {
89da06ac
TB
2184 .alg = data->enc_alg,
2185 .key = chunk_clone(data->enc_key),
96ab7a80
MW
2186 },
2187 .integ = {
89da06ac
TB
2188 .alg = data->int_alg,
2189 .key = chunk_clone(data->int_key),
96ab7a80 2190 },
f351d9ef
MW
2191 };
2192
2193 this->mutex->lock(this->mutex);
2194 this->osas->put(this->osas, &entry->osa, entry);
2195 this->mutex->unlock(this->mutex);
96ab7a80 2196 }
96ab7a80 2197
f351d9ef 2198 return SUCCESS;
8d91eee3
MW
2199}
2200
2201METHOD(kernel_ipsec_t, update_sa, status_t,
89da06ac
TB
2202 private_kernel_wfp_ipsec_t *this, kernel_ipsec_sa_id_t *id,
2203 kernel_ipsec_update_sa_t *data)
8d91eee3 2204{
5a5b9925
MW
2205 entry_t *entry;
2206 sa_entry_t key = {
89da06ac
TB
2207 .dst = id->dst,
2208 .spi = id->spi,
5a5b9925
MW
2209 };
2210 UINT64 sa_id = 0;
2211 IPSEC_SA_CONTEXT1 *ctx;
2212 IPSEC_V4_UDP_ENCAPSULATION0 ports;
2213 UINT32 flags = IPSEC_SA_DETAILS_UPDATE_TRAFFIC;
2214 DWORD res;
2215
2216 this->mutex->lock(this->mutex);
2217 entry = this->osas->get(this->osas, &key);
2218 this->mutex->unlock(this->mutex);
2219
2220 if (entry)
2221 {
2222 /* outbound entry, nothing to do */
2223 return SUCCESS;
2224 }
2225
2226 this->mutex->lock(this->mutex);
2227 entry = this->isas->get(this->isas, &key);
2228 if (entry)
2229 {
2230 /* inbound entry, do update */
2231 sa_id = entry->sa_id;
1f060357
TB
2232 ports.localUdpEncapPort = data->new_dst->get_port(data->new_dst);
2233 ports.remoteUdpEncapPort = data->new_src->get_port(data->new_src);
5a5b9925
MW
2234 }
2235 this->mutex->unlock(this->mutex);
2236
2237 if (!sa_id)
2238 {
2239 return NOT_FOUND;
2240 }
2241
2242 res = IPsecSaContextGetById1(this->handle, sa_id, &ctx);
2243 if (res != ERROR_SUCCESS)
2244 {
2245 DBG1(DBG_KNL, "getting WFP SA context for updated failed: 0x%08x", res);
2246 return FAILED;
2247 }
89da06ac
TB
2248 if (!hosts2traffic(this, data->new_dst, data->new_src, &ctx->inboundSa->traffic) ||
2249 !hosts2traffic(this, data->new_dst, data->new_src, &ctx->outboundSa->traffic))
5a5b9925
MW
2250 {
2251 FwpmFreeMemory0((void**)&ctx);
2252 return FAILED;
2253 }
2254
89da06ac 2255 if (data->new_encap != data->encap)
5a5b9925 2256 {
89da06ac 2257 if (data->new_encap)
5a5b9925
MW
2258 {
2259 ctx->inboundSa->udpEncapsulation = &ports;
2260 ctx->outboundSa->udpEncapsulation = &ports;
2261 }
2262 else
2263 {
2264 ctx->inboundSa->udpEncapsulation = NULL;
2265 ctx->outboundSa->udpEncapsulation = NULL;
2266 }
2267 flags |= IPSEC_SA_DETAILS_UPDATE_UDP_ENCAPSULATION;
2268 }
2269
2270 res = IPsecSaContextUpdate0(this->handle, flags, ctx);
2271 FwpmFreeMemory0((void**)&ctx);
2272 if (res != ERROR_SUCCESS)
2273 {
2274 DBG1(DBG_KNL, "updating WFP SA context failed: 0x%08x", res);
2275 return FAILED;
2276 }
2277
2278 this->mutex->lock(this->mutex);
2279 entry = this->isas->remove(this->isas, &key);
2280 if (entry)
2281 {
2282 key.spi = entry->osa.spi;
2283 key.dst = entry->osa.dst;
2284 this->osas->remove(this->osas, &key);
2285
1b3af3e3
TB
2286 if (data->new_reqid)
2287 {
2288 entry->reqid = data->new_reqid;
2289 }
5a5b9925
MW
2290 entry->local->destroy(entry->local);
2291 entry->remote->destroy(entry->remote);
89da06ac
TB
2292 entry->local = data->new_dst->clone(data->new_dst);
2293 entry->remote = data->new_src->clone(data->new_src);
5a5b9925
MW
2294 entry->isa.dst = entry->local;
2295 entry->osa.dst = entry->remote;
2296
2297 this->isas->put(this->isas, &entry->isa, entry);
2298 this->osas->put(this->osas, &entry->osa, entry);
2299
2300 manage_routes(this, entry, FALSE);
2301 manage_routes(this, entry, TRUE);
2302 }
2303 this->mutex->unlock(this->mutex);
2304
2305 return SUCCESS;
8d91eee3
MW
2306}
2307
2308METHOD(kernel_ipsec_t, query_sa, status_t,
89da06ac
TB
2309 private_kernel_wfp_ipsec_t *this, kernel_ipsec_sa_id_t *id,
2310 kernel_ipsec_query_sa_t *data, uint64_t *bytes, uint64_t *packets,
2311 time_t *time)
8d91eee3 2312{
b50d486e
MW
2313 /* It does not seem that WFP provides any means of getting per-SA traffic
2314 * statistics. IPsecGetStatistics0/1() provides global stats, and
2315 * IPsecSaContextEnum0/1() and IPsecSaEnum0/1() return the configured
2316 * values only. */
8d91eee3
MW
2317 return NOT_SUPPORTED;
2318}
2319
2320METHOD(kernel_ipsec_t, del_sa, status_t,
89da06ac
TB
2321 private_kernel_wfp_ipsec_t *this, kernel_ipsec_sa_id_t *id,
2322 kernel_ipsec_del_sa_t *data)
8d91eee3 2323{
96ab7a80 2324 entry_t *entry;
f351d9ef 2325 sa_entry_t key = {
89da06ac
TB
2326 .dst = id->dst,
2327 .spi = id->spi,
96ab7a80
MW
2328 };
2329
2330 this->mutex->lock(this->mutex);
f351d9ef
MW
2331 entry = this->isas->remove(this->isas, &key);
2332 this->mutex->unlock(this->mutex);
96ab7a80 2333
96ab7a80
MW
2334 if (entry)
2335 {
f351d9ef
MW
2336 /* keep entry until removal of outbound */
2337 return SUCCESS;
96ab7a80
MW
2338 }
2339
f351d9ef
MW
2340 this->mutex->lock(this->mutex);
2341 entry = this->osas->remove(this->osas, &key);
96ab7a80
MW
2342 this->mutex->unlock(this->mutex);
2343
f351d9ef
MW
2344 if (entry)
2345 {
2346 entry_destroy(this, entry);
2347 return SUCCESS;
2348 }
2349
2350 return NOT_FOUND;
8d91eee3
MW
2351}
2352
2353METHOD(kernel_ipsec_t, flush_sas, status_t,
2354 private_kernel_wfp_ipsec_t *this)
2355{
2356 return NOT_SUPPORTED;
2357}
2358
2359METHOD(kernel_ipsec_t, add_policy, status_t,
89da06ac
TB
2360 private_kernel_wfp_ipsec_t *this, kernel_ipsec_policy_id_t *id,
2361 kernel_ipsec_manage_policy_t *data)
8d91eee3 2362{
96ab7a80 2363 status_t status = SUCCESS;
96ab7a80
MW
2364 entry_t *entry;
2365 sp_entry_t *sp;
f351d9ef 2366 sa_entry_t key = {
89da06ac
TB
2367 .spi = data->sa->esp.use ? data->sa->esp.spi : data->sa->ah.spi,
2368 .dst = data->dst,
f351d9ef 2369 };
96ab7a80 2370
89da06ac 2371 if (data->sa->esp.use && data->sa->ah.use)
96ab7a80 2372 {
f351d9ef 2373 return NOT_SUPPORTED;
96ab7a80
MW
2374 }
2375
89da06ac 2376 switch (data->type)
b9349298
MW
2377 {
2378 case POLICY_IPSEC:
2379 break;
2380 case POLICY_PASS:
2381 case POLICY_DROP:
2382 return NOT_SUPPORTED;
2383 }
2384
89da06ac 2385 switch (id->dir)
96ab7a80 2386 {
f351d9ef
MW
2387 case POLICY_OUT:
2388 break;
2389 case POLICY_IN:
2390 case POLICY_FWD:
2391 /* not required */
2392 return SUCCESS;
2393 default:
2394 return NOT_SUPPORTED;
96ab7a80 2395 }
f351d9ef 2396
89da06ac 2397 switch (data->prio)
96ab7a80 2398 {
f351d9ef
MW
2399 case POLICY_PRIORITY_DEFAULT:
2400 break;
f351d9ef 2401 case POLICY_PRIORITY_ROUTED:
89da06ac
TB
2402 if (!add_trap(this, data->sa->reqid, FALSE, data->src, data->dst,
2403 id->src_ts, id->dst_ts))
6de78870
MW
2404 {
2405 return FAILED;
2406 }
89da06ac 2407 if (data->sa->mode == MODE_TUNNEL)
c6f189e4 2408 {
89da06ac
TB
2409 if (!add_trap(this, data->sa->reqid, TRUE, data->src, data->dst,
2410 id->src_ts, id->dst_ts))
6de78870
MW
2411 {
2412 return FAILED;
2413 }
c6f189e4 2414 }
6de78870 2415 return SUCCESS;
b9349298 2416 case POLICY_PRIORITY_FALLBACK:
f351d9ef
MW
2417 default:
2418 return NOT_SUPPORTED;
96ab7a80
MW
2419 }
2420
2421 this->mutex->lock(this->mutex);
f351d9ef 2422 entry = this->osas->get(this->osas, &key);
96ab7a80
MW
2423 if (entry)
2424 {
89da06ac 2425 if (data->sa->mode == MODE_TUNNEL || array_count(entry->sps) == 0)
149fc48e 2426 {
f351d9ef 2427 INIT(sp,
89da06ac
TB
2428 .src = id->src_ts->clone(id->src_ts),
2429 .dst = id->dst_ts->clone(id->dst_ts),
f351d9ef
MW
2430 );
2431 array_insert(entry->sps, -1, sp);
89da06ac 2432 if (array_count(entry->sps) == data->sa->policy_count)
149fc48e 2433 {
4b512803
MW
2434 if (!install(this, entry))
2435 {
2436 status = FAILED;
2437 }
149fc48e
MW
2438 }
2439 }
f351d9ef
MW
2440 else
2441 {
2442 /* TODO: reinstall with a filter using multiple TS?
2443 * Filters are ANDed for a match, but we could install a filter
2444 * with the inverse TS set using NOT-matches... */
4b512803
MW
2445 DBG1(DBG_KNL, "multiple transport mode traffic selectors not "
2446 "supported by WFP");
f351d9ef
MW
2447 status = NOT_SUPPORTED;
2448 }
96ab7a80
MW
2449 }
2450 else
2451 {
f351d9ef 2452 DBG1(DBG_KNL, "adding SP failed, no SA found for SPI 0x%08x", key.spi);
96ab7a80
MW
2453 status = FAILED;
2454 }
2455 this->mutex->unlock(this->mutex);
2456
2457 return status;
8d91eee3
MW
2458}
2459
2460METHOD(kernel_ipsec_t, query_policy, status_t,
89da06ac
TB
2461 private_kernel_wfp_ipsec_t *this, kernel_ipsec_policy_id_t *id,
2462 kernel_ipsec_query_policy_t *data, time_t *use_time)
8d91eee3 2463{
b50d486e 2464 /* see query_sa() for some notes */
8d91eee3
MW
2465 return NOT_SUPPORTED;
2466}
2467
2468METHOD(kernel_ipsec_t, del_policy, status_t,
89da06ac
TB
2469 private_kernel_wfp_ipsec_t *this, kernel_ipsec_policy_id_t *id,
2470 kernel_ipsec_manage_policy_t *data)
8d91eee3 2471{
89da06ac 2472 if (id->dir == POLICY_OUT && data->prio == POLICY_PRIORITY_ROUTED)
c6f189e4 2473 {
89da06ac
TB
2474 if (remove_trap(this, data->sa->reqid, FALSE, id->src_ts,
2475 id->dst_ts))
c6f189e4 2476 {
89da06ac
TB
2477 remove_trap(this, data->sa->reqid, TRUE, id->src_ts,
2478 id->dst_ts);
c6f189e4
MW
2479 return SUCCESS;
2480 }
2481 return NOT_FOUND;
2482 }
f351d9ef
MW
2483 /* not required, as we delete the whole SA/SP set during del_sa() */
2484 return SUCCESS;
8d91eee3
MW
2485}
2486
2487METHOD(kernel_ipsec_t, flush_policies, status_t,
2488 private_kernel_wfp_ipsec_t *this)
2489{
2490 return NOT_SUPPORTED;
2491}
2492
f206e069
MW
2493/**
2494 * Add a bypass policy for a specific UDP port
2495 */
2496static bool add_bypass(private_kernel_wfp_ipsec_t *this,
8c0a67f7
TB
2497 int family, uint16_t port, bool inbound, bool tunnel,
2498 UINT64 *luid)
f206e069
MW
2499{
2500 FWPM_FILTER_CONDITION0 *cond, *conds = NULL;
2501 int count = 0;
2502 DWORD res;
2503 UINT64 weight = 0xff00000000000000;
2504 FWPM_FILTER0 filter = {
2505 .displayData = {
2506 .name = L"charon IKE bypass",
2507 },
2508 .action = {
2509 .type = FWP_ACTION_PERMIT,
2510 },
2511 .weight = {
2512 .type = FWP_UINT64,
2513 .uint64 = &weight,
2514 },
2515 };
2516
2517 switch (family)
2518 {
2519 case AF_INET:
2520 filter.layerKey = inbound ? FWPM_LAYER_INBOUND_TRANSPORT_V4
2521 : FWPM_LAYER_OUTBOUND_TRANSPORT_V4;
2522 break;
2523 case AF_INET6:
2524 filter.layerKey = inbound ? FWPM_LAYER_INBOUND_TRANSPORT_V6
2525 : FWPM_LAYER_OUTBOUND_TRANSPORT_V6;
2526 break;
2527 default:
2528 return FALSE;
2529 }
2530
8c0a67f7
TB
2531 if (tunnel)
2532 {
2533 filter.subLayerKey = FWPM_SUBLAYER_IPSEC_TUNNEL;
2534 }
2535
f206e069
MW
2536 cond = append_condition(&conds, &count);
2537 cond->fieldKey = FWPM_CONDITION_IP_PROTOCOL;
2538 cond->matchType = FWP_MATCH_EQUAL;
2539 cond->conditionValue.type = FWP_UINT8;
2540 cond->conditionValue.uint8 = IPPROTO_UDP;
2541
2542 cond = append_condition(&conds, &count);
2543 cond->fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
2544 cond->matchType = FWP_MATCH_EQUAL;
2545 cond->conditionValue.type = FWP_UINT16;
2546 cond->conditionValue.uint16 = port;
2547
2548 filter.numFilterConditions = count;
2549 filter.filterCondition = conds;
2550
2551 res = FwpmFilterAdd0(this->handle, &filter, NULL, luid);
2552 free_conditions(conds, count);
2553 if (res != ERROR_SUCCESS)
2554 {
6de78870 2555 DBG1(DBG_KNL, "installing WFP bypass filter failed: 0x%08x", res);
f206e069
MW
2556 return FALSE;
2557 }
2558 return TRUE;
2559}
2560
8d91eee3
MW
2561METHOD(kernel_ipsec_t, bypass_socket, bool,
2562 private_kernel_wfp_ipsec_t *this, int fd, int family)
2563{
f206e069
MW
2564 union {
2565 struct sockaddr sa;
2566 SOCKADDR_IN in;
2567 SOCKADDR_IN6 in6;
2568 } saddr;
8c0a67f7
TB
2569 int addrlen = sizeof(saddr), i;
2570 UINT64 filters[4] = { 0 };
b12c53ce 2571 uint16_t port;
f206e069
MW
2572
2573 if (getsockname(fd, &saddr.sa, &addrlen) == SOCKET_ERROR)
2574 {
2575 return FALSE;
2576 }
2577 switch (family)
2578 {
2579 case AF_INET:
2580 port = ntohs(saddr.in.sin_port);
2581 break;
2582 case AF_INET6:
2583 port = ntohs(saddr.in6.sin6_port);
2584 break;
2585 default:
2586 return FALSE;
2587 }
2588
8c0a67f7
TB
2589 if (!add_bypass(this, family, port, TRUE, FALSE, &filters[0]) ||
2590 !add_bypass(this, family, port, TRUE, TRUE, &filters[1]) ||
2591 !add_bypass(this, family, port, FALSE, FALSE, &filters[2]) ||
2592 !add_bypass(this, family, port, FALSE, TRUE, &filters[3]))
f206e069 2593 {
8c0a67f7 2594 for (i = 0; i < countof(filters); i++)
f206e069 2595 {
8c0a67f7
TB
2596 if (filters[i])
2597 {
2598 FwpmFilterDeleteById0(this->handle, filters[i]);
2599 }
f206e069
MW
2600 }
2601 return FALSE;
2602 }
2603
2604 this->mutex->lock(this->mutex);
8c0a67f7
TB
2605 for (i = 0; i < countof(filters); i++)
2606 {
2607 array_insert(this->bypass, ARRAY_TAIL, &filters[i]);
2608 }
f206e069
MW
2609 this->mutex->unlock(this->mutex);
2610
2611 return TRUE;
8d91eee3
MW
2612}
2613
2614METHOD(kernel_ipsec_t, enable_udp_decap, bool,
b12c53ce 2615 private_kernel_wfp_ipsec_t *this, int fd, int family, uint16_t port)
8d91eee3 2616{
f206e069 2617 return FALSE;
8d91eee3
MW
2618}
2619
2620METHOD(kernel_ipsec_t, destroy, void,
2621 private_kernel_wfp_ipsec_t *this)
2622{
f206e069
MW
2623 UINT64 filter;
2624
2625 while (array_remove(this->bypass, ARRAY_TAIL, &filter))
2626 {
2627 FwpmFilterDeleteById0(this->handle, filter);
2628 }
ebb9362d
MW
2629 if (this->handle)
2630 {
28683140
MW
2631 if (this->event)
2632 {
2633 FwpmNetEventUnsubscribe0(this->handle, this->event);
2634 }
f5ddda7f 2635 FwpmProviderDeleteByKey0(this->handle, &this->provider.providerKey);
ebb9362d
MW
2636 FwpmEngineClose0(this->handle);
2637 }
f206e069 2638 array_destroy(this->bypass);
f351d9ef
MW
2639 this->tsas->destroy(this->tsas);
2640 this->isas->destroy(this->isas);
2641 this->osas->destroy(this->osas);
b714746e 2642 this->routes->destroy(this->routes);
c6f189e4 2643 this->traps->destroy(this->traps);
96ab7a80 2644 this->mutex->destroy(this->mutex);
8d91eee3
MW
2645 free(this);
2646}
2647
2648/*
2649 * Described in header.
2650 */
2651kernel_wfp_ipsec_t *kernel_wfp_ipsec_create()
2652{
2653 private_kernel_wfp_ipsec_t *this;
ebb9362d
MW
2654 DWORD res;
2655 FWPM_SESSION0 session = {
2656 .displayData = {
2657 .name = L"charon",
2658 .description = L"strongSwan IKE kernel-wfp backend",
2659 },
2660 };
8d91eee3
MW
2661
2662 INIT(this,
2663 .public = {
2664 .interface = {
2665 .get_features = _get_features,
2666 .get_spi = _get_spi,
2667 .get_cpi = _get_cpi,
2668 .add_sa = _add_sa,
2669 .update_sa = _update_sa,
2670 .query_sa = _query_sa,
2671 .del_sa = _del_sa,
2672 .flush_sas = _flush_sas,
2673 .add_policy = _add_policy,
2674 .query_policy = _query_policy,
2675 .del_policy = _del_policy,
2676 .flush_policies = _flush_policies,
2677 .bypass_socket = _bypass_socket,
2678 .enable_udp_decap = _enable_udp_decap,
2679 .destroy = _destroy,
2680 },
2681 },
f5ddda7f
MW
2682 .provider = {
2683 .displayData = {
2684 .name = L"charon",
2685 .description = L"strongSwan IKE kernel-wfp backend",
2686 },
2687 .providerKey = { 0x59cdae2e, 0xf6bb, 0x4c09,
2688 { 0xa9,0x59,0x9d,0x91,0xac,0xaf,0xf9,0x19 }},
2689 },
b714746e 2690 .mutex = mutex_create(MUTEX_TYPE_RECURSIVE),
f206e069 2691 .bypass = array_create(sizeof(UINT64), 2),
f351d9ef
MW
2692 .tsas = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 4),
2693 .isas = hashtable_create((void*)hash_sa, (void*)equals_sa, 4),
2694 .osas = hashtable_create((void*)hash_sa, (void*)equals_sa, 4),
b714746e 2695 .routes = hashtable_create((void*)hash_route, (void*)equals_route, 4),
c6f189e4 2696 .traps = hashtable_create((void*)hash_trap, (void*)equals_trap, 4),
8d91eee3
MW
2697 );
2698
bbe42a1f
MW
2699 if (!init_spi(this))
2700 {
2701 destroy(this);
2702 return NULL;
2703 }
2704
ebb9362d
MW
2705 res = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session,
2706 &this->handle);
2707 if (res != ERROR_SUCCESS)
2708 {
2709 DBG1(DBG_KNL, "opening WFP engine failed: 0x%08x", res);
2710 destroy(this);
2711 return NULL;
2712 }
2713
f5ddda7f
MW
2714 res = FwpmProviderAdd0(this->handle, &this->provider, NULL);
2715 if (res != ERROR_SUCCESS && res != FWP_E_ALREADY_EXISTS)
2716 {
2717 DBG1(DBG_KNL, "registering WFP provider failed: 0x%08x", res);
2718 destroy(this);
2719 return NULL;
2720 }
2721
28683140
MW
2722 if (!register_events(this))
2723 {
2724 destroy(this);
2725 return NULL;
2726 }
2727
8d91eee3 2728 return &this->public;
ebb9362d 2729}