ipsec_processor.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. /*
  2. * Copyright (C) 2012 Tobias Brunner
  3. * HSR Hochschule fuer Technik Rapperswil
  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. #include "ipsec.h"
  16. #include "ipsec_processor.h"
  17. #include <utils/debug.h>
  18. #include <library.h>
  19. #include <threading/rwlock.h>
  20. #include <collections/blocking_queue.h>
  21. #include <processing/jobs/callback_job.h>
  22. typedef struct private_ipsec_processor_t private_ipsec_processor_t;
  23. /**
  24. * Private additions to ipsec_processor_t.
  25. */
  26. struct private_ipsec_processor_t {
  27. /**
  28. * Public members
  29. */
  30. ipsec_processor_t public;
  31. /**
  32. * Queue for inbound packets (esp_packet_t*)
  33. */
  34. blocking_queue_t *inbound_queue;
  35. /**
  36. * Queue for outbound packets (ip_packet_t*)
  37. */
  38. blocking_queue_t *outbound_queue;
  39. /**
  40. * Registered inbound callback
  41. */
  42. struct {
  43. ipsec_inbound_cb_t cb;
  44. void *data;
  45. } inbound;
  46. /**
  47. * Registered outbound callback
  48. */
  49. struct {
  50. ipsec_outbound_cb_t cb;
  51. void *data;
  52. } outbound;
  53. /**
  54. * Lock used to synchronize access to the callbacks
  55. */
  56. rwlock_t *lock;
  57. };
  58. /**
  59. * Deliver an inbound IP packet to the registered listener
  60. */
  61. static void deliver_inbound(private_ipsec_processor_t *this,
  62. esp_packet_t *packet)
  63. {
  64. this->lock->read_lock(this->lock);
  65. if (this->inbound.cb)
  66. {
  67. this->inbound.cb(this->inbound.data, packet->extract_payload(packet));
  68. }
  69. else
  70. {
  71. DBG2(DBG_ESP, "no inbound callback registered, dropping packet");
  72. }
  73. packet->destroy(packet);
  74. this->lock->unlock(this->lock);
  75. }
  76. /**
  77. * Processes inbound packets
  78. */
  79. static job_requeue_t process_inbound(private_ipsec_processor_t *this)
  80. {
  81. esp_packet_t *packet;
  82. ip_packet_t *ip_packet;
  83. ipsec_sa_t *sa;
  84. uint8_t next_header;
  85. uint32_t spi, reqid;
  86. packet = (esp_packet_t*)this->inbound_queue->dequeue(this->inbound_queue);
  87. if (!packet->parse_header(packet, &spi))
  88. {
  89. packet->destroy(packet);
  90. return JOB_REQUEUE_DIRECT;
  91. }
  92. sa = ipsec->sas->checkout_by_spi(ipsec->sas, spi,
  93. packet->get_destination(packet));
  94. if (!sa)
  95. {
  96. DBG2(DBG_ESP, "inbound ESP packet does not belong to an installed SA");
  97. packet->destroy(packet);
  98. return JOB_REQUEUE_DIRECT;
  99. }
  100. if (!sa->is_inbound(sa))
  101. {
  102. DBG1(DBG_ESP, "error: IPsec SA is not inbound");
  103. packet->destroy(packet);
  104. ipsec->sas->checkin(ipsec->sas, sa);
  105. return JOB_REQUEUE_DIRECT;
  106. }
  107. if (packet->decrypt(packet, sa->get_esp_context(sa)) != SUCCESS)
  108. {
  109. ipsec->sas->checkin(ipsec->sas, sa);
  110. packet->destroy(packet);
  111. return JOB_REQUEUE_DIRECT;
  112. }
  113. ip_packet = packet->get_payload(packet);
  114. sa->update_usestats(sa, ip_packet->get_encoding(ip_packet).len);
  115. reqid = sa->get_reqid(sa);
  116. ipsec->sas->checkin(ipsec->sas, sa);
  117. next_header = packet->get_next_header(packet);
  118. switch (next_header)
  119. {
  120. case IPPROTO_IPIP:
  121. case IPPROTO_IPV6:
  122. {
  123. ipsec_policy_t *policy;
  124. policy = ipsec->policies->find_by_packet(ipsec->policies,
  125. ip_packet, TRUE, reqid);
  126. if (policy)
  127. {
  128. deliver_inbound(this, packet);
  129. policy->destroy(policy);
  130. break;
  131. }
  132. DBG1(DBG_ESP, "discarding inbound IP packet %#H == %#H [%hhu] due "
  133. "to policy", ip_packet->get_source(ip_packet),
  134. ip_packet->get_destination(ip_packet),
  135. ip_packet->get_next_header(ip_packet));
  136. /* no matching policy found, fall-through */
  137. }
  138. case IPPROTO_NONE:
  139. /* discard dummy packets */
  140. /* fall-through */
  141. default:
  142. packet->destroy(packet);
  143. break;
  144. }
  145. return JOB_REQUEUE_DIRECT;
  146. }
  147. /**
  148. * Send an ESP packet using the registered outbound callback
  149. */
  150. static void send_outbound(private_ipsec_processor_t *this,
  151. esp_packet_t *packet)
  152. {
  153. this->lock->read_lock(this->lock);
  154. if (this->outbound.cb)
  155. {
  156. this->outbound.cb(this->outbound.data, packet);
  157. }
  158. else
  159. {
  160. DBG2(DBG_ESP, "no outbound callback registered, dropping packet");
  161. packet->destroy(packet);
  162. }
  163. this->lock->unlock(this->lock);
  164. }
  165. /**
  166. * Processes outbound packets
  167. */
  168. static job_requeue_t process_outbound(private_ipsec_processor_t *this)
  169. {
  170. ipsec_policy_t *policy;
  171. esp_packet_t *esp_packet;
  172. ip_packet_t *packet;
  173. ipsec_sa_t *sa;
  174. host_t *src, *dst;
  175. packet = (ip_packet_t*)this->outbound_queue->dequeue(this->outbound_queue);
  176. policy = ipsec->policies->find_by_packet(ipsec->policies, packet, FALSE, 0);
  177. if (!policy)
  178. {
  179. DBG2(DBG_ESP, "no matching outbound IPsec policy for %#H == %#H [%hhu]",
  180. packet->get_source(packet), packet->get_destination(packet),
  181. packet->get_next_header(packet));
  182. packet->destroy(packet);
  183. return JOB_REQUEUE_DIRECT;
  184. }
  185. sa = ipsec->sas->checkout_by_reqid(ipsec->sas, policy->get_reqid(policy),
  186. FALSE);
  187. if (!sa)
  188. { /* TODO-IPSEC: send an acquire to uppper layer */
  189. DBG1(DBG_ESP, "could not find an outbound IPsec SA for reqid {%u}, "
  190. "dropping packet", policy->get_reqid(policy));
  191. packet->destroy(packet);
  192. policy->destroy(policy);
  193. return JOB_REQUEUE_DIRECT;
  194. }
  195. src = sa->get_source(sa);
  196. dst = sa->get_destination(sa);
  197. esp_packet = esp_packet_create_from_payload(src->clone(src),
  198. dst->clone(dst), packet);
  199. if (esp_packet->encrypt(esp_packet, sa->get_esp_context(sa),
  200. sa->get_spi(sa)) != SUCCESS)
  201. {
  202. ipsec->sas->checkin(ipsec->sas, sa);
  203. esp_packet->destroy(esp_packet);
  204. policy->destroy(policy);
  205. return JOB_REQUEUE_DIRECT;
  206. }
  207. sa->update_usestats(sa, packet->get_encoding(packet).len);
  208. ipsec->sas->checkin(ipsec->sas, sa);
  209. policy->destroy(policy);
  210. send_outbound(this, esp_packet);
  211. return JOB_REQUEUE_DIRECT;
  212. }
  213. METHOD(ipsec_processor_t, queue_inbound, void,
  214. private_ipsec_processor_t *this, esp_packet_t *packet)
  215. {
  216. this->inbound_queue->enqueue(this->inbound_queue, packet);
  217. }
  218. METHOD(ipsec_processor_t, queue_outbound, void,
  219. private_ipsec_processor_t *this, ip_packet_t *packet)
  220. {
  221. this->outbound_queue->enqueue(this->outbound_queue, packet);
  222. }
  223. METHOD(ipsec_processor_t, register_inbound, void,
  224. private_ipsec_processor_t *this, ipsec_inbound_cb_t cb, void *data)
  225. {
  226. this->lock->write_lock(this->lock);
  227. this->inbound.cb = cb;
  228. this->inbound.data = data;
  229. this->lock->unlock(this->lock);
  230. }
  231. METHOD(ipsec_processor_t, unregister_inbound, void,
  232. private_ipsec_processor_t *this, ipsec_inbound_cb_t cb)
  233. {
  234. this->lock->write_lock(this->lock);
  235. if (this->inbound.cb == cb)
  236. {
  237. this->inbound.cb = NULL;
  238. }
  239. this->lock->unlock(this->lock);
  240. }
  241. METHOD(ipsec_processor_t, register_outbound, void,
  242. private_ipsec_processor_t *this, ipsec_outbound_cb_t cb, void *data)
  243. {
  244. this->lock->write_lock(this->lock);
  245. this->outbound.cb = cb;
  246. this->outbound.data = data;
  247. this->lock->unlock(this->lock);
  248. }
  249. METHOD(ipsec_processor_t, unregister_outbound, void,
  250. private_ipsec_processor_t *this, ipsec_outbound_cb_t cb)
  251. {
  252. this->lock->write_lock(this->lock);
  253. if (this->outbound.cb == cb)
  254. {
  255. this->outbound.cb = NULL;
  256. }
  257. this->lock->unlock(this->lock);
  258. }
  259. METHOD(ipsec_processor_t, destroy, void,
  260. private_ipsec_processor_t *this)
  261. {
  262. this->inbound_queue->destroy_offset(this->inbound_queue,
  263. offsetof(esp_packet_t, destroy));
  264. this->outbound_queue->destroy_offset(this->outbound_queue,
  265. offsetof(ip_packet_t, destroy));
  266. this->lock->destroy(this->lock);
  267. free(this);
  268. }
  269. /**
  270. * Described in header.
  271. */
  272. ipsec_processor_t *ipsec_processor_create()
  273. {
  274. private_ipsec_processor_t *this;
  275. INIT(this,
  276. .public = {
  277. .queue_inbound = _queue_inbound,
  278. .queue_outbound = _queue_outbound,
  279. .register_inbound = _register_inbound,
  280. .unregister_inbound = _unregister_inbound,
  281. .register_outbound = _register_outbound,
  282. .unregister_outbound = _unregister_outbound,
  283. .destroy = _destroy,
  284. },
  285. .inbound_queue = blocking_queue_create(),
  286. .outbound_queue = blocking_queue_create(),
  287. .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
  288. );
  289. lib->processor->queue_job(lib->processor,
  290. (job_t*)callback_job_create((callback_job_cb_t)process_inbound, this,
  291. NULL, (callback_job_cancel_t)return_false));
  292. lib->processor->queue_job(lib->processor,
  293. (job_t*)callback_job_create((callback_job_cb_t)process_outbound, this,
  294. NULL, (callback_job_cancel_t)return_false));
  295. return &this->public;
  296. }