Annotation of embedaddon/strongswan/src/libcharon/plugins/forecast/forecast_listener.c, revision 1.1.1.2
1.1 misho 1: /*
2: * Copyright (C) 2015 Tobias Brunner
3: * HSR Hochschule fuer Technik Rapperswil
4: *
5: * Copyright (C) 2010-2014 Martin Willi
6: * Copyright (C) 2010-2014 revosec AG
7: *
8: * This program is free software; you can redistribute it and/or modify it
9: * under the terms of the GNU General Public License as published by the
10: * Free Software Foundation; either version 2 of the License, or (at your
11: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12: *
13: * This program is distributed in the hope that it will be useful, but
14: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16: * for more details.
17: */
18:
19: #include "forecast_listener.h"
20:
21: #include <errno.h>
22: #include <libiptc/libiptc.h>
23: #include <linux/netfilter/xt_MARK.h>
24: #include <linux/netfilter/xt_esp.h>
25:
26: #include <daemon.h>
27: #include <collections/array.h>
28: #include <collections/hashtable.h>
29: #include <threading/rwlock.h>
30:
31: /**
32: * Add a struct at the current position in the buffer
33: */
34: #define ADD_STRUCT(pos, st, ...) ({\
35: typeof(pos) _cur = pos; pos += XT_ALIGN(sizeof(st));\
36: *(st*)_cur = (st){ __VA_ARGS__ };\
37: (st*)_cur;\
38: })
39:
40: typedef struct private_forecast_listener_t private_forecast_listener_t;
41:
42: /**
43: * Private data of an forecast_listener_t object.
44: */
45: struct private_forecast_listener_t {
46:
47: /**
48: * Public forecast_listener_t interface.
49: */
50: forecast_listener_t public;
51:
52: /**
53: * List of entries
54: */
55: linked_list_t *entries;
56:
57: /**
58: * RWlock for IP list
59: */
60: rwlock_t *lock;
61:
62: /**
63: * Configs we do reinjection
64: */
65: char *reinject_configs;
66:
67: /**
68: * Broadcast address on LAN interface, network order
69: */
70: uint32_t broadcast;
71: };
72:
73: /**
74: * Hashtable entry
75: */
76: typedef struct {
77: /** local traffic selectors */
78: array_t *lts;
79: /** remote traffic selectors */
80: array_t *rts;
81: /** firewall mark used by CHILD_SA */
82: u_int mark;
83: /** local IKE_SA endpoint */
84: host_t *lhost;
85: /** remote IKE_SA endpoint */
86: host_t *rhost;
87: /** inbound SPI */
88: uint32_t spi;
89: /** use UDP encapsulation */
90: bool encap;
91: /** whether we should allow reencapsulation of IPsec received forecasts */
92: bool reinject;
93: /** broadcast address used for that entry */
94: uint32_t broadcast;
95: } entry_t;
96:
97: /**
98: * Destroy an entry
99: */
100: static void entry_destroy(entry_t *entry)
101: {
102: if (entry)
103: {
104: entry->lhost->destroy(entry->lhost);
105: entry->rhost->destroy(entry->rhost);
106: array_destroy_offset(entry->lts, offsetof(traffic_selector_t, destroy));
107: array_destroy_offset(entry->rts, offsetof(traffic_selector_t, destroy));
108: free(entry);
109: }
110: }
111:
112: /**
113: * Convert an (IPv4) traffic selector to an address and mask
114: */
115: static bool ts2in(traffic_selector_t *ts,
116: struct in_addr *addr, struct in_addr *mask)
117: {
118: uint8_t bits;
119: host_t *net;
120:
121: if (ts->get_type(ts) == TS_IPV4_ADDR_RANGE &&
122: ts->to_subnet(ts, &net, &bits))
123: {
124: memcpy(&addr->s_addr, net->get_address(net).ptr, 4);
125: net->destroy(net);
126: mask->s_addr = htonl(0xffffffffU << (32 - bits));
127: return TRUE;
128: }
129: return FALSE;
130: }
131:
132: /**
133: * Convert an (IPv4) host to an address with mask
134: */
135: static bool host2in(host_t *host, struct in_addr *addr, struct in_addr *mask)
136: {
137: if (host->get_family(host) == AF_INET)
138: {
139: memcpy(&addr->s_addr, host->get_address(host).ptr, 4);
140: mask->s_addr = ~0;
141: return TRUE;
142: }
143: return FALSE;
144: }
145:
146: /**
147: * Add or remove a rule to/from the specified chain
148: */
149: static bool manage_rule(struct iptc_handle *ipth, const char *chain,
150: bool add, struct ipt_entry *e)
151: {
152: if (add)
153: {
154: if (!iptc_insert_entry(chain, e, 0, ipth))
155: {
156: DBG1(DBG_CFG, "appending %s rule failed: %s",
157: chain, iptc_strerror(errno));
158: return FALSE;
159: }
160: }
161: else
162: {
163: u_char matchmask[e->next_offset];
164:
165: memset(matchmask, 255, sizeof(matchmask));
166: if (!iptc_delete_entry(chain, e, matchmask, ipth))
167: {
168: DBG1(DBG_CFG, "deleting %s rule failed: %s",
169: chain, iptc_strerror(errno));
170: return FALSE;
171: }
172: }
173: return TRUE;
174: }
175:
176: /**
177: * Add rule marking UDP-encapsulated ESP packets to match the correct policy
178: */
179: static bool manage_pre_esp_in_udp(struct iptc_handle *ipth,
180: entry_t *entry, bool add)
181: {
182: uint16_t match_size = XT_ALIGN(sizeof(struct ipt_entry_match)) +
183: XT_ALIGN(sizeof(struct xt_udp));
184: uint16_t target_offset = XT_ALIGN(sizeof(struct ipt_entry)) + match_size;
185: uint16_t target_size = XT_ALIGN(sizeof(struct ipt_entry_target)) +
186: XT_ALIGN(sizeof(struct xt_mark_tginfo2));
187: uint16_t entry_size = target_offset + target_size;
188: u_char ipt[entry_size], *pos = ipt;
189: struct ipt_entry *e;
190:
191: memset(ipt, 0, sizeof(ipt));
192: e = ADD_STRUCT(pos, struct ipt_entry,
193: .target_offset = target_offset,
194: .next_offset = entry_size,
195: .ip = {
196: .proto = IPPROTO_UDP,
197: },
198: );
199: if (!host2in(entry->lhost, &e->ip.dst, &e->ip.dmsk) ||
200: !host2in(entry->rhost, &e->ip.src, &e->ip.smsk))
201: {
202: return FALSE;
203: }
204: ADD_STRUCT(pos, struct ipt_entry_match,
205: .u = {
206: .user = {
207: .match_size = match_size,
208: .name = "udp",
209: },
210: },
211: );
212: ADD_STRUCT(pos, struct xt_udp,
213: .spts = {
214: entry->rhost->get_port(entry->rhost),
215: entry->rhost->get_port(entry->rhost)
216: },
217: .dpts = {
218: entry->lhost->get_port(entry->lhost),
219: entry->lhost->get_port(entry->lhost)
220: },
221: );
222: ADD_STRUCT(pos, struct ipt_entry_target,
223: .u = {
224: .user = {
225: .target_size = target_size,
226: .name = "MARK",
227: .revision = 2,
228: },
229: },
230: );
231: ADD_STRUCT(pos, struct xt_mark_tginfo2,
232: .mark = entry->mark,
233: .mask = ~0,
234: );
235: return manage_rule(ipth, "PREROUTING", add, e);
236: }
237:
238: /**
239: * Add rule marking non-encapsulated ESP packets to match the correct policy
240: */
241: static bool manage_pre_esp(struct iptc_handle *ipth, entry_t *entry, bool add)
242: {
243: uint16_t match_size = XT_ALIGN(sizeof(struct ipt_entry_match)) +
244: XT_ALIGN(sizeof(struct xt_esp));
245: uint16_t target_offset = XT_ALIGN(sizeof(struct ipt_entry)) + match_size;
246: uint16_t target_size = XT_ALIGN(sizeof(struct ipt_entry_target)) +
247: XT_ALIGN(sizeof(struct xt_mark_tginfo2));
248: uint16_t entry_size = target_offset + target_size;
249: u_char ipt[entry_size], *pos = ipt;
250: struct ipt_entry *e;
251:
252: memset(ipt, 0, sizeof(ipt));
253: e = ADD_STRUCT(pos, struct ipt_entry,
254: .target_offset = target_offset,
255: .next_offset = entry_size,
256: .ip = {
257: .proto = IPPROTO_ESP,
258: },
259: );
260: if (!host2in(entry->lhost, &e->ip.dst, &e->ip.dmsk) ||
261: !host2in(entry->rhost, &e->ip.src, &e->ip.smsk))
262: {
263: return FALSE;
264: }
265: ADD_STRUCT(pos, struct ipt_entry_match,
266: .u = {
267: .user = {
268: .match_size = match_size,
269: .name = "esp",
270: },
271: },
272: );
273: ADD_STRUCT(pos, struct xt_esp,
274: .spis = { htonl(entry->spi), htonl(entry->spi) },
275: );
276: ADD_STRUCT(pos, struct ipt_entry_target,
277: .u = {
278: .user = {
279: .target_size = target_size,
280: .name = "MARK",
281: .revision = 2,
282: },
283: },
284: );
285: ADD_STRUCT(pos, struct xt_mark_tginfo2,
286: .mark = entry->mark,
287: .mask = ~0,
288: );
289: return manage_rule(ipth, "PREROUTING", add, e);
290: }
291:
292: /**
293: * Add rule marking ESP packets to match the correct policy
294: */
295: static bool manage_pre(struct iptc_handle *ipth, entry_t *entry, bool add)
296: {
297: if (entry->encap)
298: {
299: return manage_pre_esp_in_udp(ipth, entry, add);
300: }
301: return manage_pre_esp(ipth, entry, add);
302: }
303:
304: /**
305: * Add rule handling outbound traffic to use correct mark
306: */
307: static bool manage_out(struct iptc_handle *ipth, entry_t *entry, bool add)
308: {
309: uint16_t target_offset = XT_ALIGN(sizeof(struct ipt_entry));
310: uint16_t target_size = XT_ALIGN(sizeof(struct ipt_entry_target)) +
311: XT_ALIGN(sizeof(struct xt_mark_tginfo2));
312: uint16_t entry_size = target_offset + target_size;
313: u_char ipt[entry_size], *pos = ipt;
314: struct ipt_entry *e;
315:
316: memset(ipt, 0, sizeof(ipt));
317: e = ADD_STRUCT(pos, struct ipt_entry,
318: .target_offset = target_offset,
319: .next_offset = entry_size,
320: );
321: ADD_STRUCT(pos, struct ipt_entry_target,
322: .u = {
323: .user = {
324: .target_size = target_size,
325: .name = "MARK",
326: .revision = 2,
327: },
328: },
329: );
330: ADD_STRUCT(pos, struct xt_mark_tginfo2,
331: .mark = entry->mark,
332: .mask = ~0,
333: );
334:
335: enumerator_t *enumerator;
336: traffic_selector_t *ts;
337:
338: enumerator = array_create_enumerator(entry->rts);
339: while (enumerator->enumerate(enumerator, &ts))
340: {
341: if (!ts2in(ts, &e->ip.dst, &e->ip.dmsk))
342: {
343: continue;
344: }
345: if (e->ip.dst.s_addr == 0xffffffff ||
346: e->ip.dst.s_addr == entry->broadcast ||
347: memeq(&e->ip.dst.s_addr, "\xe0", 1))
348: {
349: /* skip broadcast/multicast selectors, they are shared and the mark
350: * is set by the socket we use for reinjection */
351: continue;
352: }
353: if (!manage_rule(ipth, "PREROUTING", add, e) ||
354: !manage_rule(ipth, "OUTPUT", add, e))
355: {
356: enumerator->destroy(enumerator);
357: return FALSE;
358: }
359: }
360: enumerator->destroy(enumerator);
361:
362: return TRUE;
363: }
364:
365: /**
366: * Check if config is whitelisted to reinject traffic
367: */
368: static bool is_reinject_config(private_forecast_listener_t *this, char *name)
369: {
370: enumerator_t *enumerator;
371: bool reinject = FALSE;
372: char *token;
373:
374: enumerator = enumerator_create_token(this->reinject_configs, ",", " ");
375: while (enumerator->enumerate(enumerator, &token))
376: {
377: if (streq(token, name))
378: {
379: reinject = TRUE;
380: break;
381: }
382: }
383: enumerator->destroy(enumerator);
384:
385: return reinject;
386: }
387:
388: /**
389: * Add rules and entry for given CHILD_SA
390: */
391: static bool add_entry(private_forecast_listener_t *this,
392: struct iptc_handle *ipth, host_t *lhost, host_t *rhost,
393: child_sa_t *child_sa, bool encap)
394: {
395: enumerator_t *enumerator;
396: traffic_selector_t *ts;
397: entry_t *entry;
398:
399: INIT(entry,
400: .lts = array_create(0, 0),
401: .rts = array_create(0, 0),
402: .lhost = lhost->clone(lhost),
403: .rhost = rhost->clone(rhost),
404: .spi = child_sa->get_spi(child_sa, TRUE),
405: .encap = encap,
406: .mark = child_sa->get_mark(child_sa, TRUE).value,
407: .reinject = is_reinject_config(this, child_sa->get_name(child_sa)),
408: .broadcast = this->broadcast,
409: );
410:
411: enumerator = child_sa->create_ts_enumerator(child_sa, TRUE);
412: while (enumerator->enumerate(enumerator, &ts))
413: {
414: array_insert(entry->lts, ARRAY_TAIL, ts->clone(ts));
415: }
416: enumerator->destroy(enumerator);
417:
418: enumerator = child_sa->create_ts_enumerator(child_sa, FALSE);
419: while (enumerator->enumerate(enumerator, &ts))
420: {
421: array_insert(entry->rts, ARRAY_TAIL, ts->clone(ts));
422: }
423: enumerator->destroy(enumerator);
424:
425: if (manage_pre(ipth, entry, TRUE) &&
426: manage_out(ipth, entry, TRUE))
427: {
428: this->lock->write_lock(this->lock);
429: this->entries->insert_last(this->entries, entry);
430: this->lock->unlock(this->lock);
431: return TRUE;
432: }
433: entry_destroy(entry);
434: return FALSE;
435: }
436:
437: /**
438: * Remove an entry and rules for a given mark
439: */
440: static bool remove_entry(private_forecast_listener_t *this,
441: struct iptc_handle *ipth, child_sa_t *child_sa)
442: {
443: enumerator_t *enumerator;
444: entry_t *entry;
445: bool done = FALSE;
446:
447: this->lock->write_lock(this->lock);
448: enumerator = this->entries->create_enumerator(this->entries);
449: while (enumerator->enumerate(enumerator, &entry))
450: {
451: if (entry->mark == child_sa->get_mark(child_sa, TRUE).value)
452: {
453: this->entries->remove_at(this->entries, enumerator);
454: if (manage_pre(ipth, entry, FALSE) &&
455: manage_out(ipth, entry, FALSE))
456: {
457: done = TRUE;
458: }
459: entry_destroy(entry);
460: break;
461: }
462: }
463: enumerator->destroy(enumerator);
464: this->lock->unlock(this->lock);
465:
466: return done;
467: }
468:
469: /**
470: * Initialize iptables handle, log error
471: */
472: static struct iptc_handle* init_handle()
473: {
474: struct iptc_handle *ipth;
475:
476: ipth = iptc_init("mangle");
477: if (ipth)
478: {
479: return ipth;
480: }
481: DBG1(DBG_CFG, "initializing iptables failed: %s", iptc_strerror(errno));
482: return NULL;
483: }
484:
485: /**
486: * Commit iptables rules, log error
487: */
488: static bool commit_handle(struct iptc_handle *ipth)
489: {
490: if (iptc_commit(ipth))
491: {
492: return TRUE;
493: }
494: DBG1(DBG_CFG, "forecast iptables commit failed: %s", iptc_strerror(errno));
495: return FALSE;
496: }
497:
498: /**
499: * Check if we should handle the given CHILD_SA
500: */
501: static bool handle_sa(child_sa_t *child_sa)
502: {
503: return child_sa->get_mark(child_sa, TRUE).value &&
504: child_sa->get_mark(child_sa, FALSE).value;
505: }
506:
507: METHOD(listener_t, child_updown, bool,
508: private_forecast_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
509: bool up)
510: {
511: struct iptc_handle *ipth;
512: host_t *lhost, *rhost;
513: bool encap;
514:
515: lhost = ike_sa->get_my_host(ike_sa);
516: rhost = ike_sa->get_other_host(ike_sa);
517: encap = child_sa->has_encap(child_sa);
518:
519: if (handle_sa(child_sa))
520: {
521: ipth = init_handle();
522: if (ipth)
523: {
524: if (up)
525: {
526: if (add_entry(this, ipth, lhost, rhost, child_sa, encap))
527: {
528: commit_handle(ipth);
529: }
530: }
531: else
532: {
533: if (remove_entry(this, ipth, child_sa))
534: {
535: commit_handle(ipth);
536: }
537: }
538: iptc_free(ipth);
539: }
540: }
541: return TRUE;
542: }
543:
544: METHOD(listener_t, child_rekey, bool,
545: private_forecast_listener_t *this, ike_sa_t *ike_sa,
546: child_sa_t *old, child_sa_t *new)
547: {
548: struct iptc_handle *ipth;;
549: host_t *lhost, *rhost;
550:
551: lhost = ike_sa->get_my_host(ike_sa);
552: rhost = ike_sa->get_other_host(ike_sa);
553:
554: if (handle_sa(old))
555: {
556: ipth = init_handle();
557: if (ipth)
558: {
559: if (remove_entry(this, ipth, old) &&
560: add_entry(this, ipth, lhost, rhost, new, new->has_encap(new)))
561: {
562: commit_handle(ipth);
563: }
564: iptc_free(ipth);
565: }
566: }
567: return TRUE;
568: }
569:
570: METHOD(listener_t, ike_update, bool,
571: private_forecast_listener_t *this, ike_sa_t *ike_sa,
1.1.1.2 ! misho 572: host_t *local, host_t *remote)
1.1 misho 573: {
574: struct iptc_handle *ipth;
575: enumerator_t *enumerator;
576: child_sa_t *child_sa;
577: bool encap;
578:
579: /* during ike_update(), has_encap() on the CHILD_SA has not yet been
580: * updated, but shows the old state. */
581: encap = ike_sa->has_condition(ike_sa, COND_NAT_ANY);
582:
583: enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
584: while (enumerator->enumerate(enumerator, &child_sa))
585: {
586: if (handle_sa(child_sa))
587: {
588: ipth = init_handle();
589: if (ipth)
590: {
591: if (remove_entry(this, ipth, child_sa) &&
1.1.1.2 ! misho 592: add_entry(this, ipth, local, remote, child_sa, encap))
1.1 misho 593: {
594: commit_handle(ipth);
595: }
596: iptc_free(ipth);
597: }
598: }
599: }
600: enumerator->destroy(enumerator);
601:
602: return TRUE;
603: }
604:
605: CALLBACK(ts_filter, bool,
606: entry_t *entry, enumerator_t *orig, va_list args)
607: {
608: traffic_selector_t *ts, **out;
609: uint32_t *mark;
610: bool *reinject;
611:
612: VA_ARGS_VGET(args, out, mark, reinject);
613:
614: if (orig->enumerate(orig, &ts))
615: {
616: *out = ts;
617: *mark = entry->mark;
618: *reinject = entry->reinject;
619: return TRUE;
620: }
621: return FALSE;
622: }
623:
624: /**
625: * Create inner enumerator over local traffic selectors
626: */
627: static enumerator_t* create_inner_local(entry_t *entry, rwlock_t *lock)
628: {
629: return enumerator_create_filter(array_create_enumerator(entry->lts),
630: ts_filter, entry, NULL);
631: }
632:
633: /**
634: * Create inner enumerator over remote traffic selectors
635: */
636: static enumerator_t* create_inner_remote(entry_t *entry, rwlock_t *lock)
637: {
638: return enumerator_create_filter(array_create_enumerator(entry->rts),
639: ts_filter, entry, NULL);
640: }
641:
642: METHOD(forecast_listener_t, create_enumerator, enumerator_t*,
643: private_forecast_listener_t *this, bool local)
644: {
645: this->lock->read_lock(this->lock);
646: return enumerator_create_nested(
647: this->entries->create_enumerator(this->entries),
648: (void*)(local ? create_inner_local : create_inner_remote),
649: this->lock, (void*)this->lock->unlock);
650: }
651:
652: METHOD(forecast_listener_t, set_broadcast, void,
653: private_forecast_listener_t *this, host_t *bcast)
654: {
655: if (bcast->get_family(bcast) == AF_INET)
656: {
657: struct sockaddr_in *in;
658:
659: in = (struct sockaddr_in*)bcast->get_sockaddr(bcast);
660: this->broadcast = in->sin_addr.s_addr;
661: }
662: }
663:
664: METHOD(forecast_listener_t, destroy, void,
665: private_forecast_listener_t *this)
666: {
667: this->entries->destroy(this->entries);
668: this->lock->destroy(this->lock);
669: free(this);
670: }
671:
672: /**
673: * See header
674: */
675: forecast_listener_t *forecast_listener_create()
676: {
677: private_forecast_listener_t *this;
678:
679: INIT(this,
680: .public = {
681: .listener = {
682: .ike_update = _ike_update,
683: .child_updown = _child_updown,
684: .child_rekey = _child_rekey,
685: },
686: .create_enumerator = _create_enumerator,
687: .set_broadcast = _set_broadcast,
688: .destroy = _destroy,
689: },
690: .entries = linked_list_create(),
691: .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
692: .reinject_configs = lib->settings->get_str(lib->settings,
693: "%s.plugins.forecast.reinject", "", lib->ns),
694: );
695:
696: return &this->public;
697: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>