Kea  1.9.9-git
pkt_send_co.cc
Go to the documentation of this file.
1 // Copyright (C) 2013-2020 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>
18 #include <dhcp/option6_iaprefix.h>
20 #include <dhcp/pkt4.h>
21 #include <dhcp/pkt6.h>
22 #include <user_chk.h>
23 
24 using namespace isc::dhcp;
25 using namespace isc::hooks;
26 using namespace user_chk;
27 using namespace std;
28 
29 // prototypes for local helper functions
30 void 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);
34 std::string getV6AddrStr (Pkt6Ptr response);
35 std::string getAddrStrIA_NA(OptionPtr options);
36 std::string getAddrStrIA_PD(OptionPtr options);
37 bool checkIAStatus(boost::shared_ptr<Option6IA>& ia_opt);
38 
39 void add4Options(Pkt4Ptr& response, const UserPtr& user);
40 void add4Option(Pkt4Ptr& response, uint8_t opt_code, std::string& opt_value);
41 void add6Options(Pkt6Ptr& response, const UserPtr& user);
42 void add6Option(OptionPtr& vendor, uint8_t opt_code, std::string& opt_value);
43 const UserPtr& getDefaultUser4();
44 const UserPtr& getDefaultUser6();
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.
49 extern "C" {
50 
69 int pkt4_send(CalloutHandle& handle) {
70  CalloutHandle::CalloutNextStep status = handle.getStatus();
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.
109  generate_output_record(UserId::HW_ADDRESS_STR, hwaddr->toText(),
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.
118  generate_output_record(UserId::HW_ADDRESS_STR, hwaddr->toText(),
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 
149 int pkt6_send(CalloutHandle& handle) {
150  CalloutHandle::CalloutNextStep status = handle.getStatus();
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.
186  generate_output_record(UserId::DUID_STR, duid->toText(),
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.
195  generate_output_record(UserId::DUID_STR, duid->toText(),
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 
221 void 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 
258 void 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 
282 void 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 
321 void 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 
373 void 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 
402 std::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 
428 std::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 
467 std::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 
515 bool 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 
539  return (user_registry->findUser(UserId(UserId::HW_ADDRESS,
541 }
542 
549  return (user_registry->findUser(UserId(UserId::DUID,
551 }
552 
void add6Options(Pkt6Ptr &response, const UserPtr &user)
Adds IPv6 vendor options to the response packet based on given user.
Definition: pkt_send_co.cc:282
boost::shared_ptr< DUID > DuidPtr
Definition: duid.h:20
const char * query_user_id_label
Text label of user id in the inbound query in callout context.
Definition: load_unload.cc:38
boost::shared_ptr< OptionCustom > OptionCustomPtr
A pointer to the OptionCustom object.
std::string getAddrStrIA_PD(OptionPtr options)
Stringify the lease prefix in an D6O_IA_PD option set.
Definition: pkt_send_co.cc:467
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition: hwaddr.h:154
const UserPtr & getDefaultUser6()
Fetches the default IPv6 user from the registry.
Definition: pkt_send_co.cc:548
std::string getAddrStrIA_NA(OptionPtr options)
Stringify the lease address in an D6O_IA_NA option set.
Definition: pkt_send_co.cc:428
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...
Definition: load_unload.cc:53
Encapsulates a unique identifier for a DHCP client.
Definition: user.h:27
boost::shared_ptr< Option > OptionPtr
Definition: option.h:36
const UserPtr & getDefaultUser4()
Fetches the default IPv4 user from the registry.
Definition: pkt_send_co.cc:538
STL namespace.
void add6Option(OptionPtr &vendor, uint8_t opt_code, std::string &opt_value)
Adds/updates a specific IPv6 string vendor option.
Definition: pkt_send_co.cc:321
boost::shared_ptr< User > UserPtr
Defines a smart pointer to a User.
Definition: user.h:241
const char * registered_user_label
Text label of registered user pointer in callout context.
Definition: load_unload.cc:41
int pkt4_send(CalloutHandle &handle)
This callout is called at the "pkt4_send" hook.
Definition: pkt_send_co.cc:69
int pkt6_send(CalloutHandle &handle)
This callout is called at the "pkt6_send" hook.
Definition: pkt_send_co.cc:149
Defines the logger used by the user check hooks library.
Definition: user.cc:19
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition: pkt6.h:28
std::fstream user_chk_output
Output filestream for recording user check outcomes.
Definition: load_unload.cc:27
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.
Definition: pkt_send_co.cc:373
void add4Option(Pkt4Ptr &response, uint8_t opt_code, std::string &opt_value)
Adds/updates are specific IPv4 string option in response packet.
Definition: pkt_send_co.cc:258
Per-packet callout handle.
void add4Options(Pkt4Ptr &response, const UserPtr &user)
Adds IPv4 options to the response packet based on given user.
Definition: pkt_send_co.cc:221
#define DOCSIS3_V6_CONFIG_FILE
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition: pkt4.h:544
CalloutNextStep
Specifies allowed next steps.
T readInteger(const uint32_t index=0) const
Read a buffer as integer value.
std::string getV6AddrStr(Pkt6Ptr response)
Stringify the lease address or prefix IPv6 response packet.
Definition: pkt_send_co.cc:402
A generic exception that is thrown if a function is called in a prohibited way.
#define DOCSIS3_V6_TFTP_SERVERS
void getArgument(const std::string &name, T &value) const
Get argument.
bool checkIAStatus(boost::shared_ptr< Option6IA > &ia_opt)
Tests given IA option set for successful status.
Definition: pkt_send_co.cc:515
UserRegistryPtr user_registry
Pointer to the registry instance.
Definition: load_unload.cc:24
Class that represents IAPREFIX option in DHCPv6.
Option with defined data fields represented as buffers that can be accessed using data field index...
Definition: option_custom.h:32
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...
Definition: load_unload.cc:47
Class which represents an option carrying a single string value.
Definition: option_string.h:28
void getContext(const std::string &name, T &value) const
Get context.
CalloutNextStep getStatus() const
Returns the next processing step.