]> git.ipfire.org Git - thirdparty/strongswan.git/blame - src/libcharon/plugins/kernel_wfp/kernel_wfp_ipsec.c
Update copyright headers after acquisition by secunet
[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{
3b699c72 1640 kernel_acquire_data_t data = {};
b12c53ce 1641 uint32_t reqid = 0;
c6f189e4
MW
1642 trap_t *trap, key = {
1643 .filter_id = filter_id,
1644 };
1645
1646 this->mutex->lock(this->mutex);
1647 trap = this->traps->get(this->traps, &key);
1648 if (trap)
1649 {
1650 reqid = trap->reqid;
1651 }
1652 this->mutex->unlock(this->mutex);
1653
1654 if (reqid)
1655 {
3b699c72
TB
1656 data.src = src ? src->clone(src) : NULL;
1657 data.dst = dst ? dst->clone(dst) : NULL;
1658
1659 charon->kernel->acquire(charon->kernel, reqid, &data);
1660
1661 DESTROY_IF(data.src);
1662 DESTROY_IF(data.dst);
c6f189e4
MW
1663 }
1664}
1665
1666/**
1667 * Create a single host traffic selector from an FWP address definition
1668 */
1669static traffic_selector_t *addr2ts(FWP_IP_VERSION version, void *data,
b12c53ce 1670 uint8_t protocol, uint16_t from_port, uint16_t to_port)
c6f189e4
MW
1671{
1672 ts_type_t type;
1673 UINT32 ints[4];
1674 chunk_t addr;
1675
1676 switch (version)
1677 {
1678 case FWP_IP_VERSION_V4:
1679 ints[0] = untoh32(data);
1680 addr = chunk_from_thing(ints[0]);
1681 type = TS_IPV4_ADDR_RANGE;
1682 break;
1683 case FWP_IP_VERSION_V6:
1684 ints[3] = untoh32(data);
1685 ints[2] = untoh32(data + 4);
1686 ints[1] = untoh32(data + 8);
1687 ints[0] = untoh32(data + 12);
1688 addr = chunk_from_thing(ints);
1689 type = TS_IPV6_ADDR_RANGE;
1690 break;
1691 default:
1692 return NULL;
1693 }
1694 return traffic_selector_create_from_bytes(protocol, type, addr, from_port,
1695 addr, to_port);
1696}
1697
28683140
MW
1698/**
1699 * FwpmNetEventSubscribe0() callback
1700 */
cab59c73 1701static void WINAPI event_callback(void *user, const FWPM_NET_EVENT1 *event)
28683140 1702{
cab59c73 1703 private_kernel_wfp_ipsec_t *this = user;
c6f189e4 1704 traffic_selector_t *local = NULL, *remote = NULL;
b12c53ce
AS
1705 uint8_t protocol = 0;
1706 uint16_t from_local = 0, to_local = 65535;
1707 uint16_t from_remote = 0, to_remote = 65535;
c6f189e4
MW
1708
1709 if ((event->header.flags & FWPM_NET_EVENT_FLAG_LOCAL_ADDR_SET) &&
1710 (event->header.flags & FWPM_NET_EVENT_FLAG_REMOTE_ADDR_SET))
1711 {
1712 if (event->header.flags & FWPM_NET_EVENT_FLAG_LOCAL_PORT_SET)
1713 {
1714 from_local = to_local = event->header.localPort;
1715 }
1716 if (event->header.flags & FWPM_NET_EVENT_FLAG_LOCAL_PORT_SET)
1717 {
1718 from_remote = to_remote = event->header.remotePort;
1719 }
1720 if (event->header.flags & FWPM_NET_EVENT_FLAG_IP_PROTOCOL_SET)
1721 {
1722 protocol = event->header.ipProtocol;
1723 }
1724
1725 local = addr2ts(event->header.ipVersion,
1726 (void*)&event->header.localAddrV6,
1727 protocol, from_local, to_local);
1728 remote = addr2ts(event->header.ipVersion,
1729 (void*)&event->header.remoteAddrV6,
1730 protocol, from_remote, to_remote);
1731 }
1732
1733 switch (event->type)
1734 {
1735 case FWPM_NET_EVENT_TYPE_CLASSIFY_DROP:
1736 acquire(this, event->classifyDrop->filterId, local, remote);
1737 break;
1738 case FWPM_NET_EVENT_TYPE_IKEEXT_MM_FAILURE:
4d48dfd6
MW
1739 DBG1(DBG_KNL, "WFP MM failure: %R === %R, 0x%08x, filterId %llu",
1740 local, remote, event->ikeMmFailure->failureErrorCode,
1741 event->ikeMmFailure->mmFilterId);
1742 break;
c6f189e4 1743 case FWPM_NET_EVENT_TYPE_IKEEXT_QM_FAILURE:
4d48dfd6
MW
1744 DBG1(DBG_KNL, "WFP QM failure: %R === %R, 0x%08x, filterId %llu",
1745 local, remote, event->ikeQmFailure->failureErrorCode,
1746 event->ikeQmFailure->qmFilterId);
1747 break;
c6f189e4 1748 case FWPM_NET_EVENT_TYPE_IKEEXT_EM_FAILURE:
4d48dfd6
MW
1749 DBG1(DBG_KNL, "WFP EM failure: %R === %R, 0x%08x, filterId %llu",
1750 local, remote, event->ikeEmFailure->failureErrorCode,
1751 event->ikeEmFailure->qmFilterId);
1752 break;
c6f189e4 1753 case FWPM_NET_EVENT_TYPE_IPSEC_KERNEL_DROP:
c7d30c2a
MW
1754 DBG1(DBG_KNL, "IPsec kernel drop: %R === %R, error 0x%08x, "
1755 "SPI 0x%08x, %s filterId %llu", local, remote,
1756 event->ipsecDrop->failureStatus, event->ipsecDrop->spi,
1757 event->ipsecDrop->direction ? "in" : "out",
1758 event->ipsecDrop->filterId);
1759 break;
c6f189e4
MW
1760 case FWPM_NET_EVENT_TYPE_IPSEC_DOSP_DROP:
1761 default:
1762 break;
1763 }
1764
1765 DESTROY_IF(local);
1766 DESTROY_IF(remote);
28683140
MW
1767}
1768
1769/**
1770 * Register for net events
1771 */
1772static bool register_events(private_kernel_wfp_ipsec_t *this)
1773{
1774 FWPM_NET_EVENT_SUBSCRIPTION0 subscription = {};
1775 DWORD res;
1776
1777 res = FwpmNetEventSubscribe0(this->handle, &subscription,
cab59c73 1778 event_callback, this, &this->event);
28683140
MW
1779 if (res != ERROR_SUCCESS)
1780 {
1781 DBG1(DBG_KNL, "registering for WFP events failed: 0x%08x", res);
1782 return FALSE;
1783 }
1784 return TRUE;
1785}
1786
c6f189e4
MW
1787/**
1788 * Install a trap policy to kernel
1789 */
1790static bool install_trap(private_kernel_wfp_ipsec_t *this, trap_t *trap)
1791{
1792 FWPM_FILTER_CONDITION0 *conds = NULL;
1793 int count = 0;
1794 DWORD res;
6de78870 1795 const GUID *starget, *dtarget;
c6f189e4
MW
1796 UINT64 weight = 0x000000000000ff00;
1797 FWPM_FILTER0 filter = {
1798 .displayData = {
1799 .name = L"charon IPsec trap",
1800 },
1801 .action = {
1802 .type = FWP_ACTION_BLOCK,
1803 },
1804 .weight = {
1805 .type = FWP_UINT64,
1806 .uint64 = &weight,
1807 },
1808 };
1809
6de78870 1810 if (trap->fwd)
c6f189e4 1811 {
6de78870
MW
1812 if (trap->src->get_type(trap->src) == TS_IPV4_ADDR_RANGE)
1813 {
1814 filter.layerKey = FWPM_LAYER_IPFORWARD_V4;
1815 }
1816 else
1817 {
1818 filter.layerKey = FWPM_LAYER_IPFORWARD_V6;
1819 }
1820 starget = &FWPM_CONDITION_IP_SOURCE_ADDRESS;
1821 dtarget = &FWPM_CONDITION_IP_DESTINATION_ADDRESS;
1822 }
1823 else
1824 {
1825 if (trap->src->get_type(trap->src) == TS_IPV4_ADDR_RANGE)
1826 {
c6f189e4 1827 filter.layerKey = FWPM_LAYER_OUTBOUND_TRANSPORT_V4;
6de78870
MW
1828 }
1829 else
1830 {
c6f189e4 1831 filter.layerKey = FWPM_LAYER_OUTBOUND_TRANSPORT_V6;
6de78870
MW
1832 }
1833 starget = &FWPM_CONDITION_IP_LOCAL_ADDRESS;
1834 dtarget = &FWPM_CONDITION_IP_REMOTE_ADDRESS;
c6f189e4
MW
1835 }
1836
6de78870
MW
1837 if (!ts2condition(trap->src, starget, &conds, &count) ||
1838 !ts2condition(trap->dst, dtarget, &conds, &count))
c6f189e4
MW
1839 {
1840 free_conditions(conds, count);
1841 return FALSE;
1842 }
1843
1844 filter.numFilterConditions = count;
1845 filter.filterCondition = conds;
1846
1847 res = FwpmFilterAdd0(this->handle, &filter, NULL, &trap->filter_id);
1848 free_conditions(conds, count);
1849 if (res != ERROR_SUCCESS)
1850 {
6de78870 1851 DBG1(DBG_KNL, "installing WFP trap filter failed: 0x%08x", res);
c6f189e4
MW
1852 return FALSE;
1853 }
1854 return TRUE;
1855}
1856
1857/**
1858 * Uninstall a trap policy from kernel
1859 */
1860static bool uninstall_trap(private_kernel_wfp_ipsec_t *this, trap_t *trap)
1861{
1862 DWORD res;
1863
1864 res = FwpmFilterDeleteById0(this->handle, trap->filter_id);
1865 if (res != ERROR_SUCCESS)
1866 {
6de78870 1867 DBG1(DBG_KNL, "uninstalling WFP trap filter failed: 0x%08x", res);
c6f189e4
MW
1868 return FALSE;
1869 }
1870 return TRUE;
1871}
1872
1873/**
1874 * Create and install a new trap entry
1875 */
6de78870 1876static bool add_trap(private_kernel_wfp_ipsec_t *this,
b12c53ce 1877 uint32_t reqid, bool fwd, host_t *local, host_t *remote,
6de78870 1878 traffic_selector_t *src, traffic_selector_t *dst)
c6f189e4
MW
1879{
1880 trap_t *trap;
1881
1882 INIT(trap,
1883 .reqid = reqid,
6de78870
MW
1884 .fwd = fwd,
1885 .src = src->clone(src),
1886 .dst = dst->clone(dst),
78bde29a
MW
1887 .local = local->clone(local),
1888 .remote = remote->clone(remote),
c6f189e4
MW
1889 );
1890
1891 if (!install_trap(this, trap))
1892 {
1893 destroy_trap(trap);
1894 return FALSE;
1895 }
78bde29a
MW
1896
1897 trap->route = manage_route(this, local, remote, src, dst, TRUE);
1898
c6f189e4
MW
1899 this->mutex->lock(this->mutex);
1900 this->traps->put(this->traps, trap, trap);
1901 this->mutex->unlock(this->mutex);
1902 return TRUE;
1903}
1904
1905/**
1906 * Uninstall and remove a new trap entry
1907 */
6de78870 1908static bool remove_trap(private_kernel_wfp_ipsec_t *this,
b12c53ce 1909 uint32_t reqid, bool fwd,
6de78870 1910 traffic_selector_t *src, traffic_selector_t *dst)
c6f189e4
MW
1911{
1912 enumerator_t *enumerator;
1913 trap_t *trap, *found = NULL;
1914
1915 this->mutex->lock(this->mutex);
1916 enumerator = this->traps->create_enumerator(this->traps);
1917 while (enumerator->enumerate(enumerator, NULL, &trap))
1918 {
1919 if (reqid == trap->reqid &&
6de78870
MW
1920 fwd == trap->fwd &&
1921 src->equals(src, trap->src) &&
1922 dst->equals(dst, trap->dst))
c6f189e4
MW
1923 {
1924 this->traps->remove_at(this->traps, enumerator);
1925 found = trap;
1926 break;
1927 }
1928 }
1929 enumerator->destroy(enumerator);
1930 this->mutex->unlock(this->mutex);
1931
1932 if (found)
1933 {
78bde29a
MW
1934 if (trap->route)
1935 {
1936 trap->route = !manage_route(this, trap->local, trap->remote,
1937 src, dst, FALSE);
1938 }
c6f189e4
MW
1939 uninstall_trap(this, found);
1940 destroy_trap(found);
1941 return TRUE;
1942 }
1943 return FALSE;
1944}
1945
8d91eee3
MW
1946METHOD(kernel_ipsec_t, get_features, kernel_feature_t,
1947 private_kernel_wfp_ipsec_t *this)
1948{
b9349298 1949 return KERNEL_ESP_V3_TFC | KERNEL_NO_POLICY_UPDATES;
8d91eee3
MW
1950}
1951
bbe42a1f
MW
1952/**
1953 * Initialize seeds for SPI generation
1954 */
1955static bool init_spi(private_kernel_wfp_ipsec_t *this)
1956{
1957 bool ok = TRUE;
1958 rng_t *rng;
1959
1960 rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
1961 if (!rng)
1962 {
1963 return FALSE;
1964 }
b12c53ce 1965 ok = rng->get_bytes(rng, sizeof(this->nextspi), (uint8_t*)&this->nextspi);
bbe42a1f
MW
1966 if (ok)
1967 {
b12c53ce 1968 ok = rng->get_bytes(rng, sizeof(this->mixspi), (uint8_t*)&this->mixspi);
bbe42a1f
MW
1969 }
1970 rng->destroy(rng);
1971 return ok;
1972}
1973
1974/**
1975 * Map an integer x with a one-to-one function using quadratic residues.
1976 */
1977static u_int permute(u_int x, u_int p)
1978{
1979 u_int qr;
1980
1981 x = x % p;
b12c53ce 1982 qr = ((uint64_t)x * x) % p;
bbe42a1f
MW
1983 if (x <= p / 2)
1984 {
1985 return qr;
1986 }
1987 return p - qr;
1988}
1989
8d91eee3
MW
1990METHOD(kernel_ipsec_t, get_spi, status_t,
1991 private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
b12c53ce 1992 uint8_t protocol, uint32_t *spi)
8d91eee3 1993{
2db6d5b8 1994 /* To avoid sequential SPIs, we use a one-to-one permutation function on
bbe42a1f
MW
1995 * an incrementing counter, that is a full period PRNG for the range we
1996 * allocate SPIs in. We add some randomness using a fixed XOR and start
1997 * the counter at random position. This is not cryptographically safe,
1998 * but that is actually not required.
1999 * The selected prime should be smaller than the range we allocate SPIs
2000 * in, and it must satisfy p % 4 == 3 to map x > p/2 using p - qr. */
2001 static const u_int p = 268435399, offset = 0xc0000000;
2002
2003 *spi = htonl(offset + permute(ref_get(&this->nextspi) ^ this->mixspi, p));
96ab7a80 2004 return SUCCESS;
8d91eee3
MW
2005}
2006
2007METHOD(kernel_ipsec_t, get_cpi, status_t,
2008 private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
b12c53ce 2009 uint16_t *cpi)
8d91eee3
MW
2010{
2011 return NOT_SUPPORTED;
2012}
2013
af098b50
MW
2014/**
2015 * Data for an expire callback job
2016 */
2017typedef struct {
2018 /* backref to kernel backend */
2019 private_kernel_wfp_ipsec_t *this;
2020 /* SPI of expiring SA */
b12c53ce 2021 uint32_t spi;
af098b50
MW
2022 /* destination address of expiring SA */
2023 host_t *dst;
2024 /* is this a hard expire, or a rekey request? */
2025 bool hard;
2026} expire_data_t;
2027
2028/**
2029 * Clean up expire data
2030 */
2031static void expire_data_destroy(expire_data_t *data)
2032{
2033 data->dst->destroy(data->dst);
2034 free(data);
2035}
2036
2037/**
2038 * Callback job for SA expiration
2039 */
2040static job_requeue_t expire_job(expire_data_t *data)
2041{
2042 private_kernel_wfp_ipsec_t *this = data->this;
b12c53ce 2043 uint8_t protocol;
f81a9497 2044 entry_t *entry = NULL;
af098b50
MW
2045 sa_entry_t key = {
2046 .spi = data->spi,
2047 .dst = data->dst,
2048 };
2049
2050 if (data->hard)
2051 {
2052 this->mutex->lock(this->mutex);
2053 entry = this->isas->remove(this->isas, &key);
2054 this->mutex->unlock(this->mutex);
2055 if (entry)
2056 {
2057 protocol = entry->isa.protocol;
af098b50
MW
2058 if (entry->osa.dst)
2059 {
2060 key.dst = entry->osa.dst;
2061 key.spi = entry->osa.spi;
2062 this->osas->remove(this->osas, &key);
2063 }
2064 entry_destroy(this, entry);
2065 }
2066 }
2067 else
2068 {
2069 this->mutex->lock(this->mutex);
2070 entry = this->isas->get(this->isas, &key);
2071 if (entry)
2072 {
2073 protocol = entry->isa.protocol;
af098b50
MW
2074 }
2075 this->mutex->unlock(this->mutex);
2076 }
2077
f81a9497 2078 if (entry)
af098b50 2079 {
8394ea2a
TB
2080 charon->kernel->expire(charon->kernel, protocol, data->spi, data->dst,
2081 data->hard);
af098b50
MW
2082 }
2083
2084 return JOB_REQUEUE_NONE;
2085}
2086
2087/**
2088 * Schedule an expire event for an SA
2089 */
b12c53ce
AS
2090static void schedule_expire(private_kernel_wfp_ipsec_t *this, uint32_t spi,
2091 host_t *dst, uint32_t lifetime, bool hard)
af098b50
MW
2092{
2093 expire_data_t *data;
2094
2095 INIT(data,
2096 .this = this,
2097 .spi = spi,
2098 .dst = dst->clone(dst),
2099 .hard = hard,
2100 );
2101
2102 lib->scheduler->schedule_job(lib->scheduler, (job_t*)
2103 callback_job_create((void*)expire_job, data,
2104 (void*)expire_data_destroy, NULL),
2105 lifetime);
2106}
2107
8d91eee3 2108METHOD(kernel_ipsec_t, add_sa, status_t,
89da06ac
TB
2109 private_kernel_wfp_ipsec_t *this, kernel_ipsec_sa_id_t *id,
2110 kernel_ipsec_add_sa_t *data)
8d91eee3 2111{
96ab7a80
MW
2112 host_t *local, *remote;
2113 entry_t *entry;
96ab7a80 2114
89da06ac 2115 if (data->inbound)
96ab7a80 2116 {
f351d9ef 2117 /* comes first, create new entry */
89da06ac
TB
2118 local = id->dst->clone(id->dst);
2119 remote = id->src->clone(id->src);
f351d9ef
MW
2120
2121 INIT(entry,
89da06ac 2122 .reqid = data->reqid,
f351d9ef 2123 .isa = {
89da06ac 2124 .spi = id->spi,
f351d9ef 2125 .dst = local,
89da06ac
TB
2126 .protocol = id->proto,
2127 .lifetime = data->lifetime->time.life,
f351d9ef 2128 .encr = {
89da06ac
TB
2129 .alg = data->enc_alg,
2130 .key = chunk_clone(data->enc_key),
f351d9ef
MW
2131 },
2132 .integ = {
89da06ac
TB
2133 .alg = data->int_alg,
2134 .key = chunk_clone(data->int_key),
f351d9ef
MW
2135 },
2136 },
2137 .sps = array_create(0, 0),
2138 .local = local,
2139 .remote = remote,
89da06ac
TB
2140 .mode = data->mode,
2141 .encap = data->encap,
f351d9ef
MW
2142 );
2143
89da06ac 2144 if (data->lifetime->time.life)
af098b50 2145 {
89da06ac
TB
2146 schedule_expire(this, id->spi, local,
2147 data->lifetime->time.life, TRUE);
af098b50 2148 }
89da06ac
TB
2149 if (data->lifetime->time.rekey &&
2150 data->lifetime->time.rekey != data->lifetime->time.life)
af098b50 2151 {
89da06ac
TB
2152 schedule_expire(this, id->spi, local,
2153 data->lifetime->time.rekey, FALSE);
af098b50
MW
2154 }
2155
f351d9ef 2156 this->mutex->lock(this->mutex);
89da06ac 2157 this->tsas->put(this->tsas, (void*)(uintptr_t)data->reqid, entry);
f351d9ef
MW
2158 this->isas->put(this->isas, &entry->isa, entry);
2159 this->mutex->unlock(this->mutex);
96ab7a80
MW
2160 }
2161 else
2162 {
f351d9ef
MW
2163 /* comes after inbound, update entry */
2164 this->mutex->lock(this->mutex);
89da06ac 2165 entry = this->tsas->remove(this->tsas, (void*)(uintptr_t)data->reqid);
f351d9ef 2166 this->mutex->unlock(this->mutex);
96ab7a80 2167
f351d9ef
MW
2168 if (!entry)
2169 {
2170 DBG1(DBG_KNL, "adding outbound SA failed, no inbound SA found "
89da06ac 2171 "for reqid %u ", data->reqid);
f351d9ef
MW
2172 return NOT_FOUND;
2173 }
2174 /* TODO: should we check for local/remote, mode etc.? */
2175
2176 entry->osa = (sa_entry_t){
89da06ac 2177 .spi = id->spi,
f351d9ef 2178 .dst = entry->remote,
89da06ac
TB
2179 .protocol = id->proto,
2180 .lifetime = data->lifetime->time.life,
96ab7a80 2181 .encr = {
89da06ac
TB
2182 .alg = data->enc_alg,
2183 .key = chunk_clone(data->enc_key),
96ab7a80
MW
2184 },
2185 .integ = {
89da06ac
TB
2186 .alg = data->int_alg,
2187 .key = chunk_clone(data->int_key),
96ab7a80 2188 },
f351d9ef
MW
2189 };
2190
2191 this->mutex->lock(this->mutex);
2192 this->osas->put(this->osas, &entry->osa, entry);
2193 this->mutex->unlock(this->mutex);
96ab7a80 2194 }
96ab7a80 2195
f351d9ef 2196 return SUCCESS;
8d91eee3
MW
2197}
2198
2199METHOD(kernel_ipsec_t, update_sa, status_t,
89da06ac
TB
2200 private_kernel_wfp_ipsec_t *this, kernel_ipsec_sa_id_t *id,
2201 kernel_ipsec_update_sa_t *data)
8d91eee3 2202{
5a5b9925
MW
2203 entry_t *entry;
2204 sa_entry_t key = {
89da06ac
TB
2205 .dst = id->dst,
2206 .spi = id->spi,
5a5b9925
MW
2207 };
2208 UINT64 sa_id = 0;
2209 IPSEC_SA_CONTEXT1 *ctx;
2210 IPSEC_V4_UDP_ENCAPSULATION0 ports;
2211 UINT32 flags = IPSEC_SA_DETAILS_UPDATE_TRAFFIC;
2212 DWORD res;
2213
2214 this->mutex->lock(this->mutex);
2215 entry = this->osas->get(this->osas, &key);
2216 this->mutex->unlock(this->mutex);
2217
2218 if (entry)
2219 {
2220 /* outbound entry, nothing to do */
2221 return SUCCESS;
2222 }
2223
2224 this->mutex->lock(this->mutex);
2225 entry = this->isas->get(this->isas, &key);
2226 if (entry)
2227 {
2228 /* inbound entry, do update */
2229 sa_id = entry->sa_id;
1f060357
TB
2230 ports.localUdpEncapPort = data->new_dst->get_port(data->new_dst);
2231 ports.remoteUdpEncapPort = data->new_src->get_port(data->new_src);
5a5b9925
MW
2232 }
2233 this->mutex->unlock(this->mutex);
2234
2235 if (!sa_id)
2236 {
2237 return NOT_FOUND;
2238 }
2239
2240 res = IPsecSaContextGetById1(this->handle, sa_id, &ctx);
2241 if (res != ERROR_SUCCESS)
2242 {
2243 DBG1(DBG_KNL, "getting WFP SA context for updated failed: 0x%08x", res);
2244 return FAILED;
2245 }
89da06ac
TB
2246 if (!hosts2traffic(this, data->new_dst, data->new_src, &ctx->inboundSa->traffic) ||
2247 !hosts2traffic(this, data->new_dst, data->new_src, &ctx->outboundSa->traffic))
5a5b9925
MW
2248 {
2249 FwpmFreeMemory0((void**)&ctx);
2250 return FAILED;
2251 }
2252
89da06ac 2253 if (data->new_encap != data->encap)
5a5b9925 2254 {
89da06ac 2255 if (data->new_encap)
5a5b9925
MW
2256 {
2257 ctx->inboundSa->udpEncapsulation = &ports;
2258 ctx->outboundSa->udpEncapsulation = &ports;
2259 }
2260 else
2261 {
2262 ctx->inboundSa->udpEncapsulation = NULL;
2263 ctx->outboundSa->udpEncapsulation = NULL;
2264 }
2265 flags |= IPSEC_SA_DETAILS_UPDATE_UDP_ENCAPSULATION;
2266 }
2267
2268 res = IPsecSaContextUpdate0(this->handle, flags, ctx);
2269 FwpmFreeMemory0((void**)&ctx);
2270 if (res != ERROR_SUCCESS)
2271 {
2272 DBG1(DBG_KNL, "updating WFP SA context failed: 0x%08x", res);
2273 return FAILED;
2274 }
2275
2276 this->mutex->lock(this->mutex);
2277 entry = this->isas->remove(this->isas, &key);
2278 if (entry)
2279 {
2280 key.spi = entry->osa.spi;
2281 key.dst = entry->osa.dst;
2282 this->osas->remove(this->osas, &key);
2283
1b3af3e3
TB
2284 if (data->new_reqid)
2285 {
2286 entry->reqid = data->new_reqid;
2287 }
5a5b9925
MW
2288 entry->local->destroy(entry->local);
2289 entry->remote->destroy(entry->remote);
89da06ac
TB
2290 entry->local = data->new_dst->clone(data->new_dst);
2291 entry->remote = data->new_src->clone(data->new_src);
5a5b9925
MW
2292 entry->isa.dst = entry->local;
2293 entry->osa.dst = entry->remote;
2294
2295 this->isas->put(this->isas, &entry->isa, entry);
2296 this->osas->put(this->osas, &entry->osa, entry);
2297
2298 manage_routes(this, entry, FALSE);
2299 manage_routes(this, entry, TRUE);
2300 }
2301 this->mutex->unlock(this->mutex);
2302
2303 return SUCCESS;
8d91eee3
MW
2304}
2305
2306METHOD(kernel_ipsec_t, query_sa, status_t,
89da06ac
TB
2307 private_kernel_wfp_ipsec_t *this, kernel_ipsec_sa_id_t *id,
2308 kernel_ipsec_query_sa_t *data, uint64_t *bytes, uint64_t *packets,
2309 time_t *time)
8d91eee3 2310{
b50d486e
MW
2311 /* It does not seem that WFP provides any means of getting per-SA traffic
2312 * statistics. IPsecGetStatistics0/1() provides global stats, and
2313 * IPsecSaContextEnum0/1() and IPsecSaEnum0/1() return the configured
2314 * values only. */
8d91eee3
MW
2315 return NOT_SUPPORTED;
2316}
2317
2318METHOD(kernel_ipsec_t, del_sa, status_t,
89da06ac
TB
2319 private_kernel_wfp_ipsec_t *this, kernel_ipsec_sa_id_t *id,
2320 kernel_ipsec_del_sa_t *data)
8d91eee3 2321{
96ab7a80 2322 entry_t *entry;
f351d9ef 2323 sa_entry_t key = {
89da06ac
TB
2324 .dst = id->dst,
2325 .spi = id->spi,
96ab7a80
MW
2326 };
2327
2328 this->mutex->lock(this->mutex);
f351d9ef
MW
2329 entry = this->isas->remove(this->isas, &key);
2330 this->mutex->unlock(this->mutex);
96ab7a80 2331
96ab7a80
MW
2332 if (entry)
2333 {
f351d9ef
MW
2334 /* keep entry until removal of outbound */
2335 return SUCCESS;
96ab7a80
MW
2336 }
2337
f351d9ef
MW
2338 this->mutex->lock(this->mutex);
2339 entry = this->osas->remove(this->osas, &key);
96ab7a80
MW
2340 this->mutex->unlock(this->mutex);
2341
f351d9ef
MW
2342 if (entry)
2343 {
2344 entry_destroy(this, entry);
2345 return SUCCESS;
2346 }
2347
2348 return NOT_FOUND;
8d91eee3
MW
2349}
2350
2351METHOD(kernel_ipsec_t, flush_sas, status_t,
2352 private_kernel_wfp_ipsec_t *this)
2353{
2354 return NOT_SUPPORTED;
2355}
2356
2357METHOD(kernel_ipsec_t, add_policy, status_t,
89da06ac
TB
2358 private_kernel_wfp_ipsec_t *this, kernel_ipsec_policy_id_t *id,
2359 kernel_ipsec_manage_policy_t *data)
8d91eee3 2360{
96ab7a80 2361 status_t status = SUCCESS;
96ab7a80
MW
2362 entry_t *entry;
2363 sp_entry_t *sp;
f351d9ef 2364 sa_entry_t key = {
89da06ac
TB
2365 .spi = data->sa->esp.use ? data->sa->esp.spi : data->sa->ah.spi,
2366 .dst = data->dst,
f351d9ef 2367 };
96ab7a80 2368
89da06ac 2369 if (data->sa->esp.use && data->sa->ah.use)
96ab7a80 2370 {
f351d9ef 2371 return NOT_SUPPORTED;
96ab7a80
MW
2372 }
2373
89da06ac 2374 switch (data->type)
b9349298
MW
2375 {
2376 case POLICY_IPSEC:
2377 break;
2378 case POLICY_PASS:
2379 case POLICY_DROP:
2380 return NOT_SUPPORTED;
2381 }
2382
89da06ac 2383 switch (id->dir)
96ab7a80 2384 {
f351d9ef
MW
2385 case POLICY_OUT:
2386 break;
2387 case POLICY_IN:
2388 case POLICY_FWD:
2389 /* not required */
2390 return SUCCESS;
2391 default:
2392 return NOT_SUPPORTED;
96ab7a80 2393 }
f351d9ef 2394
89da06ac 2395 switch (data->prio)
96ab7a80 2396 {
f351d9ef
MW
2397 case POLICY_PRIORITY_DEFAULT:
2398 break;
f351d9ef 2399 case POLICY_PRIORITY_ROUTED:
89da06ac
TB
2400 if (!add_trap(this, data->sa->reqid, FALSE, data->src, data->dst,
2401 id->src_ts, id->dst_ts))
6de78870
MW
2402 {
2403 return FAILED;
2404 }
89da06ac 2405 if (data->sa->mode == MODE_TUNNEL)
c6f189e4 2406 {
89da06ac
TB
2407 if (!add_trap(this, data->sa->reqid, TRUE, data->src, data->dst,
2408 id->src_ts, id->dst_ts))
6de78870
MW
2409 {
2410 return FAILED;
2411 }
c6f189e4 2412 }
6de78870 2413 return SUCCESS;
b9349298 2414 case POLICY_PRIORITY_FALLBACK:
f351d9ef
MW
2415 default:
2416 return NOT_SUPPORTED;
96ab7a80
MW
2417 }
2418
2419 this->mutex->lock(this->mutex);
f351d9ef 2420 entry = this->osas->get(this->osas, &key);
96ab7a80
MW
2421 if (entry)
2422 {
89da06ac 2423 if (data->sa->mode == MODE_TUNNEL || array_count(entry->sps) == 0)
149fc48e 2424 {
f351d9ef 2425 INIT(sp,
89da06ac
TB
2426 .src = id->src_ts->clone(id->src_ts),
2427 .dst = id->dst_ts->clone(id->dst_ts),
f351d9ef
MW
2428 );
2429 array_insert(entry->sps, -1, sp);
89da06ac 2430 if (array_count(entry->sps) == data->sa->policy_count)
149fc48e 2431 {
4b512803
MW
2432 if (!install(this, entry))
2433 {
2434 status = FAILED;
2435 }
149fc48e
MW
2436 }
2437 }
f351d9ef
MW
2438 else
2439 {
2440 /* TODO: reinstall with a filter using multiple TS?
2441 * Filters are ANDed for a match, but we could install a filter
2442 * with the inverse TS set using NOT-matches... */
4b512803
MW
2443 DBG1(DBG_KNL, "multiple transport mode traffic selectors not "
2444 "supported by WFP");
f351d9ef
MW
2445 status = NOT_SUPPORTED;
2446 }
96ab7a80
MW
2447 }
2448 else
2449 {
f351d9ef 2450 DBG1(DBG_KNL, "adding SP failed, no SA found for SPI 0x%08x", key.spi);
96ab7a80
MW
2451 status = FAILED;
2452 }
2453 this->mutex->unlock(this->mutex);
2454
2455 return status;
8d91eee3
MW
2456}
2457
2458METHOD(kernel_ipsec_t, query_policy, status_t,
89da06ac
TB
2459 private_kernel_wfp_ipsec_t *this, kernel_ipsec_policy_id_t *id,
2460 kernel_ipsec_query_policy_t *data, time_t *use_time)
8d91eee3 2461{
b50d486e 2462 /* see query_sa() for some notes */
8d91eee3
MW
2463 return NOT_SUPPORTED;
2464}
2465
2466METHOD(kernel_ipsec_t, del_policy, status_t,
89da06ac
TB
2467 private_kernel_wfp_ipsec_t *this, kernel_ipsec_policy_id_t *id,
2468 kernel_ipsec_manage_policy_t *data)
8d91eee3 2469{
89da06ac 2470 if (id->dir == POLICY_OUT && data->prio == POLICY_PRIORITY_ROUTED)
c6f189e4 2471 {
89da06ac
TB
2472 if (remove_trap(this, data->sa->reqid, FALSE, id->src_ts,
2473 id->dst_ts))
c6f189e4 2474 {
89da06ac
TB
2475 remove_trap(this, data->sa->reqid, TRUE, id->src_ts,
2476 id->dst_ts);
c6f189e4
MW
2477 return SUCCESS;
2478 }
2479 return NOT_FOUND;
2480 }
f351d9ef
MW
2481 /* not required, as we delete the whole SA/SP set during del_sa() */
2482 return SUCCESS;
8d91eee3
MW
2483}
2484
2485METHOD(kernel_ipsec_t, flush_policies, status_t,
2486 private_kernel_wfp_ipsec_t *this)
2487{
2488 return NOT_SUPPORTED;
2489}
2490
f206e069
MW
2491/**
2492 * Add a bypass policy for a specific UDP port
2493 */
2494static bool add_bypass(private_kernel_wfp_ipsec_t *this,
b12c53ce 2495 int family, uint16_t port, bool inbound, UINT64 *luid)
f206e069
MW
2496{
2497 FWPM_FILTER_CONDITION0 *cond, *conds = NULL;
2498 int count = 0;
2499 DWORD res;
2500 UINT64 weight = 0xff00000000000000;
2501 FWPM_FILTER0 filter = {
2502 .displayData = {
2503 .name = L"charon IKE bypass",
2504 },
2505 .action = {
2506 .type = FWP_ACTION_PERMIT,
2507 },
2508 .weight = {
2509 .type = FWP_UINT64,
2510 .uint64 = &weight,
2511 },
2512 };
2513
2514 switch (family)
2515 {
2516 case AF_INET:
2517 filter.layerKey = inbound ? FWPM_LAYER_INBOUND_TRANSPORT_V4
2518 : FWPM_LAYER_OUTBOUND_TRANSPORT_V4;
2519 break;
2520 case AF_INET6:
2521 filter.layerKey = inbound ? FWPM_LAYER_INBOUND_TRANSPORT_V6
2522 : FWPM_LAYER_OUTBOUND_TRANSPORT_V6;
2523 break;
2524 default:
2525 return FALSE;
2526 }
2527
2528 cond = append_condition(&conds, &count);
2529 cond->fieldKey = FWPM_CONDITION_IP_PROTOCOL;
2530 cond->matchType = FWP_MATCH_EQUAL;
2531 cond->conditionValue.type = FWP_UINT8;
2532 cond->conditionValue.uint8 = IPPROTO_UDP;
2533
2534 cond = append_condition(&conds, &count);
2535 cond->fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
2536 cond->matchType = FWP_MATCH_EQUAL;
2537 cond->conditionValue.type = FWP_UINT16;
2538 cond->conditionValue.uint16 = port;
2539
2540 filter.numFilterConditions = count;
2541 filter.filterCondition = conds;
2542
2543 res = FwpmFilterAdd0(this->handle, &filter, NULL, luid);
2544 free_conditions(conds, count);
2545 if (res != ERROR_SUCCESS)
2546 {
6de78870 2547 DBG1(DBG_KNL, "installing WFP bypass filter failed: 0x%08x", res);
f206e069
MW
2548 return FALSE;
2549 }
2550 return TRUE;
2551}
2552
8d91eee3
MW
2553METHOD(kernel_ipsec_t, bypass_socket, bool,
2554 private_kernel_wfp_ipsec_t *this, int fd, int family)
2555{
f206e069
MW
2556 union {
2557 struct sockaddr sa;
2558 SOCKADDR_IN in;
2559 SOCKADDR_IN6 in6;
2560 } saddr;
2561 int addrlen = sizeof(saddr);
2562 UINT64 filter_out, filter_in = 0;
b12c53ce 2563 uint16_t port;
f206e069
MW
2564
2565 if (getsockname(fd, &saddr.sa, &addrlen) == SOCKET_ERROR)
2566 {
2567 return FALSE;
2568 }
2569 switch (family)
2570 {
2571 case AF_INET:
2572 port = ntohs(saddr.in.sin_port);
2573 break;
2574 case AF_INET6:
2575 port = ntohs(saddr.in6.sin6_port);
2576 break;
2577 default:
2578 return FALSE;
2579 }
2580
2581 if (!add_bypass(this, family, port, TRUE, &filter_in) ||
2582 !add_bypass(this, family, port, FALSE, &filter_out))
2583 {
2584 if (filter_in)
2585 {
2586 FwpmFilterDeleteById0(this->handle, filter_in);
2587 }
2588 return FALSE;
2589 }
2590
2591 this->mutex->lock(this->mutex);
2592 array_insert(this->bypass, ARRAY_TAIL, &filter_in);
2593 array_insert(this->bypass, ARRAY_TAIL, &filter_out);
2594 this->mutex->unlock(this->mutex);
2595
2596 return TRUE;
8d91eee3
MW
2597}
2598
2599METHOD(kernel_ipsec_t, enable_udp_decap, bool,
b12c53ce 2600 private_kernel_wfp_ipsec_t *this, int fd, int family, uint16_t port)
8d91eee3 2601{
f206e069 2602 return FALSE;
8d91eee3
MW
2603}
2604
2605METHOD(kernel_ipsec_t, destroy, void,
2606 private_kernel_wfp_ipsec_t *this)
2607{
f206e069
MW
2608 UINT64 filter;
2609
2610 while (array_remove(this->bypass, ARRAY_TAIL, &filter))
2611 {
2612 FwpmFilterDeleteById0(this->handle, filter);
2613 }
ebb9362d
MW
2614 if (this->handle)
2615 {
28683140
MW
2616 if (this->event)
2617 {
2618 FwpmNetEventUnsubscribe0(this->handle, this->event);
2619 }
f5ddda7f 2620 FwpmProviderDeleteByKey0(this->handle, &this->provider.providerKey);
ebb9362d
MW
2621 FwpmEngineClose0(this->handle);
2622 }
f206e069 2623 array_destroy(this->bypass);
f351d9ef
MW
2624 this->tsas->destroy(this->tsas);
2625 this->isas->destroy(this->isas);
2626 this->osas->destroy(this->osas);
b714746e 2627 this->routes->destroy(this->routes);
c6f189e4 2628 this->traps->destroy(this->traps);
96ab7a80 2629 this->mutex->destroy(this->mutex);
8d91eee3
MW
2630 free(this);
2631}
2632
2633/*
2634 * Described in header.
2635 */
2636kernel_wfp_ipsec_t *kernel_wfp_ipsec_create()
2637{
2638 private_kernel_wfp_ipsec_t *this;
ebb9362d
MW
2639 DWORD res;
2640 FWPM_SESSION0 session = {
2641 .displayData = {
2642 .name = L"charon",
2643 .description = L"strongSwan IKE kernel-wfp backend",
2644 },
2645 };
8d91eee3
MW
2646
2647 INIT(this,
2648 .public = {
2649 .interface = {
2650 .get_features = _get_features,
2651 .get_spi = _get_spi,
2652 .get_cpi = _get_cpi,
2653 .add_sa = _add_sa,
2654 .update_sa = _update_sa,
2655 .query_sa = _query_sa,
2656 .del_sa = _del_sa,
2657 .flush_sas = _flush_sas,
2658 .add_policy = _add_policy,
2659 .query_policy = _query_policy,
2660 .del_policy = _del_policy,
2661 .flush_policies = _flush_policies,
2662 .bypass_socket = _bypass_socket,
2663 .enable_udp_decap = _enable_udp_decap,
2664 .destroy = _destroy,
2665 },
2666 },
f5ddda7f
MW
2667 .provider = {
2668 .displayData = {
2669 .name = L"charon",
2670 .description = L"strongSwan IKE kernel-wfp backend",
2671 },
2672 .providerKey = { 0x59cdae2e, 0xf6bb, 0x4c09,
2673 { 0xa9,0x59,0x9d,0x91,0xac,0xaf,0xf9,0x19 }},
2674 },
b714746e 2675 .mutex = mutex_create(MUTEX_TYPE_RECURSIVE),
f206e069 2676 .bypass = array_create(sizeof(UINT64), 2),
f351d9ef
MW
2677 .tsas = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 4),
2678 .isas = hashtable_create((void*)hash_sa, (void*)equals_sa, 4),
2679 .osas = hashtable_create((void*)hash_sa, (void*)equals_sa, 4),
b714746e 2680 .routes = hashtable_create((void*)hash_route, (void*)equals_route, 4),
c6f189e4 2681 .traps = hashtable_create((void*)hash_trap, (void*)equals_trap, 4),
8d91eee3
MW
2682 );
2683
bbe42a1f
MW
2684 if (!init_spi(this))
2685 {
2686 destroy(this);
2687 return NULL;
2688 }
2689
ebb9362d
MW
2690 res = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session,
2691 &this->handle);
2692 if (res != ERROR_SUCCESS)
2693 {
2694 DBG1(DBG_KNL, "opening WFP engine failed: 0x%08x", res);
2695 destroy(this);
2696 return NULL;
2697 }
2698
f5ddda7f
MW
2699 res = FwpmProviderAdd0(this->handle, &this->provider, NULL);
2700 if (res != ERROR_SUCCESS && res != FWP_E_ALREADY_EXISTS)
2701 {
2702 DBG1(DBG_KNL, "registering WFP provider failed: 0x%08x", res);
2703 destroy(this);
2704 return NULL;
2705 }
2706
28683140
MW
2707 if (!register_events(this))
2708 {
2709 destroy(this);
2710 return NULL;
2711 }
2712
8d91eee3 2713 return &this->public;
ebb9362d 2714}