kernel-wfp: Triggering expire events for SAs to rekey/delete
[strongswan.git] / src / libcharon / plugins / kernel_wfp / kernel_wfp_ipsec.c
1 /*
2 * Copyright (C) 2013 Martin Willi
3 * Copyright (C) 2013 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 /* Windows 7, for some fwpmu.h functionality */
17 #define _WIN32_WINNT 0x0601
18
19 #include "kernel_wfp_compat.h"
20 #include "kernel_wfp_ipsec.h"
21
22 #include <daemon.h>
23 #include <hydra.h>
24 #include <threading/mutex.h>
25 #include <collections/array.h>
26 #include <collections/hashtable.h>
27 #include <processing/jobs/callback_job.h>
28
29
30 typedef struct private_kernel_wfp_ipsec_t private_kernel_wfp_ipsec_t;
31
32 struct private_kernel_wfp_ipsec_t {
33
34 /**
35 * Public interface
36 */
37 kernel_wfp_ipsec_t public;
38
39 /**
40 * Next SPI to allocate
41 */
42 refcount_t nextspi;
43
44 /**
45 * Temporary SAD/SPD entries referenced reqid, as uintptr_t => entry_t
46 */
47 hashtable_t *tsas;
48
49 /**
50 * SAD/SPD entries referenced by inbound SA, as sa_entry_t => entry_t
51 */
52 hashtable_t *isas;
53
54 /**
55 * SAD/SPD entries referenced by outbound SA, as sa_entry_t => entry_t
56 */
57 hashtable_t *osas;
58
59 /**
60 * Mutex for accessing entries
61 */
62 mutex_t *mutex;
63
64 /**
65 * WFP session handle
66 */
67 HANDLE handle;
68
69 /**
70 * Provider charon registers as
71 */
72 FWPM_PROVIDER0 provider;
73 };
74
75 /**
76 * Security association entry
77 */
78 typedef struct {
79 /** SPI for this SA */
80 u_int32_t spi;
81 /** protocol, IPPROTO_ESP/IPPROTO_AH */
82 u_int8_t protocol;
83 /** hard lifetime of SA */
84 u_int32_t lifetime;
85 /** destination host address for this SPI */
86 host_t *dst;
87 struct {
88 /** algorithm */
89 u_int16_t alg;
90 /** key */
91 chunk_t key;
92 } integ, encr;
93 } sa_entry_t;
94
95 /**
96 * Hash function for sas lookup table
97 */
98 static u_int hash_sa(sa_entry_t *key)
99 {
100 return chunk_hash_inc(chunk_from_thing(key->spi),
101 chunk_hash(key->dst->get_address(key->dst)));
102 }
103
104 /**
105 * equals function for sas lookup table
106 */
107 static bool equals_sa(sa_entry_t *a, sa_entry_t *b)
108 {
109 return a->spi == b->spi && a->dst->ip_equals(a->dst, b->dst);
110 }
111
112 /**
113 * Security policy entry
114 */
115 typedef struct {
116 /** policy source addresses */
117 traffic_selector_t *src;
118 /** policy destinaiton addresses */
119 traffic_selector_t *dst;
120 } sp_entry_t;
121
122 /**
123 * Destroy an SP entry
124 */
125 static void sp_entry_destroy(sp_entry_t *sp)
126 {
127 sp->src->destroy(sp->src);
128 sp->dst->destroy(sp->dst);
129 free(sp);
130 }
131
132 /**
133 * Collection of SA/SP database entries for a reqid
134 */
135 typedef struct {
136 /** reqid of entry */
137 u_int32_t reqid;
138 /** outer address on local host */
139 host_t *local;
140 /** outer address on remote host */
141 host_t *remote;
142 /** inbound SA entry */
143 sa_entry_t isa;
144 /** outbound SA entry */
145 sa_entry_t osa;
146 /** associated (outbound) policies, as sp_entry_t* */
147 array_t *sps;
148 /** IPsec mode, tunnel|transport */
149 ipsec_mode_t mode;
150 /** UDP encapsulation */
151 bool encap;
152 /** WFP allocated LUID for inbound filter/tunnel policy ID */
153 u_int64_t policy_in;
154 /** WFP allocated LUID for outbound filter ID, unused for tunnel mode */
155 u_int64_t policy_out;
156 /** WFP allocated LUID for SA context */
157 u_int64_t sa_id;
158 } entry_t;
159
160 /**
161 * Remove a transport or tunnel policy from kernel
162 */
163 static void cleanup_policy(private_kernel_wfp_ipsec_t *this, bool transport,
164 u_int64_t policy)
165 {
166 if (transport)
167 {
168 FwpmFilterDeleteById0(this->handle, policy);
169 }
170 else
171 {
172 FWPM_PROVIDER_CONTEXT0 *ctx;
173
174 if (FwpmProviderContextGetById0(this->handle, policy,
175 &ctx) == ERROR_SUCCESS)
176 {
177 FwpmIPsecTunnelDeleteByKey0(this->handle, &ctx->providerContextKey);
178 FwpmFreeMemory0((void**)&ctx);
179 }
180 }
181 }
182
183 /**
184 * Remove policies associated to an entry from kernel
185 */
186 static void cleanup_policies(private_kernel_wfp_ipsec_t *this, entry_t *entry)
187 {
188 if (entry->policy_in)
189 {
190 cleanup_policy(this, entry->mode == MODE_TRANSPORT, entry->policy_in);
191 entry->policy_in = 0;
192 }
193 if (entry->policy_out)
194 {
195 cleanup_policy(this, entry->mode == MODE_TRANSPORT, entry->policy_out);
196 entry->policy_out = 0;
197 }
198 }
199
200 /**
201 * Destroy a SA/SP entry set
202 */
203 static void entry_destroy(private_kernel_wfp_ipsec_t *this, entry_t *entry)
204 {
205 if (entry->sa_id)
206 {
207 IPsecSaContextDeleteById0(this->handle, entry->sa_id);
208 }
209 cleanup_policies(this, entry);
210 array_destroy_function(entry->sps, (void*)sp_entry_destroy, NULL);
211 entry->local->destroy(entry->local);
212 entry->remote->destroy(entry->remote);
213 chunk_clear(&entry->isa.integ.key);
214 chunk_clear(&entry->isa.encr.key);
215 chunk_clear(&entry->osa.integ.key);
216 chunk_clear(&entry->osa.encr.key);
217 free(entry);
218 }
219
220 /**
221 * Append/Realloc a filter condition to an existing condition set
222 */
223 static FWPM_FILTER_CONDITION0 *append_condition(FWPM_FILTER_CONDITION0 *conds[],
224 int *count)
225 {
226 FWPM_FILTER_CONDITION0 *cond;
227
228 (*count)++;
229 *conds = realloc(*conds, *count * sizeof(*cond));
230 cond = *conds + *count - 1;
231 memset(cond, 0, sizeof(*cond));
232
233 return cond;
234 }
235
236 /**
237 * Convert an IPv4 prefix to a host order subnet mask
238 */
239 static u_int32_t prefix2mask(u_int8_t prefix)
240 {
241 u_int8_t netmask[4] = {};
242 int i;
243
244 for (i = 0; i < sizeof(netmask); i++)
245 {
246 if (prefix < 8)
247 {
248 netmask[i] = 0xFF << (8 - prefix);
249 break;
250 }
251 netmask[i] = 0xFF;
252 prefix -= 8;
253 }
254 return untoh32(netmask);
255 }
256
257 /**
258 * Convert a 16-bit range to a WFP condition
259 */
260 static void range2cond(FWPM_FILTER_CONDITION0 *cond,
261 u_int16_t from, u_int16_t to)
262 {
263 if (from == to)
264 {
265 cond->matchType = FWP_MATCH_EQUAL;
266 cond->conditionValue.type = FWP_UINT16;
267 cond->conditionValue.uint16 = from;
268 }
269 else
270 {
271 cond->matchType = FWP_MATCH_RANGE;
272 cond->conditionValue.type = FWP_RANGE_TYPE;
273 cond->conditionValue.rangeValue = calloc(1, sizeof(FWP_RANGE0));
274 cond->conditionValue.rangeValue->valueLow.type = FWP_UINT16;
275 cond->conditionValue.rangeValue->valueLow.uint16 = from;
276 cond->conditionValue.rangeValue->valueHigh.type = FWP_UINT16;
277 cond->conditionValue.rangeValue->valueHigh.uint16 = to;
278 }
279 }
280
281 /**
282 * (Re-)allocate filter conditions for given local or remote traffic selector
283 */
284 static bool ts2condition(traffic_selector_t *ts, bool local,
285 FWPM_FILTER_CONDITION0 *conds[], int *count)
286 {
287 FWPM_FILTER_CONDITION0 *cond;
288 FWP_BYTE_ARRAY16 *addr;
289 FWP_RANGE0 *range;
290 u_int16_t from_port, to_port;
291 void *from, *to;
292 u_int8_t proto;
293 host_t *net;
294 u_int8_t prefix;
295
296 from = ts->get_from_address(ts).ptr;
297 to = ts->get_to_address(ts).ptr;
298 from_port = ts->get_from_port(ts);
299 to_port = ts->get_to_port(ts);
300
301 cond = append_condition(conds, count);
302 if (local)
303 {
304 cond->fieldKey = FWPM_CONDITION_IP_LOCAL_ADDRESS;
305 }
306 else
307 {
308 cond->fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS;
309 }
310 if (ts->is_host(ts, NULL))
311 {
312 cond->matchType = FWP_MATCH_EQUAL;
313 switch (ts->get_type(ts))
314 {
315 case TS_IPV4_ADDR_RANGE:
316 cond->conditionValue.type = FWP_UINT32;
317 cond->conditionValue.uint32 = untoh32(from);
318 break;
319 case TS_IPV6_ADDR_RANGE:
320 cond->conditionValue.type = FWP_BYTE_ARRAY16_TYPE;
321 cond->conditionValue.byteArray16 = addr = malloc(sizeof(*addr));
322 memcpy(addr, from, sizeof(*addr));
323 break;
324 default:
325 return FALSE;
326 }
327 }
328 else if (ts->to_subnet(ts, &net, &prefix))
329 {
330 FWP_V6_ADDR_AND_MASK *m6;
331 FWP_V4_ADDR_AND_MASK *m4;
332
333 cond->matchType = FWP_MATCH_EQUAL;
334 switch (net->get_family(net))
335 {
336 case AF_INET:
337 cond->conditionValue.type = FWP_V4_ADDR_MASK;
338 cond->conditionValue.v4AddrMask = m4 = calloc(1, sizeof(*m4));
339 m4->addr = untoh32(from);
340 m4->mask = prefix2mask(prefix);
341 break;
342 case AF_INET6:
343 cond->conditionValue.type = FWP_V6_ADDR_MASK;
344 cond->conditionValue.v6AddrMask = m6 = calloc(1, sizeof(*m6));
345 memcpy(m6->addr, from, sizeof(m6->addr));
346 m6->prefixLength = prefix;
347 break;
348 default:
349 net->destroy(net);
350 return FALSE;
351 }
352 net->destroy(net);
353 }
354 else
355 {
356 cond->matchType = FWP_MATCH_RANGE;
357 cond->conditionValue.type = FWP_RANGE_TYPE;
358 cond->conditionValue.rangeValue = range = calloc(1, sizeof(*range));
359 switch (ts->get_type(ts))
360 {
361 case TS_IPV4_ADDR_RANGE:
362 range->valueLow.type = FWP_UINT32;
363 range->valueLow.uint32 = untoh32(from);
364 range->valueHigh.type = FWP_UINT32;
365 range->valueHigh.uint32 = untoh32(to);
366 break;
367 case TS_IPV6_ADDR_RANGE:
368 range->valueLow.type = FWP_BYTE_ARRAY16_TYPE;
369 range->valueLow.byteArray16 = addr = malloc(sizeof(*addr));
370 memcpy(addr, from, sizeof(*addr));
371 range->valueHigh.type = FWP_BYTE_ARRAY16_TYPE;
372 range->valueHigh.byteArray16 = addr = malloc(sizeof(*addr));
373 memcpy(addr, to, sizeof(*addr));
374 break;
375 default:
376 return FALSE;
377 }
378 }
379
380 proto = ts->get_protocol(ts);
381 if (proto && local)
382 {
383 cond = append_condition(conds, count);
384 cond->fieldKey = FWPM_CONDITION_IP_PROTOCOL;
385 cond->matchType = FWP_MATCH_EQUAL;
386 cond->conditionValue.type = FWP_UINT8;
387 cond->conditionValue.uint8 = proto;
388 }
389
390 if (proto == IPPROTO_ICMP)
391 {
392 if (local)
393 {
394 u_int8_t from_type, to_type, from_code, to_code;
395
396 from_type = traffic_selector_icmp_type(from_port);
397 to_type = traffic_selector_icmp_type(to_port);
398 from_code = traffic_selector_icmp_code(from_port);
399 to_code = traffic_selector_icmp_code(to_port);
400
401 if (from_type != 0 || to_type != 0xFF)
402 {
403 cond = append_condition(conds, count);
404 cond->fieldKey = FWPM_CONDITION_ICMP_TYPE;
405 range2cond(cond, from_type, to_type);
406 }
407 if (from_code != 0 || to_code != 0xFF)
408 {
409 cond = append_condition(conds, count);
410 cond->fieldKey = FWPM_CONDITION_ICMP_CODE;
411 range2cond(cond, from_code, to_code);
412 }
413 }
414 }
415 else if (from_port != 0 || to_port != 0xFFFF)
416 {
417 cond = append_condition(conds, count);
418 if (local)
419 {
420 cond->fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
421 }
422 else
423 {
424 cond->fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
425 }
426 range2cond(cond, from_port, to_port);
427 }
428 return TRUE;
429 }
430
431 /**
432 * Free memory associated to a single condition
433 */
434 static void free_condition(FWP_DATA_TYPE type, void *value)
435 {
436 FWP_RANGE0 *range;
437
438 switch (type)
439 {
440 case FWP_BYTE_ARRAY16_TYPE:
441 case FWP_V4_ADDR_MASK:
442 case FWP_V6_ADDR_MASK:
443 free(value);
444 break;
445 case FWP_RANGE_TYPE:
446 range = value;
447 free_condition(range->valueLow.type, range->valueLow.sd);
448 free_condition(range->valueHigh.type, range->valueHigh.sd);
449 free(range);
450 break;
451 default:
452 break;
453 }
454 }
455
456 /**
457 * Free memory used by a set of conditions
458 */
459 static void free_conditions(FWPM_FILTER_CONDITION0 *conds, int count)
460 {
461 int i;
462
463 for (i = 0; i < count; i++)
464 {
465 free_condition(conds[i].conditionValue.type, conds[i].conditionValue.sd);
466 }
467 free(conds);
468 }
469
470 /**
471 * Install transport mode SP to the kernel
472 */
473 static bool install_transport_sp(private_kernel_wfp_ipsec_t *this,
474 entry_t *entry, bool inbound)
475 {
476 FWPM_FILTER_CONDITION0 *conds = NULL;
477 int count = 0;
478 enumerator_t *enumerator;
479 traffic_selector_t *local, *remote;
480 sp_entry_t *sp;
481 DWORD res;
482 FWPM_FILTER0 filter = {
483 .displayData = {
484 .name = L"charon IPsec transport",
485 },
486 .action = {
487 .type = FWP_ACTION_CALLOUT_TERMINATING,
488 .calloutKey = inbound ? FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4 :
489 FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4,
490 },
491 .layerKey = inbound ? FWPM_LAYER_INBOUND_TRANSPORT_V4 :
492 FWPM_LAYER_OUTBOUND_TRANSPORT_V4,
493 };
494
495 enumerator = array_create_enumerator(entry->sps);
496 while (enumerator->enumerate(enumerator, &sp))
497 {
498 if (inbound)
499 {
500 local = sp->dst;
501 remote = sp->src;
502 }
503 else
504 {
505 local = sp->src;
506 remote = sp->dst;
507 }
508
509 if (!ts2condition(local, TRUE, &conds, &count) ||
510 !ts2condition(remote, FALSE, &conds, &count))
511 {
512 free_conditions(conds, count);
513 enumerator->destroy(enumerator);
514 return FALSE;
515 }
516 }
517 enumerator->destroy(enumerator);
518
519 filter.numFilterConditions = count;
520 filter.filterCondition = conds;
521
522 if (inbound)
523 {
524 res = FwpmFilterAdd0(this->handle, &filter, NULL, &entry->policy_in);
525 }
526 else
527 {
528 res = FwpmFilterAdd0(this->handle, &filter, NULL, &entry->policy_out);
529 }
530 free_conditions(conds, count);
531 if (res != ERROR_SUCCESS)
532 {
533 DBG1(DBG_KNL, "installing inbound FWP filter failed: 0x%08x", res);
534 return FALSE;
535 }
536 return TRUE;
537 }
538
539 /**
540 * Convert a chunk_t to a WFP FWP_BYTE_BLOB
541 */
542 static inline FWP_BYTE_BLOB chunk2blob(chunk_t chunk)
543 {
544 return (FWP_BYTE_BLOB){
545 .size = chunk.len,
546 .data = chunk.ptr,
547 };
548 }
549
550 /**
551 * Convert an integrity_algorithm_t to a WFP IPSEC_AUTH_TRANFORM_ID0
552 */
553 static bool alg2auth(integrity_algorithm_t alg,
554 IPSEC_SA_AUTH_INFORMATION0 *info)
555 {
556 struct {
557 integrity_algorithm_t alg;
558 IPSEC_AUTH_TRANSFORM_ID0 transform;
559 } map[] = {
560 { AUTH_HMAC_MD5_96, IPSEC_AUTH_TRANSFORM_ID_HMAC_MD5_96 },
561 { AUTH_HMAC_SHA1_96, IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_1_96 },
562 { AUTH_HMAC_SHA2_256_128, IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_256_128},
563 { AUTH_AES_128_GMAC, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_128 },
564 { AUTH_AES_192_GMAC, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_192 },
565 { AUTH_AES_256_GMAC, IPSEC_AUTH_TRANSFORM_ID_GCM_AES_256 },
566 };
567 int i;
568
569 for (i = 0; i < countof(map); i++)
570 {
571 if (map[i].alg == alg)
572 {
573 info->authTransform.authTransformId = map[i].transform;
574 return TRUE;
575 }
576 }
577 return FALSE;
578 }
579
580 /**
581 * Convert an encryption_algorithm_t to a WFP IPSEC_CIPHER_TRANFORM_ID0
582 */
583 static bool alg2cipher(encryption_algorithm_t alg, int keylen,
584 IPSEC_SA_CIPHER_INFORMATION0 *info)
585 {
586 struct {
587 encryption_algorithm_t alg;
588 int keylen;
589 IPSEC_CIPHER_TRANSFORM_ID0 transform;
590 } map[] = {
591 { ENCR_DES, 8, IPSEC_CIPHER_TRANSFORM_ID_CBC_DES },
592 { ENCR_3DES, 24, IPSEC_CIPHER_TRANSFORM_ID_CBC_3DES },
593 { ENCR_AES_CBC, 16, IPSEC_CIPHER_TRANSFORM_ID_AES_128 },
594 { ENCR_AES_CBC, 24, IPSEC_CIPHER_TRANSFORM_ID_AES_192 },
595 { ENCR_AES_CBC, 32, IPSEC_CIPHER_TRANSFORM_ID_AES_256 },
596 { ENCR_AES_GCM_ICV16, 20, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_128 },
597 { ENCR_AES_GCM_ICV16, 28, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_192 },
598 { ENCR_AES_GCM_ICV16, 36, IPSEC_CIPHER_TRANSFORM_ID_GCM_AES_256 },
599 };
600 int i;
601
602 for (i = 0; i < countof(map); i++)
603 {
604 if (map[i].alg == alg && map[i].keylen == keylen)
605 {
606 info->cipherTransform.cipherTransformId = map[i].transform;
607 return TRUE;
608 }
609 }
610 return FALSE;
611 }
612
613 /**
614 * Get the integrity algorithm used for an AEAD transform
615 */
616 static integrity_algorithm_t encr2integ(encryption_algorithm_t encr, int keylen)
617 {
618 struct {
619 encryption_algorithm_t encr;
620 int keylen;
621 integrity_algorithm_t integ;
622 } map[] = {
623 { ENCR_NULL_AUTH_AES_GMAC, 20, AUTH_AES_128_GMAC },
624 { ENCR_NULL_AUTH_AES_GMAC, 28, AUTH_AES_192_GMAC },
625 { ENCR_NULL_AUTH_AES_GMAC, 36, AUTH_AES_256_GMAC },
626 { ENCR_AES_GCM_ICV16, 20, AUTH_AES_128_GMAC },
627 { ENCR_AES_GCM_ICV16, 28, AUTH_AES_192_GMAC },
628 { ENCR_AES_GCM_ICV16, 36, AUTH_AES_256_GMAC },
629 };
630 int i;
631
632 for (i = 0; i < countof(map); i++)
633 {
634 if (map[i].encr == encr && map[i].keylen == keylen)
635 {
636 return map[i].integ;
637 }
638 }
639 return AUTH_UNDEFINED;
640 }
641
642 /**
643 * Install a single SA
644 */
645 static bool install_sa(private_kernel_wfp_ipsec_t *this, entry_t *entry,
646 bool inbound, sa_entry_t *sa, FWP_IP_VERSION version)
647 {
648 IPSEC_SA_AUTH_AND_CIPHER_INFORMATION0 info = {};
649 IPSEC_SA0 ipsec = {
650 .spi = ntohl(sa->spi),
651 };
652 IPSEC_SA_BUNDLE0 bundle = {
653 .lifetime = {
654 .lifetimeSeconds = inbound ? entry->isa.lifetime
655 : entry->osa.lifetime,
656 },
657 .saList = &ipsec,
658 .numSAs = 1,
659 .ipVersion = version,
660 };
661 struct {
662 u_int16_t alg;
663 chunk_t key;
664 } integ = {}, encr = {};
665 DWORD res;
666
667 switch (sa->protocol)
668 {
669 case IPPROTO_AH:
670 ipsec.saTransformType = IPSEC_TRANSFORM_AH;
671 ipsec.ahInformation = &info.saAuthInformation;
672 integ.key = sa->integ.key;
673 integ.alg = sa->integ.alg;
674 break;
675 case IPPROTO_ESP:
676 if (sa->encr.alg == ENCR_NULL ||
677 sa->encr.alg == ENCR_NULL_AUTH_AES_GMAC)
678 {
679 ipsec.saTransformType = IPSEC_TRANSFORM_ESP_AUTH;
680 ipsec.espAuthInformation = &info.saAuthInformation;
681 }
682 else
683 {
684 ipsec.saTransformType = IPSEC_TRANSFORM_ESP_AUTH_AND_CIPHER;
685 ipsec.espAuthAndCipherInformation = &info;
686 encr.key = sa->encr.key;
687 encr.alg = sa->encr.alg;
688 }
689 if (encryption_algorithm_is_aead(sa->encr.alg))
690 {
691 integ.alg = encr2integ(sa->encr.alg, sa->encr.key.len);
692 integ.key = sa->encr.key;
693 }
694 else
695 {
696 integ.alg = sa->integ.alg;
697 integ.key = sa->integ.key;
698 }
699 break;
700 default:
701 return FALSE;
702 }
703
704 if (integ.alg)
705 {
706 info.saAuthInformation.authKey = chunk2blob(integ.key);
707 if (!alg2auth(integ.alg, &info.saAuthInformation))
708 {
709 DBG1(DBG_KNL, "integrity algorithm %N not supported by WFP",
710 integrity_algorithm_names, integ.alg);
711 return FALSE;
712 }
713 }
714 if (encr.alg)
715 {
716 info.saCipherInformation.cipherKey = chunk2blob(encr.key);
717 if (!alg2cipher(encr.alg, encr.key.len, &info.saCipherInformation))
718 {
719 DBG1(DBG_KNL, "encryption algorithm %N not supported by WFP",
720 encryption_algorithm_names, encr.alg);
721 return FALSE;
722 }
723 }
724
725 if (inbound)
726 {
727 res = IPsecSaContextAddInbound0(this->handle, entry->sa_id, &bundle);
728 }
729 else
730 {
731 res = IPsecSaContextAddOutbound0(this->handle, entry->sa_id, &bundle);
732 }
733 if (res != ERROR_SUCCESS)
734 {
735 DBG1(DBG_KNL, "adding %sbound WFP SA failed: 0x%08x",
736 inbound ? "in" : "out", res);
737 return FALSE;
738 }
739 return TRUE;
740 }
741
742 /**
743 * Install SAs to the kernel
744 */
745 static bool install_sas(private_kernel_wfp_ipsec_t *this, entry_t *entry,
746 IPSEC_TRAFFIC_TYPE type)
747 {
748 IPSEC_TRAFFIC0 traffic = {
749 .trafficType = type,
750 };
751 IPSEC_GETSPI1 spi = {
752 .inboundIpsecTraffic = {
753 .trafficType = type,
754 },
755 };
756 DWORD res;
757
758 if (type == IPSEC_TRAFFIC_TYPE_TRANSPORT)
759 {
760 traffic.ipsecFilterId = entry->policy_out;
761 spi.inboundIpsecTraffic.ipsecFilterId = entry->policy_in;
762 }
763 else
764 {
765 traffic.tunnelPolicyId = entry->policy_in;
766 spi.inboundIpsecTraffic.tunnelPolicyId = entry->policy_in;
767 }
768
769 switch (entry->local->get_family(entry->local))
770 {
771 case AF_INET:
772 traffic.ipVersion = FWP_IP_VERSION_V4;
773 traffic.localV4Address =
774 untoh32(entry->local->get_address(entry->local).ptr);
775 traffic.remoteV4Address =
776 untoh32(entry->remote->get_address(entry->remote).ptr);
777 break;
778 case AF_INET6:
779 traffic.ipVersion = FWP_IP_VERSION_V6;
780 memcpy(&traffic.localV6Address,
781 entry->local->get_address(entry->local).ptr, 16);
782 memcpy(&traffic.remoteV6Address,
783 entry->remote->get_address(entry->remote).ptr, 16);
784 break;
785 default:
786 return FALSE;
787 }
788
789 res = IPsecSaContextCreate0(this->handle, &traffic, NULL, &entry->sa_id);
790 if (res != ERROR_SUCCESS)
791 {
792 DBG1(DBG_KNL, "creating WFP SA context failed: 0x%08x", res);
793 return FALSE;
794 }
795
796 memcpy(spi.inboundIpsecTraffic.localV6Address, traffic.localV6Address,
797 sizeof(traffic.localV6Address));
798 memcpy(spi.inboundIpsecTraffic.remoteV6Address, traffic.remoteV6Address,
799 sizeof(traffic.remoteV6Address));
800 spi.ipVersion = traffic.ipVersion;
801
802 res = IPsecSaContextSetSpi0(this->handle, entry->sa_id, &spi,
803 ntohl(entry->isa.spi));
804 if (res != ERROR_SUCCESS)
805 {
806 DBG1(DBG_KNL, "setting WFP SA SPI failed: 0x%08x", res);
807 IPsecSaContextDeleteById0(this->handle, entry->sa_id);
808 entry->sa_id = 0;
809 return FALSE;
810 }
811
812 if (!install_sa(this, entry, TRUE, &entry->isa, spi.ipVersion) ||
813 !install_sa(this, entry, FALSE, &entry->osa, spi.ipVersion))
814 {
815 IPsecSaContextDeleteById0(this->handle, entry->sa_id);
816 entry->sa_id = 0;
817 return FALSE;
818 }
819
820 return TRUE;
821 }
822
823 /**
824 * Install a transport mode SA/SP set to the kernel
825 */
826 static bool install_transport(private_kernel_wfp_ipsec_t *this, entry_t *entry)
827 {
828 if (install_transport_sp(this, entry, TRUE) &&
829 install_transport_sp(this, entry, FALSE) &&
830 install_sas(this, entry, IPSEC_TRAFFIC_TYPE_TRANSPORT))
831 {
832 return TRUE;
833 }
834 cleanup_policies(this, entry);
835 return FALSE;
836 }
837
838 /**
839 * Generate a new GUID, random
840 */
841 static bool generate_guid(private_kernel_wfp_ipsec_t *this, GUID *guid)
842 {
843 bool ok;
844 rng_t *rng;
845
846 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
847 if (!rng)
848 {
849 return FALSE;
850 }
851 ok = rng->get_bytes(rng, sizeof(GUID), (u_int8_t*)guid);
852 rng->destroy(rng);
853 return ok;
854 }
855
856 /**
857 * Install tunnel mode SPs to the kernel
858 */
859 static bool install_tunnel_sps(private_kernel_wfp_ipsec_t *this, entry_t *entry)
860 {
861 FWPM_FILTER_CONDITION0 *conds = NULL;
862 int count = 0;
863 enumerator_t *enumerator;
864 sp_entry_t *sp;
865 DWORD res;
866
867 IPSEC_AUTH_TRANSFORM0 transform = {
868 /* Create any valid proposal. This is actually not used, as we
869 * don't create an SA from this information. */
870 .authTransformId = IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_1_96,
871 };
872 IPSEC_SA_TRANSFORM0 transforms = {
873 .ipsecTransformType = IPSEC_TRANSFORM_ESP_AUTH,
874 .espAuthTransform = &transform,
875 };
876 IPSEC_PROPOSAL0 proposal = {
877 .lifetime = {
878 /* We need a valid lifetime, even if we don't create any SA
879 * from these values. Pick some values accepted. */
880 .lifetimeSeconds = 0xFFFF,
881 .lifetimeKilobytes = 0xFFFFFFFF,
882 .lifetimePackets = 0xFFFFFFFF,
883 },
884 .numSaTransforms = 1,
885 .saTransforms = &transforms,
886 };
887 IPSEC_TUNNEL_POLICY0 policy = {
888 .numIpsecProposals = 1,
889 .ipsecProposals = &proposal,
890 .saIdleTimeout = {
891 /* not used, set to lifetime for maximum */
892 .idleTimeoutSeconds = proposal.lifetime.lifetimeSeconds,
893 .idleTimeoutSecondsFailOver = proposal.lifetime.lifetimeSeconds,
894 },
895 };
896 FWPM_PROVIDER_CONTEXT0 *ctx, qm = {
897 .displayData = {
898 .name = L"charon tunnel provider context",
899 },
900 .providerKey = (GUID*)&this->provider.providerKey,
901 .type = FWPM_IPSEC_IKE_QM_TUNNEL_CONTEXT,
902 .ikeQmTunnelPolicy = &policy,
903 };
904
905 switch (entry->local->get_family(entry->local))
906 {
907 case AF_INET:
908 policy.tunnelEndpoints.ipVersion = FWP_IP_VERSION_V4;
909 policy.tunnelEndpoints.localV4Address =
910 untoh32(entry->local->get_address(entry->local).ptr);
911 policy.tunnelEndpoints.remoteV4Address =
912 untoh32(entry->remote->get_address(entry->remote).ptr);
913 break;
914 case AF_INET6:
915 policy.tunnelEndpoints.ipVersion = FWP_IP_VERSION_V6;
916 memcpy(&policy.tunnelEndpoints.localV6Address,
917 entry->local->get_address(entry->local).ptr, 16);
918 memcpy(&policy.tunnelEndpoints.remoteV6Address,
919 entry->remote->get_address(entry->remote).ptr, 16);
920 break;
921 default:
922 return FALSE;
923 }
924
925 if (!generate_guid(this, &qm.providerContextKey))
926 {
927 return FALSE;
928 }
929
930 enumerator = array_create_enumerator(entry->sps);
931 while (enumerator->enumerate(enumerator, &sp))
932 {
933 if (!ts2condition(sp->src, TRUE, &conds, &count) ||
934 !ts2condition(sp->dst, FALSE, &conds, &count))
935 {
936 free_conditions(conds, count);
937 enumerator->destroy(enumerator);
938 return FALSE;
939 }
940 }
941 enumerator->destroy(enumerator);
942
943 res = FwpmIPsecTunnelAdd0(this->handle, 0, NULL, &qm, count, conds, NULL);
944 free_conditions(conds, count);
945 if (res != ERROR_SUCCESS)
946 {
947 DBG1(DBG_KNL, "installing FWP tunnel policy failed: 0x%08x", res);
948 return FALSE;
949 }
950
951 /* to get the tunnelPolicyId LUID we have to query the context */
952 res = FwpmProviderContextGetByKey0(this->handle, &qm.providerContextKey,
953 &ctx);
954 if (res != ERROR_SUCCESS)
955 {
956 DBG1(DBG_KNL, "getting FWP tunnel policy context failed: 0x%08x", res);
957 return FALSE;
958 }
959 entry->policy_in = ctx->providerContextId;
960 FwpmFreeMemory0((void**)&ctx);
961
962 return TRUE;
963 }
964
965 /**
966 * Install a tunnel mode SA/SP set to the kernel
967 */
968 static bool install_tunnel(private_kernel_wfp_ipsec_t *this, entry_t *entry)
969 {
970 if (install_tunnel_sps(this, entry) &&
971 install_sas(this, entry, IPSEC_TRAFFIC_TYPE_TUNNEL))
972 {
973 return TRUE;
974 }
975 cleanup_policies(this, entry);
976 return FALSE;
977 }
978
979 /**
980 * Install a SA/SP set to the kernel
981 */
982 static bool install(private_kernel_wfp_ipsec_t *this, entry_t *entry)
983 {
984 switch (entry->mode)
985 {
986 case MODE_TRANSPORT:
987 return install_transport(this, entry);
988 case MODE_TUNNEL:
989 return install_tunnel(this, entry);
990 case MODE_BEET:
991 default:
992 return FALSE;
993 }
994 }
995
996 METHOD(kernel_ipsec_t, get_features, kernel_feature_t,
997 private_kernel_wfp_ipsec_t *this)
998 {
999 return KERNEL_ESP_V3_TFC;
1000 }
1001
1002 METHOD(kernel_ipsec_t, get_spi, status_t,
1003 private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
1004 u_int8_t protocol, u_int32_t reqid, u_int32_t *spi)
1005 {
1006 *spi = ref_get(&this->nextspi);
1007 return SUCCESS;
1008 }
1009
1010 METHOD(kernel_ipsec_t, get_cpi, status_t,
1011 private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
1012 u_int32_t reqid, u_int16_t *cpi)
1013 {
1014 return NOT_SUPPORTED;
1015 }
1016
1017 /**
1018 * Data for an expire callback job
1019 */
1020 typedef struct {
1021 /* backref to kernel backend */
1022 private_kernel_wfp_ipsec_t *this;
1023 /* SPI of expiring SA */
1024 u_int32_t spi;
1025 /* destination address of expiring SA */
1026 host_t *dst;
1027 /* is this a hard expire, or a rekey request? */
1028 bool hard;
1029 } expire_data_t;
1030
1031 /**
1032 * Clean up expire data
1033 */
1034 static void expire_data_destroy(expire_data_t *data)
1035 {
1036 data->dst->destroy(data->dst);
1037 free(data);
1038 }
1039
1040 /**
1041 * Callback job for SA expiration
1042 */
1043 static job_requeue_t expire_job(expire_data_t *data)
1044 {
1045 private_kernel_wfp_ipsec_t *this = data->this;
1046 u_int32_t reqid = 0;
1047 u_int8_t protocol;
1048 entry_t *entry;
1049 sa_entry_t key = {
1050 .spi = data->spi,
1051 .dst = data->dst,
1052 };
1053
1054 if (data->hard)
1055 {
1056 this->mutex->lock(this->mutex);
1057 entry = this->isas->remove(this->isas, &key);
1058 this->mutex->unlock(this->mutex);
1059 if (entry)
1060 {
1061 protocol = entry->isa.protocol;
1062 reqid = entry->reqid;
1063 if (entry->osa.dst)
1064 {
1065 key.dst = entry->osa.dst;
1066 key.spi = entry->osa.spi;
1067 this->osas->remove(this->osas, &key);
1068 }
1069 entry_destroy(this, entry);
1070 }
1071 }
1072 else
1073 {
1074 this->mutex->lock(this->mutex);
1075 entry = this->isas->get(this->isas, &key);
1076 if (entry)
1077 {
1078 protocol = entry->isa.protocol;
1079 reqid = entry->reqid;
1080 }
1081 this->mutex->unlock(this->mutex);
1082 }
1083
1084 if (reqid)
1085 {
1086 hydra->kernel_interface->expire(hydra->kernel_interface,
1087 reqid, protocol, data->spi, data->hard);
1088 }
1089
1090 return JOB_REQUEUE_NONE;
1091 }
1092
1093 /**
1094 * Schedule an expire event for an SA
1095 */
1096 static void schedule_expire(private_kernel_wfp_ipsec_t *this, u_int32_t spi,
1097 host_t *dst, u_int32_t lifetime, bool hard)
1098 {
1099 expire_data_t *data;
1100
1101 INIT(data,
1102 .this = this,
1103 .spi = spi,
1104 .dst = dst->clone(dst),
1105 .hard = hard,
1106 );
1107
1108 lib->scheduler->schedule_job(lib->scheduler, (job_t*)
1109 callback_job_create((void*)expire_job, data,
1110 (void*)expire_data_destroy, NULL),
1111 lifetime);
1112 }
1113
1114 METHOD(kernel_ipsec_t, add_sa, status_t,
1115 private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
1116 u_int32_t spi, u_int8_t protocol, u_int32_t reqid, mark_t mark,
1117 u_int32_t tfc, lifetime_cfg_t *lifetime, u_int16_t enc_alg, chunk_t enc_key,
1118 u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, u_int16_t ipcomp,
1119 u_int16_t cpi, bool initiator, bool encap, bool esn, bool inbound,
1120 traffic_selector_t *src_ts, traffic_selector_t *dst_ts)
1121 {
1122 host_t *local, *remote;
1123 entry_t *entry;
1124
1125 if (inbound)
1126 {
1127 /* comes first, create new entry */
1128 local = dst->clone(dst);
1129 remote = src->clone(src);
1130
1131 INIT(entry,
1132 .reqid = reqid,
1133 .isa = {
1134 .spi = spi,
1135 .dst = local,
1136 .protocol = protocol,
1137 .lifetime = lifetime->time.life,
1138 .encr = {
1139 .alg = enc_alg,
1140 .key = chunk_clone(enc_key),
1141 },
1142 .integ = {
1143 .alg = int_alg,
1144 .key = chunk_clone(int_key),
1145 },
1146 },
1147 .sps = array_create(0, 0),
1148 .local = local,
1149 .remote = remote,
1150 .mode = mode,
1151 .encap = encap,
1152 );
1153
1154 if (lifetime->time.life)
1155 {
1156 schedule_expire(this, spi, local, lifetime->time.life, TRUE);
1157 }
1158 if (lifetime->time.rekey && lifetime->time.rekey != lifetime->time.life)
1159 {
1160 schedule_expire(this, spi, local, lifetime->time.rekey, FALSE);
1161 }
1162
1163 this->mutex->lock(this->mutex);
1164 this->tsas->put(this->tsas, (void*)(uintptr_t)reqid, entry);
1165 this->isas->put(this->isas, &entry->isa, entry);
1166 this->mutex->unlock(this->mutex);
1167 }
1168 else
1169 {
1170 /* comes after inbound, update entry */
1171 this->mutex->lock(this->mutex);
1172 entry = this->tsas->remove(this->tsas, (void*)(uintptr_t)reqid);
1173 this->mutex->unlock(this->mutex);
1174
1175 if (!entry)
1176 {
1177 DBG1(DBG_KNL, "adding outbound SA failed, no inbound SA found "
1178 "for reqid %u ", reqid);
1179 return NOT_FOUND;
1180 }
1181 /* TODO: should we check for local/remote, mode etc.? */
1182
1183 entry->osa = (sa_entry_t){
1184 .spi = spi,
1185 .dst = entry->remote,
1186 .protocol = protocol,
1187 .lifetime = lifetime->time.life,
1188 .encr = {
1189 .alg = enc_alg,
1190 .key = chunk_clone(enc_key),
1191 },
1192 .integ = {
1193 .alg = int_alg,
1194 .key = chunk_clone(int_key),
1195 },
1196 };
1197
1198 this->mutex->lock(this->mutex);
1199 this->osas->put(this->osas, &entry->osa, entry);
1200 this->mutex->unlock(this->mutex);
1201 }
1202
1203 return SUCCESS;
1204 }
1205
1206 METHOD(kernel_ipsec_t, update_sa, status_t,
1207 private_kernel_wfp_ipsec_t *this, u_int32_t spi, u_int8_t protocol,
1208 u_int16_t cpi, host_t *src, host_t *dst, host_t *new_src, host_t *new_dst,
1209 bool encap, bool new_encap, mark_t mark)
1210 {
1211 return NOT_SUPPORTED;
1212 }
1213
1214 METHOD(kernel_ipsec_t, query_sa, status_t,
1215 private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
1216 u_int32_t spi, u_int8_t protocol, mark_t mark, u_int64_t *bytes,
1217 u_int64_t *packets, time_t *time)
1218 {
1219 /* It does not seem that WFP provides any means of getting per-SA traffic
1220 * statistics. IPsecGetStatistics0/1() provides global stats, and
1221 * IPsecSaContextEnum0/1() and IPsecSaEnum0/1() return the configured
1222 * values only. */
1223 return NOT_SUPPORTED;
1224 }
1225
1226 METHOD(kernel_ipsec_t, del_sa, status_t,
1227 private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
1228 u_int32_t spi, u_int8_t protocol, u_int16_t cpi, mark_t mark)
1229 {
1230 entry_t *entry;
1231 sa_entry_t key = {
1232 .dst = dst,
1233 .spi = spi,
1234 };
1235
1236 this->mutex->lock(this->mutex);
1237 entry = this->isas->remove(this->isas, &key);
1238 this->mutex->unlock(this->mutex);
1239
1240 if (entry)
1241 {
1242 /* keep entry until removal of outbound */
1243 return SUCCESS;
1244 }
1245
1246 this->mutex->lock(this->mutex);
1247 entry = this->osas->remove(this->osas, &key);
1248 this->mutex->unlock(this->mutex);
1249
1250 if (entry)
1251 {
1252 entry_destroy(this, entry);
1253 return SUCCESS;
1254 }
1255
1256 return NOT_FOUND;
1257 }
1258
1259 METHOD(kernel_ipsec_t, flush_sas, status_t,
1260 private_kernel_wfp_ipsec_t *this)
1261 {
1262 return NOT_SUPPORTED;
1263 }
1264
1265 METHOD(kernel_ipsec_t, add_policy, status_t,
1266 private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
1267 traffic_selector_t *src_ts, traffic_selector_t *dst_ts,
1268 policy_dir_t direction, policy_type_t type, ipsec_sa_cfg_t *sa, mark_t mark,
1269 policy_priority_t priority)
1270 {
1271 status_t status = SUCCESS;
1272 entry_t *entry;
1273 sp_entry_t *sp;
1274 sa_entry_t key = {
1275 .spi = sa->esp.use ? sa->esp.spi : sa->ah.spi,
1276 .dst = dst,
1277 };
1278
1279 if (sa->esp.use && sa->ah.use)
1280 {
1281 return NOT_SUPPORTED;
1282 }
1283
1284 switch (direction)
1285 {
1286 case POLICY_OUT:
1287 break;
1288 case POLICY_IN:
1289 case POLICY_FWD:
1290 /* not required */
1291 return SUCCESS;
1292 default:
1293 return NOT_SUPPORTED;
1294 }
1295
1296 switch (priority)
1297 {
1298 case POLICY_PRIORITY_DEFAULT:
1299 break;
1300 case POLICY_PRIORITY_FALLBACK:
1301 /* TODO: install fallback policy? */
1302 return SUCCESS;
1303 case POLICY_PRIORITY_ROUTED:
1304 /* TODO: install trap policy with low prio */
1305 default:
1306 return NOT_SUPPORTED;
1307 }
1308
1309 this->mutex->lock(this->mutex);
1310 entry = this->osas->get(this->osas, &key);
1311 if (entry)
1312 {
1313 if (array_count(entry->sps) == 0)
1314 {
1315 INIT(sp,
1316 .src = src_ts->clone(src_ts),
1317 .dst = dst_ts->clone(dst_ts),
1318 );
1319 array_insert(entry->sps, -1, sp);
1320 if (!install(this, entry))
1321 {
1322 status = FAILED;
1323 }
1324 }
1325 else
1326 {
1327 /* TODO: reinstall with a filter using multiple TS?
1328 * Filters are ANDed for a match, but we could install a filter
1329 * with the inverse TS set using NOT-matches... */
1330 status = NOT_SUPPORTED;
1331 }
1332 }
1333 else
1334 {
1335 DBG1(DBG_KNL, "adding SP failed, no SA found for SPI 0x%08x", key.spi);
1336 status = FAILED;
1337 }
1338 this->mutex->unlock(this->mutex);
1339
1340 return status;
1341 }
1342
1343 METHOD(kernel_ipsec_t, query_policy, status_t,
1344 private_kernel_wfp_ipsec_t *this, traffic_selector_t *src_ts,
1345 traffic_selector_t *dst_ts, policy_dir_t direction, mark_t mark,
1346 time_t *use_time)
1347 {
1348 /* see query_sa() for some notes */
1349 return NOT_SUPPORTED;
1350 }
1351
1352 METHOD(kernel_ipsec_t, del_policy, status_t,
1353 private_kernel_wfp_ipsec_t *this, traffic_selector_t *src_ts,
1354 traffic_selector_t *dst_ts, policy_dir_t direction, u_int32_t reqid,
1355 mark_t mark, policy_priority_t priority)
1356 {
1357 /* not required, as we delete the whole SA/SP set during del_sa() */
1358 return SUCCESS;
1359 }
1360
1361 METHOD(kernel_ipsec_t, flush_policies, status_t,
1362 private_kernel_wfp_ipsec_t *this)
1363 {
1364 return NOT_SUPPORTED;
1365 }
1366
1367 METHOD(kernel_ipsec_t, bypass_socket, bool,
1368 private_kernel_wfp_ipsec_t *this, int fd, int family)
1369 {
1370 return NOT_SUPPORTED;
1371 }
1372
1373 METHOD(kernel_ipsec_t, enable_udp_decap, bool,
1374 private_kernel_wfp_ipsec_t *this, int fd, int family, u_int16_t port)
1375 {
1376 return NOT_SUPPORTED;
1377 }
1378
1379 METHOD(kernel_ipsec_t, destroy, void,
1380 private_kernel_wfp_ipsec_t *this)
1381 {
1382 if (this->handle)
1383 {
1384 FwpmProviderDeleteByKey0(this->handle, &this->provider.providerKey);
1385 FwpmEngineClose0(this->handle);
1386 }
1387 this->tsas->destroy(this->tsas);
1388 this->isas->destroy(this->isas);
1389 this->osas->destroy(this->osas);
1390 this->mutex->destroy(this->mutex);
1391 free(this);
1392 }
1393
1394 /*
1395 * Described in header.
1396 */
1397 kernel_wfp_ipsec_t *kernel_wfp_ipsec_create()
1398 {
1399 private_kernel_wfp_ipsec_t *this;
1400 DWORD res;
1401 FWPM_SESSION0 session = {
1402 .displayData = {
1403 .name = L"charon",
1404 .description = L"strongSwan IKE kernel-wfp backend",
1405 },
1406 };
1407
1408 INIT(this,
1409 .public = {
1410 .interface = {
1411 .get_features = _get_features,
1412 .get_spi = _get_spi,
1413 .get_cpi = _get_cpi,
1414 .add_sa = _add_sa,
1415 .update_sa = _update_sa,
1416 .query_sa = _query_sa,
1417 .del_sa = _del_sa,
1418 .flush_sas = _flush_sas,
1419 .add_policy = _add_policy,
1420 .query_policy = _query_policy,
1421 .del_policy = _del_policy,
1422 .flush_policies = _flush_policies,
1423 .bypass_socket = _bypass_socket,
1424 .enable_udp_decap = _enable_udp_decap,
1425 .destroy = _destroy,
1426 },
1427 },
1428 .provider = {
1429 .displayData = {
1430 .name = L"charon",
1431 .description = L"strongSwan IKE kernel-wfp backend",
1432 },
1433 .providerKey = { 0x59cdae2e, 0xf6bb, 0x4c09,
1434 { 0xa9,0x59,0x9d,0x91,0xac,0xaf,0xf9,0x19 }},
1435 },
1436 .nextspi = htonl(0xc0000001),
1437 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
1438 .tsas = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 4),
1439 .isas = hashtable_create((void*)hash_sa, (void*)equals_sa, 4),
1440 .osas = hashtable_create((void*)hash_sa, (void*)equals_sa, 4),
1441 );
1442
1443 res = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session,
1444 &this->handle);
1445 if (res != ERROR_SUCCESS)
1446 {
1447 DBG1(DBG_KNL, "opening WFP engine failed: 0x%08x", res);
1448 destroy(this);
1449 return NULL;
1450 }
1451
1452 res = FwpmProviderAdd0(this->handle, &this->provider, NULL);
1453 if (res != ERROR_SUCCESS && res != FWP_E_ALREADY_EXISTS)
1454 {
1455 DBG1(DBG_KNL, "registering WFP provider failed: 0x%08x", res);
1456 destroy(this);
1457 return NULL;
1458 }
1459
1460 return &this->public;
1461 }