Kea 2.7.0
pkt_send_co.cc
Go to the documentation of this file.
1// Copyright (C) 2013-2022 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
8
9#include <config.h>
10#include <asiolink/io_address.h>
11#include <hooks/hooks.h>
12#include <dhcp/dhcp4.h>
13#include <dhcp/dhcp6.h>
14#include <dhcp/option_string.h>
15#include <dhcp/option_custom.h>
16#include <dhcp/option6_ia.h>
17#include <dhcp/option6_iaaddr.h>
20#include <dhcp/pkt4.h>
21#include <dhcp/pkt6.h>
22#include <user_chk.h>
23
24using namespace isc::dhcp;
25using namespace isc::hooks;
26using namespace user_chk;
27using namespace std;
28
29// prototypes for local helper functions
30void generate_output_record(const std::string& id_type_str,
31 const std::string& id_val_str,
32 const std::string& addr_str,
33 const bool& registered);
34std::string getV6AddrStr (Pkt6Ptr response);
35std::string getAddrStrIA_NA(OptionPtr options);
36std::string getAddrStrIA_PD(OptionPtr options);
37bool checkIAStatus(boost::shared_ptr<Option6IA>& ia_opt);
38
39void add4Options(Pkt4Ptr& response, const UserPtr& user);
40void add4Option(Pkt4Ptr& response, uint8_t opt_code, std::string& opt_value);
41void add6Options(Pkt6Ptr& response, const UserPtr& user);
42void add6Option(OptionPtr& vendor, uint8_t opt_code, std::string& opt_value);
45
46// Functions accessed by the hooks framework use C linkage to avoid the name
47// mangling that accompanies use of the C++ compiler as well as to avoid
48// issues related to namespaces.
49extern "C" {
50
71 if (status == CalloutHandle::NEXT_STEP_DROP) {
72 return (0);
73 }
74
75 if (status == CalloutHandle::NEXT_STEP_SKIP) {
76 isc_throw(isc::InvalidOperation, "packet pack already handled");
77 }
78
79 try {
80 Pkt4Ptr response;
81 handle.getArgument("response4", response);
82
83 uint8_t packet_type = response->getType();
84 if (packet_type == DHCPNAK) {
85 std::cout << "DHCP UserCheckHook : pkt4_send"
86 << "skipping packet type: "
87 << static_cast<int>(packet_type) << std::endl;
88 return (0);
89 }
90
91 // Get the user id saved from the query packet.
92 HWAddrPtr hwaddr;
93 handle.getContext(query_user_id_label, hwaddr);
94
95 // Get registered_user pointer.
96 UserPtr registered_user;
97 handle.getContext(registered_user_label, registered_user);
98
99 // Fetch the lease address.
100 isc::asiolink::IOAddress addr = response->getYiaddr();
101
102 if (registered_user) {
103 // add options based on user
104 // then generate registered output record
105 std::cout << "DHCP UserCheckHook : pkt4_send registered_user is: "
106 << registered_user->getUserId() << std::endl;
107
108 // Add the outcome entry to the output file.
110 addr.toText(), true);
111 add4Options(response, registered_user);
112 } else {
113 // add default options based
114 // then generate not registered output record
115 std::cout << "DHCP UserCheckHook : pkt4_send no registered_user"
116 << std::endl;
117 // Add the outcome entry to the output file.
119 addr.toText(), false);
120
121 add4Options(response, getDefaultUser4());
122 }
123 } catch (const std::exception& ex) {
124 std::cout << "DHCP UserCheckHook : pkt4_send unexpected error: "
125 << ex.what() << std::endl;
126 return (1);
127 }
128
129 return (0);
130}
131
151 if (status == CalloutHandle::NEXT_STEP_DROP) {
152 return (0);
153 }
154
155 if (status == CalloutHandle::NEXT_STEP_SKIP) {
156 isc_throw(isc::InvalidOperation, "packet pack already handled");
157 }
158
159 try {
160 Pkt6Ptr response;
161 handle.getArgument("response6", response);
162
163 // Fetch the lease address as a string
164 std::string addr_str = getV6AddrStr(response);
165 if (addr_str.empty()) {
166 // packet did not contain an address, must be failed.
167 std::cout << "pkt6_send: Skipping packet address is blank"
168 << std::endl;
169 return (0);
170 }
171
172 // Get the user id saved from the query packet.
173 DuidPtr duid;
174 handle.getContext(query_user_id_label, duid);
175
176 // Get registered_user pointer.
177 UserPtr registered_user;
178 handle.getContext(registered_user_label, registered_user);
179
180 if (registered_user) {
181 // add options based on user
182 // then generate registered output record
183 std::cout << "DHCP UserCheckHook : pkt6_send registered_user is: "
184 << registered_user->getUserId() << std::endl;
185 // Add the outcome entry to the output file.
187 addr_str, true);
188 add6Options(response, registered_user);
189 } else {
190 // add default options based
191 // then generate not registered output record
192 std::cout << "DHCP UserCheckHook : pkt6_send no registered_user"
193 << std::endl;
194 // Add the outcome entry to the output file.
196 addr_str, false);
197 add6Options(response, getDefaultUser6());
198 }
199 } catch (const std::exception& ex) {
200 std::cout << "DHCP UserCheckHook : pkt6_send unexpected error: "
201 << ex.what() << std::endl;
202 return (1);
203 }
204
205 return (0);
206}
207
208} // extern C
209
221void add4Options(Pkt4Ptr& response, const UserPtr& user) {
222 // If user is null, do nothing.
223 if (!user) {
224 return;
225 }
226
227 // If the user has bootfile property, update it in the response.
228 std::string opt_value = user->getProperty("bootfile");
229 if (!opt_value.empty()) {
230 std::cout << "DHCP UserCheckHook : add4Options "
231 << "adding boot file:" << opt_value << std::endl;
232
233 // Add boot file to packet.
234 add4Option(response, DHO_BOOT_FILE_NAME, opt_value);
235
236 // Boot file also goes in file field.
237 response->setFile((const uint8_t*)(opt_value.c_str()),
238 opt_value.length());
239 }
240
241 // If the user has tftp server property, update it in the response.
242 opt_value = user->getProperty("tftp_server");
243 if (!opt_value.empty()) {
244 std::cout << "DHCP UserCheckHook : add4Options "
245 << "adding TFTP server:" << opt_value << std::endl;
246
247 // Add tftp server option to packet.
248 add4Option(response, DHO_TFTP_SERVER_NAME, opt_value);
249 }
250 // add next option here
251}
252
258void add4Option(Pkt4Ptr& response, uint8_t opt_code, std::string& opt_value) {
259 // Remove the option if it exists.
260 OptionPtr opt = response->getOption(opt_code);
261 if (opt) {
262 response->delOption(opt_code);
263 }
264
265 // Now add the option.
266 opt.reset(new OptionString(Option::V4, opt_code, opt_value));
267 response->addOption(opt);
268}
269
270
282void add6Options(Pkt6Ptr& response, const UserPtr& user) {
283 if (!user) {
284 return;
285 }
286
289 OptionPtr vendor = response->getOption(D6O_VENDOR_OPTS);
290 if (!vendor) {
291 std::cout << "DHCP UserCheckHook : add6Options "
292 << "response has no vendor option to update" << std::endl;
293 return;
294 }
295
296 // If the user defines bootfile, set the option in response.
297 std::string opt_value = user->getProperty("bootfile");
298 if (!opt_value.empty()) {
299 std::cout << "DHCP UserCheckHook : add6Options "
300 << "adding boot file:" << opt_value << std::endl;
301 add6Option(vendor, DOCSIS3_V6_CONFIG_FILE, opt_value);
302 }
303
304 // If the user defines tftp server, set the option in response.
305 opt_value = user->getProperty("tftp_server");
306 if (!opt_value.empty()) {
307 std::cout << "DHCP UserCheckHook : add6Options "
308 << "adding tftp server:" << opt_value << std::endl;
309
310 add6Option(vendor, DOCSIS3_V6_TFTP_SERVERS, opt_value);
311 }
312
313 // add next option here
314}
315
321void add6Option(OptionPtr& vendor, uint8_t opt_code, std::string& opt_value) {
322 vendor->delOption(opt_code);
323 OptionPtr option(new OptionString(Option::V6, opt_code, opt_value));
324 vendor->addOption(option);
325}
326
327
373void generate_output_record(const std::string& id_type_str,
374 const std::string& id_val_str,
375 const std::string& addr_str,
376 const bool& registered)
377{
378 user_chk_output << "id_type=" << id_type_str << std::endl
379 << "client=" << id_val_str << std::endl
380 << "addr=" << addr_str << std::endl
381 << "registered=" << (registered ? "yes" : "no")
382 << std::endl
383 << std::endl; // extra line in between
384
385 // @todo Flush is here to ensure output is immediate for demo purposes.
386 // Performance would generally dictate not using it.
387 flush(user_chk_output);
388}
389
402std::string getV6AddrStr(Pkt6Ptr response) {
403 OptionPtr tmp = response->getOption(D6O_IA_NA);
404 if (tmp) {
405 return(getAddrStrIA_NA(tmp));
406 }
407
408 // IA_NA not there so try IA_PD
409 tmp = response->getOption(D6O_IA_PD);
410 if (!tmp) {
411 isc_throw (isc::BadValue, "Response has neither IA_NA nor IA_PD");
412 }
413
414 return(getAddrStrIA_PD(tmp));
415}
416
428std::string getAddrStrIA_NA(OptionPtr options) {
429 boost::shared_ptr<Option6IA> ia =
430 boost::dynamic_pointer_cast<Option6IA>(options);
431
432 if (!ia) {
433 isc_throw (isc::BadValue, "D6O_IA_NA option invalid");
434 }
435
436 // If status indicates a failure return a blank string.
437 if (!checkIAStatus(ia)) {
438 return (std::string(""));
439 }
440
441 options = ia->getOption(D6O_IAADDR);
442 if (!options) {
443 isc_throw (isc::BadValue, "D6O_IAADDR option missing");
444 }
445
446 boost::shared_ptr<Option6IAAddr> addr_option;
447 addr_option = boost::dynamic_pointer_cast<Option6IAAddr>(options);
448 if (!addr_option) {
449 isc_throw (isc::BadValue, "D6O_IAADDR Option6IAAddr missing");
450 }
451
452 isc::asiolink::IOAddress addr = addr_option->getAddress();
453 return (addr.toText());
454}
455
467std::string getAddrStrIA_PD(OptionPtr options) {
468 boost::shared_ptr<Option6IA> ia =
469 boost::dynamic_pointer_cast<Option6IA>(options);
470
471 // Make sure we have an IA_PD option.
472 if (!ia) {
473 isc_throw (isc::BadValue, "D6O_IA_PD option invalid");
474 }
475
476 // Check the response for success status. If it isn't a success response
477 // there will not be a lease prefix value which is denoted by returning
478 // an empty string.
479 if (!checkIAStatus(ia)) {
480 return (std::string(""));
481 }
482
483 // Get the prefix option the IA_PD option.
484 options = ia->getOption(D6O_IAPREFIX);
485 if (!options) {
486 isc_throw(isc::BadValue, "D6O_IAPREFIX option is missing");
487 }
488
489 boost::shared_ptr<Option6IAPrefix> addr_option;
490 addr_option = boost::dynamic_pointer_cast<Option6IAPrefix>(options);
491 if (!addr_option) {
492 isc_throw (isc::BadValue, "D6O_IA_PD addr option bad");
493 }
494
495 // Get the address and prefix length values.
496 isc::asiolink::IOAddress addr = addr_option->getAddress();
497 uint8_t prefix_len = addr_option->getLength();
498
499 // Build the output string and return it.
500 stringstream buf;
501 buf << addr.toText() << "/" << static_cast<int>(prefix_len);
502 return (buf.str());
503}
504
515bool checkIAStatus(boost::shared_ptr<Option6IA>& ia) {
516 OptionCustomPtr status =
517 boost::dynamic_pointer_cast
518 <OptionCustom>(ia->getOption(D6O_STATUS_CODE));
519
520 // If a non-zero status is present, bail.
521 if (status) {
522 int status_val = status->readInteger<uint16_t>(0);
523 if (status_val != 0) {
524 std::cout << "SKIPPING PACKET STATUS is not success:"
525 << status_val << std::endl;
526 return (false);
527 }
528 }
529
530 return (true);
531}
532
542
549 return (user_registry->findUser(UserId(UserId::DUID,
551}
552
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
A generic exception that is thrown if a function is called in a prohibited way.
Option with defined data fields represented as buffers that can be accessed using data field index.
T readInteger(const uint32_t index=0) const
Read a buffer as integer value.
Class which represents an option carrying a single string value.
Per-packet callout handle.
void getContext(const std::string &name, T &value) const
Get context.
CalloutNextStep
Specifies allowed next steps.
@ NEXT_STEP_DROP
drop the packet
@ NEXT_STEP_SKIP
skip the next processing step
CalloutNextStep getStatus() const
Returns the next processing step.
void getArgument(const std::string &name, T &value) const
Get argument.
Encapsulates a unique identifier for a DHCP client.
Definition user.h:27
static const char * DUID_STR
Define the text label DUID id type.
Definition user.h:42
static const char * HW_ADDRESS_STR
Defines the text label hardware address id type.
Definition user.h:40
@ HW_ADDRESS
Hardware addresses (MAC) are used for IPv4 clients.
Definition user.h:34
@ DUID
DUIDs are used for IPv6 clients.
Definition user.h:36
@ D6O_VENDOR_OPTS
Definition dhcp6.h:37
@ D6O_IA_NA
Definition dhcp6.h:23
@ D6O_IA_PD
Definition dhcp6.h:45
@ D6O_IAADDR
Definition dhcp6.h:25
@ D6O_STATUS_CODE
Definition dhcp6.h:33
@ D6O_IAPREFIX
Definition dhcp6.h:46
#define DOCSIS3_V6_TFTP_SERVERS
#define DOCSIS3_V6_CONFIG_FILE
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
const char * query_user_id_label
Text label of user id in the inbound query in callout context.
std::fstream user_chk_output
Output filestream for recording user check outcomes.
UserRegistryPtr user_registry
Pointer to the registry instance.
const char * default_user4_id_str
Text id used to identify the default IPv4 user in the registry The format is a string containing an e...
const char * registered_user_label
Text label of registered user pointer in callout context.
const char * default_user6_id_str
Text id used to identify the default IPv6 user in the registry The format is a string containing an e...
@ DHO_TFTP_SERVER_NAME
Definition dhcp4.h:135
@ DHO_BOOT_FILE_NAME
Definition dhcp4.h:136
boost::shared_ptr< OptionCustom > OptionCustomPtr
A pointer to the OptionCustom object.
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition pkt4.h:555
boost::shared_ptr< DUID > DuidPtr
Definition duid.h:136
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition hwaddr.h:154
@ DHCPNAK
Definition dhcp4.h:240
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition pkt6.h:31
boost::shared_ptr< Option > OptionPtr
Definition option.h:37
Defines the logger used by the user check hooks library.
Definition user.cc:19
boost::shared_ptr< User > UserPtr
Defines a smart pointer to a User.
Definition user.h:241
void add6Options(Pkt6Ptr &response, const UserPtr &user)
Adds IPv6 vendor options to the response packet based on given user.
bool checkIAStatus(boost::shared_ptr< Option6IA > &ia_opt)
Tests given IA option set for successful status.
void generate_output_record(const std::string &id_type_str, const std::string &id_val_str, const std::string &addr_str, const bool &registered)
Adds an entry to the end of the user check outcome file.
std::string getAddrStrIA_NA(OptionPtr options)
Stringify the lease address in an D6O_IA_NA option set.
std::string getAddrStrIA_PD(OptionPtr options)
Stringify the lease prefix in an D6O_IA_PD option set.
void add6Option(OptionPtr &vendor, uint8_t opt_code, std::string &opt_value)
Adds/updates a specific IPv6 string vendor option.
const UserPtr & getDefaultUser4()
Fetches the default IPv4 user from the registry.
const UserPtr & getDefaultUser6()
Fetches the default IPv6 user from the registry.
int pkt6_send(CalloutHandle &handle)
This callout is called at the "pkt6_send" hook.
int pkt4_send(CalloutHandle &handle)
This callout is called at the "pkt4_send" hook.
std::string getV6AddrStr(Pkt6Ptr response)
Stringify the lease address or prefix IPv6 response packet.
void add4Option(Pkt4Ptr &response, uint8_t opt_code, std::string &opt_value)
Adds/updates are specific IPv4 string option in response packet.
void add4Options(Pkt4Ptr &response, const UserPtr &user)
Adds IPv4 options to the response packet based on given user.