Kea  1.5.0
token.cc
Go to the documentation of this file.
1 // Copyright (C) 2015-2018 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 
7 #include <config.h>
8 
9 #include <eval/token.h>
10 #include <eval/eval_log.h>
11 #include <eval/eval_context.h>
12 #include <util/encode/hex.h>
13 #include <util/io_utilities.h>
14 #include <asiolink/io_address.h>
15 #include <dhcp/pkt4.h>
16 #include <dhcp/pkt6.h>
17 #include <boost/lexical_cast.hpp>
18 #include <dhcp/dhcp4.h>
19 #include <dhcp/dhcp6.h>
20 #include <dhcp/option_vendor.h>
22 #include <cstring>
23 #include <string>
24 #include <iomanip>
25 #include <sstream>
26 
27 using namespace isc::dhcp;
28 using namespace isc::util;
29 using namespace std;
30 
32 
33 void
34 TokenString::evaluate(Pkt& /*pkt*/, ValueStack& values) {
35  // Literals only push, nothing to pop
36  values.push(value_);
37 
38  // Log what we pushed
39  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_STRING)
40  .arg('\'' + value_ + '\'');
41 }
42 
43 TokenHexString::TokenHexString(const string& str) : value_("") {
44  // Check string starts "0x" or "0x" and has at least one additional character.
45  if ((str.size() < 3) ||
46  (str[0] != '0') ||
47  ((str[1] != 'x') && (str[1] != 'X'))) {
48  return;
49  }
50  string digits = str.substr(2);
51 
52  // Transform string of hexadecimal digits into binary format
53  vector<uint8_t> binary;
54  try {
55  // The decodeHex function expects that the string contains an
56  // even number of digits. If we don't meet this requirement,
57  // we have to insert a leading 0.
58  if ((digits.length() % 2) != 0) {
59  digits = digits.insert(0, "0");
60  }
61  util::encode::decodeHex(digits, binary);
62  } catch (...) {
63  return;
64  }
65 
66  // Convert to a string (note that binary.size() cannot be 0)
67  value_.resize(binary.size());
68  memmove(&value_[0], &binary[0], binary.size());
69 }
70 
71 void
73  // Literals only push, nothing to pop
74  values.push(value_);
75 
76  // Log what we pushed
77  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_HEXSTRING)
78  .arg(toHex(value_));
79 }
80 
81 TokenIpAddress::TokenIpAddress(const string& addr) : value_("") {
82  // Transform IP address into binary format
83  vector<uint8_t> binary;
84  try {
85  asiolink::IOAddress ip(addr);
86  binary = ip.toBytes();
87  } catch (...) {
88  return;
89  }
90 
91  // Convert to a string (note that binary.size() is 4 or 16, so not 0)
92  value_.resize(binary.size());
93  memmove(&value_[0], &binary[0], binary.size());
94 }
95 
96 void
98  // Literals only push, nothing to pop
99  values.push(value_);
100 
101  // Log what we pushed
102  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_IPADDRESS)
103  .arg(toHex(value_));
104 }
105 
106 OptionPtr
108  return (pkt.getOption(option_code_));
109 }
110 
111 void
113  OptionPtr opt = getOption(pkt);
114  std::string opt_str;
115  if (opt) {
116  if (representation_type_ == TEXTUAL) {
117  opt_str = opt->toString();
118  } else if (representation_type_ == HEXADECIMAL) {
119  std::vector<uint8_t> binary = opt->toBinary();
120  opt_str.resize(binary.size());
121  if (!binary.empty()) {
122  memmove(&opt_str[0], &binary[0], binary.size());
123  }
124  } else {
125  opt_str = "true";
126  }
127  } else if (representation_type_ == EXISTS) {
128  opt_str = "false";
129  }
130 
131  // Push value of the option or empty string if there was no such option
132  // in the packet.
133  values.push(opt_str);
134 
135  // Log what we pushed, both exists and textual are simple text
136  // and can be output directly. We also include the code number
137  // of the requested option.
139  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_OPTION)
140  .arg(option_code_)
141  .arg(toHex(opt_str));
142  } else {
143  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_OPTION)
144  .arg(option_code_)
145  .arg('\'' + opt_str + '\'');
146  }
147 }
148 
149 std::string
151  std::string txt;
152  if (representation_type_ == EXISTS) {
153  txt = "false";
154  }
155  values.push(txt);
156  return (txt);
157 }
158 
159 TokenRelay4Option::TokenRelay4Option(const uint16_t option_code,
160  const RepresentationType& rep_type)
161  :TokenOption(option_code, rep_type) {
162 }
163 
165 
166  // Check if there is Relay Agent Option.
168  if (!rai) {
169  return (OptionPtr());
170  }
171 
172  // If there is, try to return its suboption
173  return (rai->getOption(option_code_));
174 }
175 
177 
178  try {
179  // Check if it's a Pkt6. If it's not the dynamic_cast will
180  // throw std::bad_cast.
181  Pkt6& pkt6 = dynamic_cast<Pkt6&>(pkt);
182 
183  try {
184  // Now that we have the right type of packet we can
185  // get the option and return it.
186  if (nest_level_ >= 0) {
187  uint8_t nesting_level = static_cast<uint8_t>(nest_level_);
188  return(pkt6.getRelayOption(option_code_, nesting_level));
189  } else {
190  int nesting_level = pkt6.relay_info_.size() + nest_level_;
191  if (nesting_level < 0) {
192  return (OptionPtr());
193  }
194  return(pkt6.getRelayOption(option_code_,
195  static_cast<uint8_t>(nesting_level)));
196  }
197  }
198  catch (const isc::OutOfRange&) {
199  // The only exception we expect is OutOfRange if the nest
200  // level is out of range of the encapsulations, for example
201  // if nest_level_ is 4 and there are only 2 encapsulations.
202  // We return a NULL in that case.
203  return (OptionPtr());
204  }
205 
206  } catch (const std::bad_cast&) {
207  isc_throw(EvalTypeError, "Specified packet is not Pkt6");
208  }
209 
210 }
211 
212 void
214 
215  string value;
216  vector<uint8_t> binary;
217  string type_str;
218  bool is_binary = true;
219  bool print_hex = true;
220  switch (type_) {
221  case IFACE:
222  is_binary = false;
223  print_hex = false;
224  value = pkt.getIface();
225  type_str = "iface";
226  break;
227  case SRC:
228  binary = pkt.getRemoteAddr().toBytes();
229  type_str = "src";
230  break;
231  case DST:
232  binary = pkt.getLocalAddr().toBytes();
233  type_str = "dst";
234  break;
235  case LEN:
236  // len() returns a size_t but in fact it can't be very large
237  // (with UDP transport it fits in 16 bits)
238  // the len() method is not const because of DHCPv6 relays.
239  // We assume here it has no bad side effects...
240  value = EvalContext::fromUint32(static_cast<uint32_t>(const_cast<Pkt&>(pkt).len()));
241  is_binary = false;
242  type_str = "len";
243  break;
244 
245  default:
246  isc_throw(EvalTypeError, "Bad meta data specified: "
247  << static_cast<int>(type_) );
248  }
249 
250  if (is_binary) {
251  value.resize(binary.size());
252  if (!binary.empty()) {
253  memmove(&value[0], &binary[0], binary.size());
254  }
255  }
256  values.push(value);
257 
258  // Log what we pushed
259  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_PKT)
260  .arg(type_str)
261  .arg(print_hex ? toHex(value) : value);
262 }
263 
264 void
266 
267  vector<uint8_t> binary;
268  string value;
269  string type_str;
270  try {
271  // Check if it's a Pkt4. If it's not, the dynamic_cast will throw
272  // std::bad_cast (failed dynamic_cast returns NULL for pointers and
273  // throws for references).
274  const Pkt4& pkt4 = dynamic_cast<const Pkt4&>(pkt);
275 
276  switch (type_) {
277  case CHADDR: {
278  HWAddrPtr hwaddr = pkt4.getHWAddr();
279  if (!hwaddr) {
280  // This should never happen. Every Pkt4 should always have
281  // a hardware address.
283  "Packet does not have hardware address");
284  }
285  binary = hwaddr->hwaddr_;
286  type_str = "mac";
287  break;
288  }
289  case GIADDR:
290  binary = pkt4.getGiaddr().toBytes();
291  type_str = "giaddr";
292  break;
293 
294  case CIADDR:
295  binary = pkt4.getCiaddr().toBytes();
296  type_str = "ciaddr";
297  break;
298 
299  case YIADDR:
300  binary = pkt4.getYiaddr().toBytes();
301  type_str = "yiaddr";
302  break;
303 
304  case SIADDR:
305  binary = pkt4.getSiaddr().toBytes();
306  type_str = "siaddr";
307  break;
308 
309  case HLEN:
310  // Pad the uint8_t field to 4 bytes.
311  value = EvalContext::fromUint32(pkt4.getHlen());
312  type_str = "hlen";
313  break;
314 
315  case HTYPE:
316  // Pad the uint8_t field to 4 bytes.
317  value = EvalContext::fromUint32(pkt4.getHtype());
318  type_str = "htype";
319  break;
320  case MSGTYPE:
321  value = EvalContext::fromUint32(pkt4.getType());
322  type_str = "msgtype";
323  break;
324  case TRANSID:
325  value = EvalContext::fromUint32(pkt4.getTransid());
326  type_str = "transid";
327  break;
328 
329  default:
330  isc_throw(EvalTypeError, "Bad field specified: "
331  << static_cast<int>(type_) );
332  }
333 
334  } catch (const std::bad_cast&) {
335  isc_throw(EvalTypeError, "Specified packet is not a Pkt4");
336  }
337 
338  if (!binary.empty()) {
339  value.resize(binary.size());
340  memmove(&value[0], &binary[0], binary.size());
341  }
342  values.push(value);
343 
344  // Log what we pushed
345  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_PKT4)
346  .arg(type_str)
347  .arg(toHex(value));
348 }
349 
350 void
352 
353  string value;
354  string type_str;
355  try {
356  // Check if it's a Pkt6. If it's not the dynamic_cast will throw
357  // std::bad_cast (failed dynamic_cast returns NULL for pointers and
358  // throws for references).
359  const Pkt6& pkt6 = dynamic_cast<const Pkt6&>(pkt);
360 
361  switch (type_) {
362  case MSGTYPE: {
363  // msg type is an uint8_t integer. We want a 4 byte string so 0 pad.
364  value = EvalContext::fromUint32(pkt6.getType());
365  type_str = "msgtype";
366  break;
367  }
368  case TRANSID: {
369  // transaction id is an uint32_t integer. We want a 4 byte string so copy
370  value = EvalContext::fromUint32(pkt6.getTransid());
371  type_str = "transid";
372  break;
373  }
374  default:
375  isc_throw(EvalTypeError, "Bad field specified: "
376  << static_cast<int>(type_) );
377  }
378 
379  } catch (const std::bad_cast&) {
380  isc_throw(EvalTypeError, "Specified packet is not Pkt6");
381  }
382 
383  values.push(value);
384 
385  // Log what we pushed
386  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_PKT6)
387  .arg(type_str)
388  .arg(toHex(value));
389 }
390 
391 void
393 
394  vector<uint8_t> binary;
395  string type_str;
396  try {
397  // Check if it's a Pkt6. If it's not the dynamic_cast will
398  // throw std::bad_cast.
399  const Pkt6& pkt6 = dynamic_cast<const Pkt6&>(pkt);
400  uint8_t relay_level;
401 
402  try {
403  if (nest_level_ >= 0) {
404  relay_level = static_cast<uint8_t>(nest_level_);
405  } else {
406  int nesting_level = pkt6.relay_info_.size() + nest_level_;
407  if (nesting_level < 0) {
408  // Don't throw OutOfRange here
409  nesting_level = 32;
410  }
411  relay_level = static_cast<uint8_t>(nesting_level);
412  }
413  switch (type_) {
414  // Now that we have the right type of packet we can
415  // get the option and return it.
416  case LINKADDR:
417  type_str = "linkaddr";
418  binary = pkt6.getRelay6LinkAddress(relay_level).toBytes();
419  break;
420  case PEERADDR:
421  type_str = "peeraddr";
422  binary = pkt6.getRelay6PeerAddress(relay_level).toBytes();
423  break;
424  }
425  } catch (const isc::OutOfRange&) {
426  // The only exception we expect is OutOfRange if the nest
427  // level is invalid. We push "" in that case.
428  values.push("");
429  // Log what we pushed
430  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_RELAY6_RANGE)
431  .arg(type_str)
432  .arg(int(nest_level_))
433  .arg("0x");
434  return;
435  }
436  } catch (const std::bad_cast&) {
437  isc_throw(EvalTypeError, "Specified packet is not Pkt6");
438  }
439 
440  string value;
441  value.resize(binary.size());
442  if (!binary.empty()) {
443  memmove(&value[0], &binary[0], binary.size());
444  }
445  values.push(value);
446 
447  // Log what we pushed
448  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_RELAY6)
449  .arg(type_str)
450  .arg(int(nest_level_))
451  .arg(toHex(value));
452 }
453 
454 void
455 TokenEqual::evaluate(Pkt& /*pkt*/, ValueStack& values) {
456 
457  if (values.size() < 2) {
458  isc_throw(EvalBadStack, "Incorrect stack order. Expected at least "
459  "2 values for == operator, got " << values.size());
460  }
461 
462  string op1 = values.top();
463  values.pop();
464  string op2 = values.top();
465  values.pop(); // Dammit, std::stack interface is awkward.
466 
467  if (op1 == op2)
468  values.push("true");
469  else
470  values.push("false");
471 
472  // Log what we popped and pushed
473  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_EQUAL)
474  .arg(toHex(op1))
475  .arg(toHex(op2))
476  .arg('\'' + values.top() + '\'');
477 }
478 
479 void
481 
482  if (values.size() < 3) {
483  isc_throw(EvalBadStack, "Incorrect stack order. Expected at least "
484  "3 values for substring operator, got " << values.size());
485  }
486 
487  string len_str = values.top();
488  values.pop();
489  string start_str = values.top();
490  values.pop();
491  string string_str = values.top();
492  values.pop();
493 
494  // If we have no string to start with we push an empty string and leave
495  if (string_str.empty()) {
496  values.push("");
497 
498  // Log what we popped and pushed
499  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_SUBSTRING_EMPTY)
500  .arg(len_str)
501  .arg(start_str)
502  .arg("0x")
503  .arg("0x");
504  return;
505  }
506 
507  // Convert the starting position and length from strings to numbers
508  // the length may also be "all" in which case simply make it the
509  // length of the string.
510  // If we have a problem push an empty string and leave
511  int start_pos;
512  int length;
513  try {
514  start_pos = boost::lexical_cast<int>(start_str);
515  } catch (const boost::bad_lexical_cast&) {
516  isc_throw(EvalTypeError, "the parameter '" << start_str
517  << "' for the starting position of the substring "
518  << "couldn't be converted to an integer.");
519  }
520  try {
521  if (len_str == "all") {
522  length = string_str.length();
523  } else {
524  length = boost::lexical_cast<int>(len_str);
525  }
526  } catch (const boost::bad_lexical_cast&) {
527  isc_throw(EvalTypeError, "the parameter '" << len_str
528  << "' for the length of the substring "
529  << "couldn't be converted to an integer.");
530  }
531 
532  const int string_length = string_str.length();
533  // If the starting position is outside of the string push an
534  // empty string and leave
535  if ((start_pos < -string_length) || (start_pos >= string_length)) {
536  values.push("");
537 
538  // Log what we popped and pushed
539  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_SUBSTRING_RANGE)
540  .arg(len_str)
541  .arg(start_str)
542  .arg(toHex(string_str))
543  .arg("0x");
544  return;
545  }
546 
547  // Adjust the values to be something for substr. We first figure out
548  // the starting position, then update it and the length to get the
549  // characters before or after it depending on the sign of length
550  if (start_pos < 0) {
551  start_pos = string_length + start_pos;
552  }
553 
554  if (length < 0) {
555  length = -length;
556  if (length <= start_pos){
557  start_pos -= length;
558  } else {
559  length = start_pos;
560  start_pos = 0;
561  }
562  }
563 
564  // and finally get the substring
565  values.push(string_str.substr(start_pos, length));
566 
567  // Log what we popped and pushed
568  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_SUBSTRING)
569  .arg(len_str)
570  .arg(start_str)
571  .arg(toHex(string_str))
572  .arg(toHex(values.top()));
573 }
574 
575 void
577 
578  if (values.size() < 2) {
579  isc_throw(EvalBadStack, "Incorrect stack order. Expected at least "
580  "2 values for concat, got " << values.size());
581  }
582 
583  string op1 = values.top();
584  values.pop();
585  string op2 = values.top();
586  values.pop(); // Dammit, std::stack interface is awkward.
587 
588  // The top of the stack was evaluated last so this is the right order
589  values.push(op2 + op1);
590 
591  // Log what we popped and pushed
592  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_CONCAT)
593  .arg(toHex(op1))
594  .arg(toHex(op2))
595  .arg(toHex(values.top()));
596 }
597 
598 void
600 
601  if (values.size() < 3) {
602  isc_throw(EvalBadStack, "Incorrect stack order. Expected at least "
603  "3 values for ifelse, got " << values.size());
604  }
605 
606  string iffalse = values.top();
607  values.pop();
608  string iftrue = values.top();
609  values.pop();
610  string cond = values.top();
611  values.pop();
612  bool val = toBool(cond);
613 
614  if (val) {
615  values.push(iftrue);
616  } else {
617  values.push(iffalse);
618  }
619 
620  // Log what we popped and pushed
621  if (val) {
622  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_IFELSE_TRUE)
623  .arg('\'' + cond + '\'')
624  .arg(toHex(iffalse))
625  .arg(toHex(iftrue));
626  } else {
627  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_IFELSE_FALSE)
628  .arg('\'' +cond + '\'')
629  .arg(toHex(iftrue))
630  .arg(toHex(iffalse));
631  }
632 }
633 
634 void
636  if (values.size() < 2) {
637  isc_throw(EvalBadStack, "Incorrect stack order. Expected at least "
638  "2 values for hexstring, got " << values.size());
639  }
640 
641  string separator = values.top();
642  values.pop();
643  string binary = values.top();
644  values.pop();
645 
646  bool first = true;
647  stringstream tmp;
648  tmp << hex;
649  for (size_t i = 0; i < binary.size(); ++i) {
650  if (!first) {
651  tmp << separator;
652  } else {
653  first = false;
654  }
655  tmp << setw(2) << setfill('0')
656  << (static_cast<unsigned>(binary[i]) & 0xff);
657  }
658  values.push(tmp.str());
659 
660  // Log what we popped and pushed
661  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_TOHEXSTRING)
662  .arg(toHex(binary))
663  .arg(separator)
664  .arg(tmp.str());
665 }
666 
667 void
668 TokenNot::evaluate(Pkt& /*pkt*/, ValueStack& values) {
669 
670  if (values.size() == 0) {
671  isc_throw(EvalBadStack, "Incorrect empty stack.");
672  }
673 
674  string op = values.top();
675  values.pop();
676  bool val = toBool(op);
677 
678  if (!val) {
679  values.push("true");
680  } else {
681  values.push("false");
682  }
683 
684  // Log what we popped and pushed
685  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_NOT)
686  .arg('\'' + op + '\'')
687  .arg('\'' + values.top() + '\'');
688 }
689 
690 void
691 TokenAnd::evaluate(Pkt& /*pkt*/, ValueStack& values) {
692 
693  if (values.size() < 2) {
694  isc_throw(EvalBadStack, "Incorrect stack order. Expected at least "
695  "2 values for and operator, got " << values.size());
696  }
697 
698  string op1 = values.top();
699  values.pop();
700  bool val1 = toBool(op1);
701  string op2 = values.top();
702  values.pop(); // Dammit, std::stack interface is awkward.
703  bool val2 = toBool(op2);
704 
705  if (val1 && val2) {
706  values.push("true");
707  } else {
708  values.push("false");
709  }
710 
711  // Log what we popped and pushed
712  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_AND)
713  .arg('\'' + op1 + '\'')
714  .arg('\'' + op2 + '\'')
715  .arg('\'' + values.top() + '\'');
716 }
717 
718 void
719 TokenOr::evaluate(Pkt& /*pkt*/, ValueStack& values) {
720 
721  if (values.size() < 2) {
722  isc_throw(EvalBadStack, "Incorrect stack order. Expected at least "
723  "2 values for or operator, got " << values.size());
724  }
725 
726  string op1 = values.top();
727  values.pop();
728  bool val1 = toBool(op1);
729  string op2 = values.top();
730  values.pop(); // Dammit, std::stack interface is awkward.
731  bool val2 = toBool(op2);
732 
733  if (val1 || val2) {
734  values.push("true");
735  } else {
736  values.push("false");
737  }
738 
739  // Log what we popped and pushed
740  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_OR)
741  .arg('\'' + op1 + '\'')
742  .arg('\'' + op2 + '\'')
743  .arg('\'' + values.top() + '\'');
744 }
745 
746 void
748  if (pkt.inClass(client_class_)) {
749  values.push("true");
750  } else {
751  values.push("false");
752  }
753 
754  // Log what we pushed
755  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_MEMBER)
756  .arg(client_class_)
757  .arg('\'' + values.top() + '\'');
758 }
759 
761  uint16_t option_code)
762  :TokenOption(option_code, repr), universe_(u), vendor_id_(vendor_id),
763  field_(option_code ? SUBOPTION : EXISTS)
764 {
765 }
766 
768  :TokenOption(0, TokenOption::HEXADECIMAL), universe_(u), vendor_id_(vendor_id),
769  field_(field)
770 {
771  if (field_ == EXISTS) {
773  }
774 }
775 
776 uint32_t TokenVendor::getVendorId() const {
777  return (vendor_id_);
778 }
779 
781  return (field_);
782 }
783 
784 void TokenVendor::evaluate(Pkt& pkt, ValueStack& values) {
785 
786  // Get the option first.
787  uint16_t code = 0;
788  switch (universe_) {
789  case Option::V4:
790  code = DHO_VIVSO_SUBOPTIONS;
791  break;
792  case Option::V6:
793  code = D6O_VENDOR_OPTS;
794  break;
795  }
796 
797  OptionPtr opt = pkt.getOption(code);
798  OptionVendorPtr vendor = boost::dynamic_pointer_cast<OptionVendor>(opt);
799  if (!vendor) {
800  // There's no vendor option, give up.
801  std::string txt = pushFailure(values);
802  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_NO_OPTION)
803  .arg(code)
804  .arg(txt);
805  return;
806  }
807 
808  if (vendor_id_ && (vendor_id_ != vendor->getVendorId())) {
809  // There is vendor option, but it has other vendor-id value
810  // than we're looking for. (0 means accept any vendor-id)
811  std::string txt = pushFailure(values);
812  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_ENTERPRISE_ID_MISMATCH)
813  .arg(vendor_id_)
814  .arg(vendor->getVendorId())
815  .arg(txt);
816  return;
817  }
818 
819  switch (field_) {
820  case ENTERPRISE_ID:
821  {
822  // Extract enterprise-id
823  string txt(sizeof(uint32_t), 0);
824  uint32_t value = htonl(vendor->getVendorId());
825  memcpy(&txt[0], &value, sizeof(uint32_t));
826  values.push(txt);
827  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_ENTERPRISE_ID)
828  .arg(vendor->getVendorId())
829  .arg(util::encode::encodeHex(std::vector<uint8_t>(txt.begin(),
830  txt.end())));
831  return;
832  }
833  case SUBOPTION:
836  TokenOption::evaluate(pkt, values);
837  return;
838  case EXISTS:
839  // We already passed all the checks: the option is there and has specified
840  // enterprise-id.
841  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_EXISTS)
842  .arg(vendor->getVendorId())
843  .arg("true");
844  values.push("true");
845  return;
846  case DATA:
847  // This is for vendor-class option, we can skip it here.
848  isc_throw(EvalTypeError, "Field None is not valid for vendor-class");
849  return;
850  }
851 }
852 
854  uint16_t code = 0;
855  switch (universe_) {
856  case Option::V4:
857  code = DHO_VIVSO_SUBOPTIONS;
858  break;
859  case Option::V6:
860  code = D6O_VENDOR_OPTS;
861  break;
862  }
863 
864  OptionPtr opt = pkt.getOption(code);
865  if (!opt) {
866  // If vendor option is not found, return NULL
867  return (opt);
868  }
869 
870  // If vendor option is found, try to return its
871  // encapsulated option.
872  return (opt->getOption(option_code_));
873 }
874 
876  RepresentationType repr)
877  :TokenVendor(u, vendor_id, repr, 0), index_(0) {
878 }
879 
881  FieldType field, uint16_t index)
882  :TokenVendor(u, vendor_id, TokenOption::HEXADECIMAL, 0), index_(index)
883 {
884  field_ = field;
885 }
886 
888  return (index_);
889 }
890 
892 
893  // Get the option first.
894  uint16_t code = 0;
895  switch (universe_) {
896  case Option::V4:
897  code = DHO_VIVCO_SUBOPTIONS;
898  break;
899  case Option::V6:
900  code = D6O_VENDOR_CLASS;
901  break;
902  }
903 
904  OptionPtr opt = pkt.getOption(code);
905  OptionVendorClassPtr vendor = boost::dynamic_pointer_cast<OptionVendorClass>(opt);
906  if (!vendor) {
907  // There's no vendor class option, give up.
908  std::string txt = pushFailure(values);
909  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_CLASS_NO_OPTION)
910  .arg(code)
911  .arg(txt);
912  return;
913  }
914 
915  if (vendor_id_ && (vendor_id_ != vendor->getVendorId())) {
916  // There is vendor option, but it has other vendor-id value
917  // than we're looking for. (0 means accept any vendor-id)
918  std::string txt = pushFailure(values);
919  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_CLASS_ENTERPRISE_ID_MISMATCH)
920  .arg(vendor_id_)
921  .arg(vendor->getVendorId())
922  .arg(txt);
923  return;
924  }
925 
926  switch (field_) {
927  case ENTERPRISE_ID:
928  {
929  // Extract enterprise-id
930  string txt(sizeof(uint32_t), 0);
931  uint32_t value = htonl(vendor->getVendorId());
932  memcpy(&txt[0], &value, sizeof(uint32_t));
933  values.push(txt);
934  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_CLASS_ENTERPRISE_ID)
935  .arg(vendor->getVendorId())
936  .arg(util::encode::encodeHex(std::vector<uint8_t>(txt.begin(),
937  txt.end())));
938  return;
939  }
940  case SUBOPTION:
941  // Extract sub-options
942  isc_throw(EvalTypeError, "Field None is not valid for vendor-class");
943  return;
944  case EXISTS:
945  // We already passed all the checks: the option is there and has specified
946  // enterprise-id.
947  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_CLASS_EXISTS)
948  .arg(vendor->getVendorId())
949  .arg("true");
950  values.push("true");
951  return;
952  case DATA:
953  {
954  size_t max = vendor->getTuplesNum();
955  if (index_ + 1 > max) {
956  // The index specified is out of bounds, e.g. there are only
957  // 2 tuples and index specified is 5.
958  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_CLASS_DATA_NOT_FOUND)
959  .arg(index_)
960  .arg(vendor->getVendorId())
961  .arg(max)
962  .arg("");
963  values.push("");
964  return;
965  }
966 
967  OpaqueDataTuple tuple = vendor->getTuple(index_);
968  OpaqueDataTuple::Buffer buf = tuple.getData();
969  string txt(buf.begin(), buf.end());
970 
971  LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_CLASS_DATA)
972  .arg(index_)
973  .arg(max)
974  .arg(txt);
975 
976  values.push(txt);
977  return;
978  }
979  default:
980  isc_throw(EvalTypeError, "Invalid field specified." << field_);
981  }
982 }
983 
984 TokenInteger::TokenInteger(const uint32_t value)
985  :TokenString(EvalContext::fromUint32(value)), int_value_(value) {
986 
987 }
data chunk, used in derived vendor-class only
Definition: token.h:898
boost::shared_ptr< OptionVendorClass > OptionVendorClassPtr
Defines a pointer to the OptionVendorClass.
void evaluate(Pkt &pkt, ValueStack &values)
Logical negation.
Definition: token.cc:668
uint32_t vendor_id_
Enterprise-id value.
Definition: token.h:982
virtual OptionPtr getOption(Pkt &pkt)
Attempts to get a suboption.
Definition: token.cc:853
interface name (string)
Definition: token.h:390
uint8_t getHlen() const
Returns hlen field.
Definition: pkt4.cc:541
HWAddrPtr getHWAddr() const
returns hardware address information
Definition: pkt4.h:326
void evaluate(Pkt &pkt, ValueStack &values)
Alternative.
Definition: token.cc:599
giaddr (IPv4 address)
Definition: token.h:441
The order where Token subtypes are declared should be:
Definition: token.h:114
uint8_t getHtype() const
Returns htype field.
Definition: pkt4.cc:533
const isc::asiolink::IOAddress & getRelay6LinkAddress(uint8_t relay_level) const
return the link address field from a relay option
Definition: pkt6.cc:211
uint16_t getDataIndex() const
Returns data index.
Definition: token.cc:887
bool inClass(const isc::dhcp::ClientClass &client_class)
Checks whether a client belongs to a given class.
Definition: pkt.cc:95
void evaluate(Pkt &pkt, ValueStack &values)
Gets a value from the specified packet.
Definition: token.cc:265
std::string value_
< Constant value (empty string if the IP address cannot be converted)
Definition: token.h:207
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition: hwaddr.h:154
std::vector< uint8_t > Buffer
Defines a type of the data buffer used to hold the opaque data.
const isc::asiolink::IOAddress & getCiaddr() const
Returns ciaddr field.
Definition: pkt4.h:183
uint16_t option_code_
Code of the option to be extracted.
Definition: token.h:296
If this token fetches a suboption, not a field.
Definition: token.h:895
FieldType type_
field to get
Definition: token.h:590
destination (IP address)
Definition: token.h:392
void evaluate(Pkt &pkt, ValueStack &values)
Gets a value of the specified packet.
Definition: token.cc:351
Base class for classes representing DHCP messages.
Definition: pkt.h:90
void evaluate(Pkt &pkt, ValueStack &values)
Token evaluation (check if client_class_ was added to packet client classes)
Definition: token.cc:747
boost::shared_ptr< Option > OptionPtr
Definition: option.h:37
Universe
defines option universe DHCPv4 or DHCPv6
Definition: option.h:67
Represents a DHCPv6 packet.
Definition: pkt6.h:44
const isc::asiolink::IOAddress & getSiaddr() const
Returns siaddr field.
Definition: pkt4.h:196
FieldType
Specifies a field of the vendor option.
Definition: token.h:894
OptionPtr getRelayOption(uint16_t option_code, uint8_t nesting_level)
Returns option inserted by relay.
Definition: pkt6.cc:190
ClientClass client_class_
The client class name.
Definition: token.h:873
Link address field (IPv6 address)
Definition: token.h:546
void evaluate(Pkt &pkt, ValueStack &values)
Extracts the specified field from the requested relay.
Definition: token.cc:392
transaction-id (xid)
Definition: token.h:448
const isc::asiolink::IOAddress & getRelay6PeerAddress(uint8_t relay_level) const
return the peer address field from a relay option
Definition: pkt6.cc:221
std::string getIface() const
Returns interface name.
Definition: pkt.h:504
isc::log::Logger eval_logger("eval")
Eval Logger.
Definition: eval_log.h:33
FieldType getField() const
Returns field.
Definition: token.cc:780
std::vector< RelayInfo > relay_info_
Relay information.
Definition: pkt6.h:434
TokenRelay4Option(const uint16_t option_code, const RepresentationType &rep_type)
Constructor for extracting sub-option from RAI (option 82)
Definition: token.cc:159
virtual OptionPtr getOption(Pkt &pkt)
Attempts to obtain specified option from the specified relay block.
Definition: token.cc:176
virtual OptionPtr getOption(Pkt &pkt)
Attempts to retrieve an option.
Definition: token.cc:107
void evaluate(Pkt &pkt, ValueStack &values)
This is a method for evaluating a packet.
Definition: token.cc:891
const isc::asiolink::IOAddress & getYiaddr() const
Returns yiaddr field.
Definition: pkt4.h:208
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition: edns.h:19
ciaddr (IPv4 address)
Definition: token.h:442
vendor[123].exists
Definition: token.h:897
Option::Universe universe_
Universe (V4 or V6)
Definition: token.h:976
length (4 octets)
Definition: token.h:393
enterprise-id field (vendor-info, vendor-class)
Definition: token.h:896
void decodeHex(const string &input, vector< uint8_t > &result)
Decode a text encoded in the base16 ('hex') format into the original data.
Definition: base_n.cc:466
TokenInteger(const uint32_t value)
Integer value set during construction.
Definition: token.cc:984
std::stack< std::string > ValueStack
Evaluated values are stored as a stack of strings.
Definition: token.h:33
uint8_t getType() const
Returns DHCP message type (e.g.
Definition: pkt4.cc:230
std::string value_
Constant value.
Definition: token.h:155
Evaluation context, an interface to the expression evaluation.
Definition: eval_context.h:34
static bool toBool(std::string value)
Coverts a (string) value to a boolean.
Definition: token.h:90
void evaluate(Pkt &pkt, ValueStack &values)
Extract a substring from a string.
Definition: token.cc:480
chaddr field (up to 16 bytes link-layer address)
Definition: token.h:440
Token that represents a value of an option.
Definition: token.h:219
htype (hardware address type)
Definition: token.h:446
const int EVAL_DBG_STACK
Definition: eval_log.h:26
virtual OptionPtr getOption(Pkt &pkt)
Attempts to obtain specified sub-option of option 82 from the packet.
Definition: token.cc:164
virtual std::string pushFailure(ValueStack &values)
Auxiliary method that puts string representing a failure.
Definition: token.cc:150
message type (not really a field, content of option 53)
Definition: token.h:447
void evaluate(Pkt &pkt, ValueStack &values)
Logical and.
Definition: token.cc:691
void evaluate(Pkt &pkt, ValueStack &values)
Token evaluation (puts value of the constant string on the stack after decoding or an empty string if...
Definition: token.cc:72
const isc::asiolink::IOAddress & getGiaddr() const
Returns giaddr field.
Definition: pkt4.h:220
void evaluate(Pkt &pkt, ValueStack &values)
Concatenate two values.
Definition: token.cc:576
void evaluate(Pkt &pkt, ValueStack &values)
Gets a value from the specified packet.
Definition: token.cc:213
const isc::asiolink::IOAddress & getRemoteAddr() const
Returns remote IP address.
Definition: pkt.h:417
TokenVendor(Option::Universe u, uint32_t vendor_id, FieldType field)
Constructor used for accessing a field.
Definition: token.cc:767
string encodeHex(const vector< uint8_t > &binary)
Encode binary data in the base16 ('hex') format.
Definition: base_n.cc:461
void evaluate(Pkt &pkt, ValueStack &values)
Token evaluation (puts value of the constant string on the stack after decoding)
Definition: token.cc:97
Represents DHCPv4 packet.
Definition: pkt4.h:38
void evaluate(Pkt &pkt, ValueStack &values)
Compare two values.
Definition: token.cc:455
RepresentationType
Token representation type.
Definition: token.h:229
EvalBadStack is thrown when more or less parameters are on the stack than expected.
Definition: token.h:37
yiaddr (IPv4 address)
Definition: token.h:443
const Buffer & getData() const
Returns a reference to the buffer holding tuple data.
Peer address field (IPv6 address)
Definition: token.h:545
RepresentationType representation_type_
Representation type.
Definition: token.h:297
OptionPtr getOption(const uint16_t type)
Returns the first option of specified type.
Definition: pkt.cc:70
void evaluate(Pkt &pkt, ValueStack &values)
Convert a binary value to its hexadecimal string representation.
Definition: token.cc:635
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
Represents a single instance of the opaque data preceded by length.
siaddr (IPv4 address)
Definition: token.h:444
virtual uint8_t getType() const
Returns message type (e.g.
Definition: pkt6.h:218
std::string toHex(std::string value)
Encode in hexadecimal inline.
Definition: hex.h:53
A generic exception that is thrown if a parameter given to a method would refer to or modify out-of-r...
uint32_t getVendorId() const
Returns enterprise-id.
Definition: token.cc:776
void evaluate(Pkt &pkt, ValueStack &values)
Token evaluation (puts value of the constant string on the stack)
Definition: token.cc:34
EvalTypeError is thrown when a value on the stack has a content with an unexpected type.
Definition: token.h:45
uint16_t index_
Data chunk index.
Definition: token.h:1058
void evaluate(Pkt &pkt, ValueStack &values)
Logical or.
Definition: token.cc:719
TokenIpAddress(const std::string &addr)
Value is set during token construction.
Definition: token.cc:81
source (IP address)
Definition: token.h:391
virtual void evaluate(Pkt &pkt, ValueStack &values)
This is a method for evaluating a packet.
Definition: token.cc:784
TokenHexString(const std::string &str)
Value is set during token construction.
Definition: token.cc:43
int8_t nest_level_
nesting level of the relay block to use
Definition: token.h:372
const isc::asiolink::IOAddress & getLocalAddr() const
Returns local IP address.
Definition: pkt.h:431
boost::shared_ptr< OptionVendor > OptionVendorPtr
Pointer to a vendor option.
int8_t nest_level_
Specifies field of the DHCPv6 relay option to get.
Definition: token.h:589
void evaluate(Pkt &pkt, ValueStack &values)
Evaluates the values of the option.
Definition: token.cc:112
uint32_t getTransid() const
Returns value of transaction-id field.
Definition: pkt.h:266
hlen (hardware address length)
Definition: token.h:445
TokenVendorClass(Option::Universe u, uint32_t vendor_id, RepresentationType repr)
This constructor is used to access fields.
Definition: token.cc:875
transaction id (integer but manipulated as a string)
Definition: token.h:494
Token that represents vendor options in DHCPv4 and DHCPv6.
Definition: token.h:890
FieldType field_
Specifies which field should be accessed.
Definition: token.h:985