summaryrefslogtreecommitdiffstats
path: root/src/hooks/dhcp/flex_option/flex_option.h
blob: f92e7ae7ebe7af53ccd3103cbe5684fb64ba407e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
// Copyright (C) 2019-2020 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef FLEX_OPTION_H
#define FLEX_OPTION_H

#include <cc/data.h>
#include <dhcp/libdhcp++.h>
#include <dhcp/option.h>
#include <dhcp/option_definition.h>
#include <dhcp/std_option_defs.h>
#include <eval/evaluate.h>
#include <eval/token.h>
#include <util/strutil.h>

#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>

#include <map>
#include <string>

namespace isc {
namespace flex_option {

/// @brief Flex Option implementation.
///
/// The implementation can be divided into two parts:
///  - the configuration parsed and stored by load()
///  - the response packet processing performed by the process method
///
class FlexOptionImpl {
public:

    /// @brief Action.
    ///
    /// Currently supported actions are:
    ///  - add (if not already existing)
    ///  - supersede (as add but also when already existing)
    ///  - remove
    enum Action {
        NONE,
        ADD,
        SUPERSEDE,
        REMOVE
    };

    /// @brief Option configuration.
    ///
    /// Per option configuration.
    class OptionConfig {
    public:
        /// @brief Constructor.
        ///
        /// @param code the option code.
        /// @param def the option definition.
        OptionConfig(uint16_t code, isc::dhcp::OptionDefinitionPtr def);

        /// @brief Destructor.
        virtual ~OptionConfig();

        /// @brief Return option code.
        ///
        /// @return option code.
        uint16_t getCode() const {
            return (code_);
        }

        /// @brief Return option definition.
        ///
        /// @return option definition.
        isc::dhcp::OptionDefinitionPtr getOptionDef() const {
            return (def_);
        }

        /// @brief Set action.
        ///
        /// @param action the action.
        void setAction(Action action) {
            action_ = action;
        }

        /// @brief Return action.
        ///
        /// @return action.
        Action getAction() const {
            return (action_);
        }

        /// @brief Set textual expression.
        ///
        /// @param text the textual expression.
        void setText(const std::string& text) {
            text_ = text;
        };

        /// @brief Get textual expression.
        ///
        /// @return textual expression.
        const std::string& getText() const {
            return (text_);
        }

        /// @brief Set match expression.
        ///
        /// @param expr the match expression.
        void setExpr(const isc::dhcp::ExpressionPtr& expr) {
            expr_ = expr;
        }

        /// @brief Get match expression.
        ///
        /// @return the match expression.
        const isc::dhcp::ExpressionPtr& getExpr() const {
            return (expr_);
        }

    private:
        /// @brief The code.
        uint16_t code_;

        /// @brief The option definition.
        /// @note This value is set only when csv-format is true.
        isc::dhcp::OptionDefinitionPtr def_;

        /// @brief The action.
        Action action_;

        /// @brief The textual expression.
        std::string text_;

        /// @brief The match expression.
        isc::dhcp::ExpressionPtr expr_;
    };

    /// @brief The type of shared pointers to option config.
    typedef boost::shared_ptr<OptionConfig> OptionConfigPtr;

    /// @brief The type of the option config map.
    typedef std::map<uint16_t, OptionConfigPtr> OptionConfigMap;

    /// @brief Constructor.
    FlexOptionImpl();

    /// @brief Destructor.
    ~FlexOptionImpl();

    /// @brief Get the option config map.
    ///
    /// @return The option config map.
    const OptionConfigMap& getOptionConfigMap() const {
        return (option_config_map_);
    }

    /// @brief Configure the Flex Option implementation.
    ///
    /// @param options The element with option config list.
    /// @throw BadValue and similar exceptions on error.
    void configure(isc::data::ConstElementPtr options);

    /// @brief Process a query / response pair.
    ///
    /// @tparam PktType The type of pointers to packets: Pkt4Ptr or Pkt6Ptr.
    /// @param universe The option universe: Option::V4 or Option::V6.
    /// @param query The query packet.
    /// @param response The response packet.
    template <typename PktType>
    void process(isc::dhcp::Option::Universe universe,
                 PktType query, PktType response) {
        for (auto pair : getOptionConfigMap()) {
            const OptionConfigPtr& opt_cfg = pair.second;
            std::string value;
            isc::dhcp::OptionBuffer buffer;
            isc::dhcp::OptionPtr opt = response->getOption(opt_cfg->getCode());
            isc::dhcp::OptionDefinitionPtr def = opt_cfg->getOptionDef();
            switch (opt_cfg->getAction()) {
            case NONE:
                break;
            case ADD:
                // Don't add if option is already there.
                if (opt) {
                    break;
                }
                value = isc::dhcp::evaluateString(*opt_cfg->getExpr(), *query);
                // Do nothing is the expression evaluates to empty.
                if (value.empty()) {
                    break;
                }
                // Set the value.
                if (def) {
                    std::vector<std::string> split_vec =
                            isc::util::str::tokens(value, ",", true);
                    opt = def->optionFactory(universe, opt_cfg->getCode(),
                                             split_vec);
                } else {
                    buffer.assign(value.begin(), value.end());
                    opt.reset(new isc::dhcp::Option(universe,
                                                    opt_cfg->getCode(),
                                                    buffer));
                }
                // Add the option.
                response->addOption(opt);
                logAction(ADD, opt_cfg->getCode(), value);
                break;
            case SUPERSEDE:
                // Do nothing is the expression evaluates to empty.
                value = isc::dhcp::evaluateString(*opt_cfg->getExpr(), *query);
                if (value.empty()) {
                    break;
                }
                // Remove the option if already there.
                while (opt) {
                    response->delOption(opt_cfg->getCode());
                    opt = response->getOption(opt_cfg->getCode());
                }
                // Set the value.
                if (def) {
                    std::vector<std::string> split_vec =
                            isc::util::str::tokens(value, ",", true);
                    opt = def->optionFactory(universe, opt_cfg->getCode(),
                                             split_vec);
                } else {
                    buffer.assign(value.begin(), value.end());
                    opt.reset(new isc::dhcp::Option(universe,
                                                    opt_cfg->getCode(),
                                                    buffer));
                }
                // Add the option.
                response->addOption(opt);
                logAction(SUPERSEDE, opt_cfg->getCode(), value);
                break;
            case REMOVE:
                // Nothing to remove if option is not present.
                if (!opt) {
                    break;
                }
                // Do nothing is the expression evaluates to false.
                if (!isc::dhcp::evaluateBool(*opt_cfg->getExpr(), *query)) {
                    break;
                }
                // Remove the option.
                while (opt) {
                    response->delOption(opt_cfg->getCode());
                    opt = response->getOption(opt_cfg->getCode());
                }
                logAction(REMOVE, opt_cfg->getCode(), "");
                break;
            }
        }
    }

    /// @brief Log the action.
    ///
    /// @param action The action.
    /// @param code The option code.
    /// @param value The option value ("" for remove).
    void logAction(Action action, uint16_t code, const std::string& value) const;

protected:
    /// @brief Get a mutable reference to the option config map
    ///
    /// @return The option config map.
    OptionConfigMap& getMutableOptionConfigMap() {
        return (option_config_map_);
    }

private:
    /// @brief The option config map (code and pointer to option config).
    OptionConfigMap option_config_map_;

    /// @brief Parse an option config.
    ///
    /// @param option The element with option config.
    /// @throw BadValue and similar exceptionson error.
    void parseOptionConfig(isc::data::ConstElementPtr option);

};

/// @brief The type of shared pointers to Flex Option implementations.
typedef boost::shared_ptr<FlexOptionImpl> FlexOptionImplPtr;

} // end of namespace flex_option
} // end of namespace isc
#endif