Kea  1.5.0
pkt_send_co.cc
Go to the documentation of this file.
1 // Copyright (C) 2013-2015,2017 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  try {
71  Pkt4Ptr response;
72  handle.getArgument("response4", response);
73 
74  uint8_t packet_type = response->getType();
75  if (packet_type == DHCPNAK) {
76  std::cout << "DHCP UserCheckHook : pkt4_send"
77  << "skipping packet type: "
78  << static_cast<int>(packet_type) << std::endl;
79  return (0);
80  }
81 
82  // Get the user id saved from the query packet.
83  HWAddrPtr hwaddr;
84  handle.getContext(query_user_id_label, hwaddr);
85 
86  // Get registered_user pointer.
87  UserPtr registered_user;
88  handle.getContext(registered_user_label, registered_user);
89 
90  // Fetch the lease address.
91  isc::asiolink::IOAddress addr = response->getYiaddr();
92 
93  if (registered_user) {
94  // add options based on user
95  // then generate registered output record
96  std::cout << "DHCP UserCheckHook : pkt4_send registered_user is: "
97  << registered_user->getUserId() << std::endl;
98 
99  // Add the outcome entry to the output file.
100  generate_output_record(UserId::HW_ADDRESS_STR, hwaddr->toText(),
101  addr.toText(), true);
102  add4Options(response, registered_user);
103  } else {
104  // add default options based
105  // then generate not registered output record
106  std::cout << "DHCP UserCheckHook : pkt4_send no registered_user"
107  << std::endl;
108  // Add the outcome entry to the output file.
109  generate_output_record(UserId::HW_ADDRESS_STR, hwaddr->toText(),
110  addr.toText(), false);
111 
112  add4Options(response, getDefaultUser4());
113  }
114  } catch (const std::exception& ex) {
115  std::cout << "DHCP UserCheckHook : pkt4_send unexpected error: "
116  << ex.what() << std::endl;
117  return (1);
118  }
119 
120  return (0);
121 }
122 
140 int pkt6_send(CalloutHandle& handle) {
141  try {
142  Pkt6Ptr response;
143  handle.getArgument("response6", response);
144 
145  // Fetch the lease address as a string
146  std::string addr_str = getV6AddrStr(response);
147  if (addr_str.empty()) {
148  // packet did not contain an address, must be failed.
149  std::cout << "pkt6_send: Skipping packet address is blank"
150  << std::endl;
151  return (0);
152  }
153 
154  // Get the user id saved from the query packet.
155  DuidPtr duid;
156  handle.getContext(query_user_id_label, duid);
157 
158  // Get registered_user pointer.
159  UserPtr registered_user;
160  handle.getContext(registered_user_label, registered_user);
161 
162  if (registered_user) {
163  // add options based on user
164  // then generate registered output record
165  std::cout << "DHCP UserCheckHook : pkt6_send registered_user is: "
166  << registered_user->getUserId() << std::endl;
167  // Add the outcome entry to the output file.
168  generate_output_record(UserId::DUID_STR, duid->toText(),
169  addr_str, true);
170  add6Options(response, registered_user);
171  } else {
172  // add default options based
173  // then generate not registered output record
174  std::cout << "DHCP UserCheckHook : pkt6_send no registered_user"
175  << std::endl;
176  // Add the outcome entry to the output file.
177  generate_output_record(UserId::DUID_STR, duid->toText(),
178  addr_str, false);
179  add6Options(response, getDefaultUser6());
180  }
181  } catch (const std::exception& ex) {
182  std::cout << "DHCP UserCheckHook : pkt6_send unexpected error: "
183  << ex.what() << std::endl;
184  return (1);
185  }
186 
187  return (0);
188 }
189 
190 } // extern C
191 
203 void add4Options(Pkt4Ptr& response, const UserPtr& user) {
204  // If user is null, do nothing.
205  if (!user) {
206  return;
207  }
208 
209  // If the user has bootfile property, update it in the response.
210  std::string opt_value = user->getProperty("bootfile");
211  if (!opt_value.empty()) {
212  std::cout << "DHCP UserCheckHook : add4Options "
213  << "adding boot file:" << opt_value << std::endl;
214 
215  // Add boot file to packet.
216  add4Option(response, DHO_BOOT_FILE_NAME, opt_value);
217 
218  // Boot file also goes in file field.
219  response->setFile((const uint8_t*)(opt_value.c_str()),
220  opt_value.length());
221  }
222 
223  // If the user has tftp server property, update it in the response.
224  opt_value = user->getProperty("tftp_server");
225  if (!opt_value.empty()) {
226  std::cout << "DHCP UserCheckHook : add4Options "
227  << "adding TFTP server:" << opt_value << std::endl;
228 
229  // Add tftp server option to packet.
230  add4Option(response, DHO_TFTP_SERVER_NAME, opt_value);
231  }
232  // add next option here
233 }
234 
240 void add4Option(Pkt4Ptr& response, uint8_t opt_code, std::string& opt_value) {
241  // Remove the option if it exists.
242  OptionPtr opt = response->getOption(opt_code);
243  if (opt) {
244  response->delOption(opt_code);
245  }
246 
247  // Now add the option.
248  opt.reset(new OptionString(Option::V4, opt_code, opt_value));
249  response->addOption(opt);
250 }
251 
252 
264 void add6Options(Pkt6Ptr& response, const UserPtr& user) {
265  if (!user) {
266  return;
267  }
268 
271  OptionPtr vendor = response->getOption(D6O_VENDOR_OPTS);
272  if (!vendor) {
273  std::cout << "DHCP UserCheckHook : add6Options "
274  << "response has no vendor option to update" << std::endl;
275  return;
276  }
277 
278  // If the user defines bootfile, set the option in response.
279  std::string opt_value = user->getProperty("bootfile");
280  if (!opt_value.empty()) {
281  std::cout << "DHCP UserCheckHook : add6Options "
282  << "adding boot file:" << opt_value << std::endl;
283  add6Option(vendor, DOCSIS3_V6_CONFIG_FILE, opt_value);
284  }
285 
286  // If the user defines tftp server, set the option in response.
287  opt_value = user->getProperty("tftp_server");
288  if (!opt_value.empty()) {
289  std::cout << "DHCP UserCheckHook : add6Options "
290  << "adding tftp server:" << opt_value << std::endl;
291 
292  add6Option(vendor, DOCSIS3_V6_TFTP_SERVERS, opt_value);
293  }
294 
295  // add next option here
296 }
297 
303 void add6Option(OptionPtr& vendor, uint8_t opt_code, std::string& opt_value) {
304  vendor->delOption(opt_code);
305  OptionPtr option(new OptionString(Option::V6, opt_code, opt_value));
306  vendor->addOption(option);
307 }
308 
309 
355 void generate_output_record(const std::string& id_type_str,
356  const std::string& id_val_str,
357  const std::string& addr_str,
358  const bool& registered)
359 {
360  user_chk_output << "id_type=" << id_type_str << std::endl
361  << "client=" << id_val_str << std::endl
362  << "addr=" << addr_str << std::endl
363  << "registered=" << (registered ? "yes" : "no")
364  << std::endl
365  << std::endl; // extra line in between
366 
367  // @todo Flush is here to ensure output is immediate for demo purposes.
368  // Performance would generally dictate not using it.
369  flush(user_chk_output);
370 }
371 
384 std::string getV6AddrStr(Pkt6Ptr response) {
385  OptionPtr tmp = response->getOption(D6O_IA_NA);
386  if (tmp) {
387  return(getAddrStrIA_NA(tmp));
388  }
389 
390  // IA_NA not there so try IA_PD
391  tmp = response->getOption(D6O_IA_PD);
392  if (!tmp) {
393  isc_throw (isc::BadValue, "Response has neither IA_NA nor IA_PD");
394  }
395 
396  return(getAddrStrIA_PD(tmp));
397 }
398 
410 std::string getAddrStrIA_NA(OptionPtr options) {
411  boost::shared_ptr<Option6IA> ia =
412  boost::dynamic_pointer_cast<Option6IA>(options);
413 
414  if (!ia) {
415  isc_throw (isc::BadValue, "D6O_IA_NA option invalid");
416  }
417 
418  // If status indicates a failure return a blank string.
419  if (!checkIAStatus(ia)) {
420  return (std::string(""));
421  }
422 
423  options = ia->getOption(D6O_IAADDR);
424  if (!options) {
425  isc_throw (isc::BadValue, "D6O_IAADDR option missing");
426  }
427 
428  boost::shared_ptr<Option6IAAddr> addr_option;
429  addr_option = boost::dynamic_pointer_cast<Option6IAAddr>(options);
430  if (!addr_option) {
431  isc_throw (isc::BadValue, "D6O_IAADDR Option6IAAddr missing");
432  }
433 
434  isc::asiolink::IOAddress addr = addr_option->getAddress();
435  return (addr.toText());
436 }
437 
449 std::string getAddrStrIA_PD(OptionPtr options) {
450  boost::shared_ptr<Option6IA> ia =
451  boost::dynamic_pointer_cast<Option6IA>(options);
452 
453  // Make sure we have an IA_PD option.
454  if (!ia) {
455  isc_throw (isc::BadValue, "D6O_IA_PD option invalid");
456  }
457 
458  // Check the response for success status. If it isn't a success response
459  // there will not be a lease prefix value which is denoted by returning
460  // an empty string.
461  if (!checkIAStatus(ia)) {
462  return (std::string(""));
463  }
464 
465  // Get the prefix option the IA_PD option.
466  options = ia->getOption(D6O_IAPREFIX);
467  if (!options) {
468  isc_throw(isc::BadValue, "D60_IAPREFIX option is missing");
469  }
470 
471  boost::shared_ptr<Option6IAPrefix> addr_option;
472  addr_option = boost::dynamic_pointer_cast<Option6IAPrefix>(options);
473  if (!addr_option) {
474  isc_throw (isc::BadValue, "D6O_IA_PD addr option bad");
475  }
476 
477  // Get the address and prefix length values.
478  isc::asiolink::IOAddress addr = addr_option->getAddress();
479  uint8_t prefix_len = addr_option->getLength();
480 
481  // Build the output string and return it.
482  stringstream buf;
483  buf << addr.toText() << "/" << static_cast<int>(prefix_len);
484  return (buf.str());
485 }
486 
497 bool checkIAStatus(boost::shared_ptr<Option6IA>& ia) {
498  OptionCustomPtr status =
499  boost::dynamic_pointer_cast
500  <OptionCustom>(ia->getOption(D6O_STATUS_CODE));
501 
502  // If a non-zero status is present, bail.
503  if (status) {
504  int status_val = status->readInteger<uint16_t>(0);
505  if (status_val != 0) {
506  std::cout << "SKIPPING PACKET STATUS is not success:"
507  << status_val << std::endl;
508  return (false);
509  }
510  }
511 
512  return (true);
513 }
514 
521  return (user_registry->findUser(UserId(UserId::HW_ADDRESS,
523 }
524 
531  return (user_registry->findUser(UserId(UserId::DUID,
533 }
534 
user_registry
UserRegistryPtr user_registry
Pointer to the registry instance.
Definition: load_unload.cc:24
default_user6_id_str
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
docsis3_option_defs.h
isc::hooks::CalloutHandle::getContext
void getContext(const std::string &name, T &value) const
Get context.
Definition: callout_handle.h:292
pkt4.h
user_chk::UserId
Encapsulates a unique identifier for a DHCP client.
Definition: user.h:27
getDefaultUser4
const UserPtr & getDefaultUser4()
Fetches the default IPv4 user from the registry.
Definition: pkt_send_co.cc:520
hooks.h
isc::hooks::CalloutHandle
Per-packet callout handle.
Definition: callout_handle.h:84
isc::dhcp::OptionCustom
Option with defined data fields represented as buffers that can be accessed using data field index.
Definition: option_custom.h:31
isc::dhcp::OptionString
Class which represents an option carrying a single string value.
Definition: option_string.h:27
io_address.h
add6Option
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:303
pkt6_send
int pkt6_send(CalloutHandle &handle)
This callout is called at the "pkt6_send" hook.
Definition: pkt_send_co.cc:140
option6_ia.h
D6O_IA_PD
@ D6O_IA_PD
Definition: dhcp6.h:45
isc::dhcp::DuidPtr
boost::shared_ptr< DUID > DuidPtr
Definition: duid.h:20
add6Options
void add6Options(Pkt6Ptr &response, const UserPtr &user)
Adds IPv6 vendor options to the response packet based on given user.
Definition: pkt_send_co.cc:264
isc::dhcp::DHO_BOOT_FILE_NAME
@ DHO_BOOT_FILE_NAME
Definition: dhcp4.h:136
pkt6.h
add4Options
void add4Options(Pkt4Ptr &response, const UserPtr &user)
Adds IPv4 options to the response packet based on given user.
Definition: pkt_send_co.cc:203
query_user_id_label
const char * query_user_id_label
Text label of user id in the inbound query in callout context.
Definition: load_unload.cc:38
user_chk_output
std::fstream user_chk_output
Output filestream for recording user check outcomes.
Definition: load_unload.cc:27
option_string.h
registered_user_label
const char * registered_user_label
Text label of registered user pointer in callout context.
Definition: load_unload.cc:41
D6O_VENDOR_OPTS
@ D6O_VENDOR_OPTS
Definition: dhcp6.h:37
option6_iaprefix.h
getAddrStrIA_PD
std::string getAddrStrIA_PD(OptionPtr options)
Stringify the lease prefix in an D6O_IA_PD option set.
Definition: pkt_send_co.cc:449
DOCSIS3_V6_TFTP_SERVERS
#define DOCSIS3_V6_TFTP_SERVERS
Definition: docsis3_option_defs.h:34
dhcp4.h
isc::dhcp::HWAddrPtr
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition: hwaddr.h:154
isc::dhcp::Pkt4Ptr
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition: pkt4.h:546
isc::hooks::CalloutHandle::getArgument
void getArgument(const std::string &name, T &value) const
Get argument.
Definition: callout_handle.h:170
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition: exceptions/exceptions.h:192
isc::BadValue
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
Definition: exceptions/exceptions.h:132
isc::dhcp::DHO_TFTP_SERVER_NAME
@ DHO_TFTP_SERVER_NAME
Definition: dhcp4.h:135
isc::dhcp::DHCPNAK
@ DHCPNAK
Definition: dhcp4.h:233
isc::dhcp
Definition: ctrl_dhcp4_srv.cc:75
pkt4_send
int pkt4_send(CalloutHandle &handle)
This callout is called at the "pkt4_send" hook.
Definition: pkt_send_co.cc:69
isc::dhcp::OptionCustomPtr
boost::shared_ptr< OptionCustom > OptionCustomPtr
A pointer to the OptionCustom object.
Definition: option_custom.h:464
D6O_IA_NA
@ D6O_IA_NA
Definition: dhcp6.h:23
add4Option
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:240
dhcp6.h
generate_output_record
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:355
user_chk
Defines the logger used by the user check hooks library.
Definition: user.cc:19
D6O_IAADDR
@ D6O_IAADDR
Definition: dhcp6.h:25
checkIAStatus
bool checkIAStatus(boost::shared_ptr< Option6IA > &ia_opt)
Tests given IA option set for successful status.
Definition: pkt_send_co.cc:497
isc::dhcp::Option::V6
@ V6
Definition: option.h:67
isc::dhcp::OptionPtr
boost::shared_ptr< Option > OptionPtr
Definition: option.h:37
isc::hooks
Definition: callout_handle.cc:21
DOCSIS3_V6_CONFIG_FILE
#define DOCSIS3_V6_CONFIG_FILE
Definition: docsis3_option_defs.h:35
getV6AddrStr
std::string getV6AddrStr(Pkt6Ptr response)
Stringify the lease address or prefix IPv6 response packet.
Definition: pkt_send_co.cc:384
D6O_STATUS_CODE
@ D6O_STATUS_CODE
Definition: dhcp6.h:33
option6_iaaddr.h
isc::dhcp::OptionCustom::readInteger
T readInteger(const uint32_t index=0) const
Read a buffer as integer value.
Definition: option_custom.h:251
D6O_IAPREFIX
@ D6O_IAPREFIX
Definition: dhcp6.h:46
option_custom.h
default_user4_id_str
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
isc::dhcp::Option::V4
@ V4
Definition: option.h:67
isc::dhcp::Pkt6Ptr
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition: pkt6.h:28
getAddrStrIA_NA
std::string getAddrStrIA_NA(OptionPtr options)
Stringify the lease address in an D6O_IA_NA option set.
Definition: pkt_send_co.cc:410
user_chk::UserPtr
boost::shared_ptr< User > UserPtr
Defines a smart pointer to a User.
Definition: user.h:241
getDefaultUser6
const UserPtr & getDefaultUser6()
Fetches the default IPv6 user from the registry.
Definition: pkt_send_co.cc:530
user_chk.h