1/* -*- coding: utf-8 -*-
2 * ----------------------------------------------------------------------
3 * Copyright © 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
11#include <libcork/core.h>
12
13#include "ipset/bdd/nodes.h"
14#include "ipset/bits.h"
15#include "ipset/errors.h"
16#include "ipset/ipset.h"
17
18
19/**
20 * Given a BDD variable number, return the index of the corresponding
21 * bit in an IP address.  IPv4 addresses use variables 1-32; IPv6
22 * addresses use 1-128.  (Variable 0 is used to identify the kind of
23 * address — TRUE for IPv4, FALSE for IPv6.)
24 */
25
26static unsigned int
27IPSET_NAME(bit_for_var)(ipset_variable var)
28{
29    return (var - 1);
30}
31
32
33/**
34 * An assignment function that can be used to evaluate an IP set BDD.
35 */
36
37static bool
38IPSET_NAME(assignment)(const void *addr, ipset_variable var)
39{
40    if (var == 0) {
41        return IP_DISCRIMINATOR_VALUE;
42    } else {
43        unsigned int  bit = IPSET_NAME(bit_for_var)(var);
44        return IPSET_BIT_GET(addr, bit);
45    }
46}
47
48
49bool
50IPSET_PRENAME(contains)(const struct ip_set *set, CORK_IP *elem)
51{
52    return ipset_node_evaluate
53        (set->cache, set->set_bdd, IPSET_NAME(assignment), elem);
54}
55
56
57bool
58IPSET_NAME(add_network)(struct ip_set *set, CORK_IP *elem,
59                        unsigned int cidr_prefix)
60{
61    /* Special case — the BDD for a netmask that's out of range never
62     * evaluates to true. */
63    if (cidr_prefix > IP_BIT_SIZE) {
64        cork_error_set
65            (IPSET_ERROR, IPSET_PARSE_ERROR,
66             "CIDR block %u out of range [0..%u]", cidr_prefix, IP_BIT_SIZE);
67        return false;
68    }
69
70    ipset_node_id  new_bdd =
71        ipset_node_insert
72        (set->cache, set->set_bdd,
73         IPSET_NAME(assignment), elem, cidr_prefix + 1, 1);
74    bool  result = (new_bdd == set->set_bdd);
75    ipset_node_decref(set->cache, set->set_bdd);
76    set->set_bdd = new_bdd;
77    return result;
78}
79
80
81bool
82IPSET_NAME(add)(struct ip_set *set, CORK_IP *elem)
83{
84    ipset_node_id  new_bdd =
85        ipset_node_insert
86        (set->cache, set->set_bdd,
87         IPSET_NAME(assignment), elem, IP_BIT_SIZE + 1, 1);
88    bool  result = (new_bdd == set->set_bdd);
89    ipset_node_decref(set->cache, set->set_bdd);
90    set->set_bdd = new_bdd;
91    return result;
92}
93
94
95bool
96IPSET_NAME(remove)(struct ip_set *set, CORK_IP *elem)
97{
98    ipset_node_id  new_bdd =
99        ipset_node_insert
100        (set->cache, set->set_bdd,
101         IPSET_NAME(assignment), elem, IP_BIT_SIZE + 1, 0);
102    bool  result = (new_bdd == set->set_bdd);
103    ipset_node_decref(set->cache, set->set_bdd);
104    set->set_bdd = new_bdd;
105    return result;
106}
107
108
109bool
110IPSET_NAME(remove_network)(struct ip_set *set, CORK_IP *elem,
111                           unsigned int cidr_prefix)
112{
113    /* Special case — the BDD for a netmask that's out of range never
114     * evaluates to true. */
115    if (cidr_prefix > IP_BIT_SIZE) {
116        cork_error_set
117            (IPSET_ERROR, IPSET_PARSE_ERROR,
118             "CIDR block %u out of range [0..%u]", cidr_prefix, IP_BIT_SIZE);
119        return false;
120    }
121
122    ipset_node_id  new_bdd =
123        ipset_node_insert
124        (set->cache, set->set_bdd,
125         IPSET_NAME(assignment), elem, cidr_prefix + 1, 0);
126    bool  result = (new_bdd == set->set_bdd);
127    ipset_node_decref(set->cache, set->set_bdd);
128    set->set_bdd = new_bdd;
129    return result;
130}
131