You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

342 lines
10 KiB

10 years ago
  1. /* -*- coding: utf-8 -*-
  2. * ----------------------------------------------------------------------
  3. * Copyright © 2010-2012, RedJack, LLC.
  4. * All rights reserved.
  5. *
  6. * Please see the LICENSE.txt file in this distribution for license
  7. * details.
  8. * ----------------------------------------------------------------------
  9. */
  10. #include <string.h>
  11. #include <libcork/core.h>
  12. #include "ipset/bdd/nodes.h"
  13. #include "ipset/bits.h"
  14. #include "ipset/ipset.h"
  15. #include "ipset/logging.h"
  16. #define IPV4_BIT_SIZE 32
  17. #define IPV6_BIT_SIZE 128
  18. /* Forward declarations */
  19. static void
  20. process_assignment(struct ipset_iterator *iterator);
  21. static void
  22. expand_ipv6(struct ipset_iterator *iterator);
  23. /**
  24. * Find the highest non-EITHER bit in an assignment, starting from the
  25. * given bit index.
  26. */
  27. static unsigned int
  28. find_last_non_either_bit(struct ipset_assignment *assignment,
  29. unsigned int starting_bit)
  30. {
  31. unsigned int i;
  32. for (i = starting_bit; i >= 1; i--) {
  33. enum ipset_tribool value = ipset_assignment_get(assignment, i);
  34. if (value != IPSET_EITHER) {
  35. return i;
  36. }
  37. }
  38. return 0;
  39. }
  40. /**
  41. * Create a generic IP address object from the current expanded
  42. * assignment.
  43. */
  44. static void
  45. create_ip_address(struct ipset_iterator *iterator)
  46. {
  47. struct cork_ip *addr = &iterator->addr;
  48. struct ipset_expanded_assignment *exp = iterator->assignment_iterator;
  49. /* Initialize the address to all 0 bits. */
  50. memset(addr, 0, sizeof(struct cork_ip));
  51. /* Check variable 0 to see if this is an IPv4 or IPv6 address. */
  52. addr->version = IPSET_BIT_GET(exp->values.buf, 0)? 4: 6;
  53. /* Copy bits from the expanded assignment. The number of bits to
  54. * copy is given as the current netmask. We'll have calculated that
  55. * already based on the non-expanded assignment. */
  56. unsigned int i;
  57. for (i = 0; i < iterator->cidr_prefix; i++) {
  58. IPSET_BIT_SET(&addr->ip, i, IPSET_BIT_GET(exp->values.buf, i+1));
  59. }
  60. #if IPSET_DEBUG
  61. char buf[CORK_IP_STRING_LENGTH];
  62. cork_ip_to_raw_string(addr, buf);
  63. DEBUG("Current IP address is %s/%u", buf, iterator->cidr_prefix);
  64. #endif
  65. }
  66. /**
  67. * Advance the BDD iterator, taking into account that some assignments
  68. * need to be expanded twice.
  69. */
  70. static void
  71. advance_assignment(struct ipset_iterator *iterator)
  72. {
  73. /* Check the current state of the iterator to determine how to
  74. * advance. */
  75. /* In most cases, the assignment we just finished only needed to be
  76. * expanded once. So we move on to the next assignment and process
  77. * it. */
  78. if (CORK_LIKELY(iterator->multiple_expansion_state ==
  79. IPSET_ITERATOR_NORMAL))
  80. {
  81. ipset_bdd_iterator_advance(iterator->bdd_iterator);
  82. process_assignment(iterator);
  83. return;
  84. }
  85. /* If the assignment needs to be expanded twice, we'll do the IPv4
  86. * expansion first. If that's what we've just finished, do the IPv6
  87. * expansion next. */
  88. if (iterator->multiple_expansion_state == IPSET_ITERATOR_MULTIPLE_IPV4) {
  89. DEBUG("Expanding IPv6 second");
  90. iterator->multiple_expansion_state = IPSET_ITERATOR_MULTIPLE_IPV6;
  91. ipset_assignment_set
  92. (iterator->bdd_iterator->assignment, 0, IPSET_FALSE);
  93. expand_ipv6(iterator);
  94. return;
  95. }
  96. /* If we've just finished the IPv6 expansion, then we've finished
  97. * with this assignment. Before moving on to the next one, we have
  98. * to reset variable 0 to EITHER (which it was before we started
  99. * this whole mess). */
  100. if (iterator->multiple_expansion_state == IPSET_ITERATOR_MULTIPLE_IPV6) {
  101. DEBUG("Finished both expansions");
  102. ipset_assignment_set
  103. (iterator->bdd_iterator->assignment, 0, IPSET_EITHER);
  104. ipset_bdd_iterator_advance(iterator->bdd_iterator);
  105. process_assignment(iterator);
  106. return;
  107. }
  108. }
  109. /**
  110. * Process the current expanded assignment in the current BDD
  111. * assignment.
  112. */
  113. static void
  114. process_expanded_assignment(struct ipset_iterator *iterator)
  115. {
  116. if (iterator->assignment_iterator->finished) {
  117. /* If there isn't anything in the expanded assignment, advance
  118. * to the next BDD assignment. */
  119. DEBUG("Expanded assignment is finished");
  120. ipset_expanded_assignment_free(iterator->assignment_iterator);
  121. iterator->assignment_iterator = NULL;
  122. advance_assignment(iterator);
  123. } else {
  124. /* Otherwise, we've found a fully expanded assignment, so create
  125. * an IP address for it and return. */
  126. create_ip_address(iterator);
  127. }
  128. }
  129. /**
  130. * Expand the current assignment as IPv4 addresses.
  131. */
  132. static void
  133. expand_ipv4(struct ipset_iterator *iterator)
  134. {
  135. unsigned int last_bit;
  136. if (iterator->summarize) {
  137. last_bit = find_last_non_either_bit
  138. (iterator->bdd_iterator->assignment, IPV4_BIT_SIZE);
  139. DEBUG("Last non-either bit is %u", last_bit);
  140. } else {
  141. last_bit = IPV4_BIT_SIZE;
  142. }
  143. iterator->assignment_iterator =
  144. ipset_assignment_expand
  145. (iterator->bdd_iterator->assignment, last_bit + 1);
  146. iterator->cidr_prefix = last_bit;
  147. process_expanded_assignment(iterator);
  148. }
  149. /**
  150. * Expand the current assignment as IPv4 addresses.
  151. */
  152. static void
  153. expand_ipv6(struct ipset_iterator *iterator)
  154. {
  155. unsigned int last_bit;
  156. if (iterator->summarize) {
  157. last_bit = find_last_non_either_bit
  158. (iterator->bdd_iterator->assignment, IPV6_BIT_SIZE);
  159. DEBUG("Last non-either bit is %u", last_bit);
  160. } else {
  161. last_bit = IPV6_BIT_SIZE;
  162. }
  163. iterator->assignment_iterator =
  164. ipset_assignment_expand
  165. (iterator->bdd_iterator->assignment, last_bit + 1);
  166. iterator->cidr_prefix = last_bit;
  167. process_expanded_assignment(iterator);
  168. }
  169. /**
  170. * Process the current assignment in the BDD iterator.
  171. */
  172. static void
  173. process_assignment(struct ipset_iterator *iterator)
  174. {
  175. while (!iterator->bdd_iterator->finished) {
  176. if (iterator->bdd_iterator->value == iterator->desired_value) {
  177. /* If the BDD iterator hasn't finished, and the result of
  178. * the function with this assignment matches what the caller
  179. * wants, then we've found an assignment to generate IP
  180. * addresses from.
  181. *
  182. * Try to expand this assignment, and process the first
  183. * expanded assignment. We want 32 + 1 variables if the
  184. * current address is IPv4; 128 + 1 if it's IPv6. */
  185. DEBUG("Got a matching BDD assignment");
  186. enum ipset_tribool address_type = ipset_assignment_get
  187. (iterator->bdd_iterator->assignment, 0);
  188. if (address_type == IPSET_FALSE) {
  189. /* FALSE means IPv6*/
  190. DEBUG("Assignment is IPv6");
  191. iterator->multiple_expansion_state = IPSET_ITERATOR_NORMAL;
  192. expand_ipv6(iterator);
  193. return;
  194. } else if (address_type == IPSET_TRUE) {
  195. /* TRUE means IPv4*/
  196. DEBUG("Assignment is IPv4");
  197. iterator->multiple_expansion_state = IPSET_ITERATOR_NORMAL;
  198. expand_ipv4(iterator);
  199. return;
  200. } else {
  201. /* EITHER means that this assignment contains both IPv4
  202. * and IPv6 addresses. Expand it as IPv4 first. */
  203. DEBUG("Assignment is both IPv4 and IPv6");
  204. DEBUG("Expanding IPv4 first");
  205. iterator->multiple_expansion_state =
  206. IPSET_ITERATOR_MULTIPLE_IPV4;
  207. ipset_assignment_set
  208. (iterator->bdd_iterator->assignment, 0, IPSET_TRUE);
  209. expand_ipv4(iterator);
  210. return;
  211. }
  212. }
  213. /* The BDD iterator has a value, but it doesn't match the one we
  214. * want. Advance the BDD iterator and try again. */
  215. DEBUG("Value is %d, skipping", iterator->bdd_iterator->value);
  216. ipset_bdd_iterator_advance(iterator->bdd_iterator);
  217. }
  218. /* If we fall through, then the BDD iterator has finished. That
  219. * means there's nothing left for the set iterator. */
  220. DEBUG("Set iterator is finished");
  221. ipset_expanded_assignment_free(iterator->assignment_iterator);
  222. iterator->assignment_iterator = NULL;
  223. ipset_bdd_iterator_free(iterator->bdd_iterator);
  224. iterator->bdd_iterator = NULL;
  225. iterator->finished = true;
  226. }
  227. static struct ipset_iterator *
  228. create_iterator(struct ip_set *set, bool desired_value, bool summarize)
  229. {
  230. /* First allocate the iterator itself. */
  231. struct ipset_iterator *iterator = cork_new(struct ipset_iterator);
  232. iterator->finished = false;
  233. iterator->assignment_iterator = NULL;
  234. iterator->desired_value = desired_value;
  235. iterator->summarize = summarize;
  236. /* Then create the iterator that returns each BDD assignment. */
  237. DEBUG("Iterating set");
  238. iterator->bdd_iterator = ipset_node_iterate(set->cache, set->set_bdd);
  239. /* Then drill down from the current BDD assignment, creating an
  240. * expanded assignment for it. */
  241. process_assignment(iterator);
  242. return iterator;
  243. }
  244. struct ipset_iterator *
  245. ipset_iterate(struct ip_set *set, bool desired_value)
  246. {
  247. return create_iterator(set, desired_value, false);
  248. }
  249. struct ipset_iterator *
  250. ipset_iterate_networks(struct ip_set *set, bool desired_value)
  251. {
  252. return create_iterator(set, desired_value, true);
  253. }
  254. void
  255. ipset_iterator_free(struct ipset_iterator *iterator)
  256. {
  257. if (iterator->bdd_iterator != NULL) {
  258. ipset_bdd_iterator_free(iterator->bdd_iterator);
  259. }
  260. if (iterator->assignment_iterator != NULL) {
  261. ipset_expanded_assignment_free(iterator->assignment_iterator);
  262. }
  263. free(iterator);
  264. }
  265. void
  266. ipset_iterator_advance(struct ipset_iterator *iterator)
  267. {
  268. /* If we're already at the end of the iterator, don't do anything. */
  269. if (CORK_UNLIKELY(iterator->finished)) {
  270. return;
  271. }
  272. /* Otherwise, advance the expanded assignment iterator to the next
  273. * assignment, and then drill down into it. */
  274. DEBUG("Advancing set iterator");
  275. ipset_expanded_assignment_advance(iterator->assignment_iterator);
  276. process_expanded_assignment(iterator);
  277. }