PageRenderTime 42ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/elements/standard/classifier.cc

https://github.com/bhesmans/click
C++ | 260 lines | 196 code | 32 blank | 32 comment | 64 complexity | 7f25a9f56759e0a6247bda757091a777 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause
  1. /*
  2. * classifier.{cc,hh} -- element is a generic classifier
  3. * Eddie Kohler
  4. *
  5. * Copyright (c) 1999-2000 Massachusetts Institute of Technology
  6. * Copyright (c) 2000 Mazu Networks, Inc.
  7. * Copyright (c) 2008 Regents of the University of California
  8. * Copyright (c) 2010 Meraki, Inc.
  9. *
  10. * Permission is hereby granted, free of charge, to any person obtaining a
  11. * copy of this software and associated documentation files (the "Software"),
  12. * to deal in the Software without restriction, subject to the conditions
  13. * listed in the Click LICENSE file. These conditions include: you must
  14. * preserve this copyright notice, and you cannot mention the copyright
  15. * holders in advertising related to the Software without their permission.
  16. * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
  17. * notice is a summary of the Click LICENSE file; the license in that file is
  18. * legally binding.
  19. */
  20. #include <click/config.h>
  21. #include "classifier.hh"
  22. #include <click/glue.hh>
  23. #include <click/error.hh>
  24. #include <click/confparse.hh>
  25. #include <click/straccum.hh>
  26. #if !HAVE_INDIFFERENT_ALIGNMENT
  27. #include <click/router.hh>
  28. #endif
  29. #include <click/standard/alignmentinfo.hh>
  30. CLICK_DECLS
  31. Classifier::Classifier()
  32. {
  33. }
  34. Classification::Wordwise::Program
  35. Classifier::empty_program(ErrorHandler *errh) const
  36. {
  37. // set align offset
  38. int c, o;
  39. if (AlignmentInfo::query(this, 0, c, o) && c >= 4)
  40. // want 'data - _align_offset' aligned at 4/(o%4)
  41. o = (4 - (o % 4)) % 4;
  42. else {
  43. #if !HAVE_INDIFFERENT_ALIGNMENT
  44. if (errh) {
  45. errh->warning("alignment unknown, but machine is sensitive to alignment");
  46. void *&x = router()->force_attachment("Classifier alignment warning");
  47. if (!x) {
  48. x = (void *) this;
  49. errh->message("(%s must be told how its input packets are aligned in memory.\n"
  50. "Fix this error either by passing your configuration through click-align,\n"
  51. "or by providing explicit AlignmentInfo. I am assuming the equivalent\n"
  52. "of %<AlignmentInfo(%s 4 0)%>.)", class_name(), name().c_str());
  53. }
  54. }
  55. #else
  56. (void) errh;
  57. #endif
  58. o = 0;
  59. }
  60. return Classification::Wordwise::Program(o);
  61. }
  62. static void
  63. update_value_mask(int c, int shift, int &value, int &mask)
  64. {
  65. int v = 0, m = 0xF;
  66. if (c == '?')
  67. v = m = 0;
  68. else if (c >= '0' && c <= '9')
  69. v = c - '0';
  70. else if (c >= 'A' && c <= 'F')
  71. v = c - 'A' + 10;
  72. else if (c >= 'a' && c <= 'f')
  73. v = c - 'a' + 10;
  74. value |= (v << shift);
  75. mask |= (m << shift);
  76. }
  77. void
  78. Classifier::parse_program(Classification::Wordwise::Program &prog,
  79. Vector<String> &conf, ErrorHandler *errh)
  80. {
  81. Vector<int> tree = prog.init_subtree();
  82. prog.start_subtree(tree);
  83. for (int slot = 0; slot < conf.size(); slot++) {
  84. int i = 0;
  85. int len = conf[slot].length();
  86. const char *s = conf[slot].data();
  87. bool empty = true;
  88. prog.start_subtree(tree);
  89. if (s[0] == '-' && len == 1)
  90. // slot accepting everything
  91. i = 1;
  92. while (i < len) {
  93. while (i < len && isspace((unsigned char) s[i]))
  94. i++;
  95. if (i >= len)
  96. break;
  97. // negated?
  98. bool negated = false;
  99. if (s[i] == '!') {
  100. negated = true;
  101. i++;
  102. while (i < len && isspace((unsigned char) s[i]))
  103. i++;
  104. }
  105. if (i >= len || !isdigit((unsigned char) s[i])) {
  106. errh->error("pattern %d: expected a digit", slot);
  107. break;
  108. }
  109. // read offset
  110. int offset = 0;
  111. while (i < len && isdigit((unsigned char) s[i])) {
  112. offset *= 10;
  113. offset += s[i] - '0';
  114. i++;
  115. }
  116. if (i >= len || s[i] != '/') {
  117. errh->error("pattern %d: expected %</%>", slot);
  118. break;
  119. }
  120. i++;
  121. // scan past value
  122. int value_pos = i;
  123. while (i < len && (isxdigit((unsigned char) s[i]) || s[i] == '?'))
  124. i++;
  125. int value_end = i;
  126. // scan past mask
  127. int mask_pos = -1;
  128. int mask_end = -1;
  129. if (i < len && s[i] == '%') {
  130. i++;
  131. mask_pos = i;
  132. while (i < len && (isxdigit((unsigned char) s[i]) || s[i] == '?'))
  133. i++;
  134. mask_end = i;
  135. }
  136. // check lengths
  137. if (value_end - value_pos < 2) {
  138. errh->error("pattern %d: value has less than 2 hex digits", slot);
  139. value_end = value_pos;
  140. mask_end = mask_pos;
  141. }
  142. if ((value_end - value_pos) % 2 != 0) {
  143. errh->error("pattern %d: value has odd number of hex digits", slot);
  144. value_end--;
  145. mask_end--;
  146. }
  147. if (mask_pos >= 0 && (mask_end - mask_pos) != (value_end - value_pos)) {
  148. bool too_many = (mask_end - mask_pos) > (value_end - value_pos);
  149. errh->error("pattern %d: mask has too %s hex digits", slot,
  150. (too_many ? "many" : "few"));
  151. if (too_many)
  152. mask_end = mask_pos + value_end - value_pos;
  153. else
  154. value_end = value_pos + mask_end - mask_pos;
  155. }
  156. // add values to exprs
  157. prog.start_subtree(tree);
  158. bool first = true;
  159. offset += prog.align_offset();
  160. while (value_pos < value_end) {
  161. int v = 0, m = 0;
  162. update_value_mask(s[value_pos], 4, v, m);
  163. update_value_mask(s[value_pos+1], 0, v, m);
  164. value_pos += 2;
  165. if (mask_pos >= 0) {
  166. int mv = 0, mm = 0;
  167. update_value_mask(s[mask_pos], 4, mv, mm);
  168. update_value_mask(s[mask_pos+1], 0, mv, mm);
  169. mask_pos += 2;
  170. m = m & mv & mm;
  171. }
  172. if (first || offset % 4 == 0) {
  173. prog.add_insn(tree, (offset / 4) * 4, 0, 0);
  174. first = empty = false;
  175. }
  176. prog.back().mask.c[offset % 4] = m;
  177. prog.back().value.c[offset % 4] = v & m;
  178. offset++;
  179. }
  180. // combine with "and"
  181. prog.finish_subtree(tree, Classification::c_and);
  182. if (negated)
  183. prog.negate_subtree(tree);
  184. }
  185. // add fake expr if required
  186. if (empty)
  187. prog.add_insn(tree, 0, 0, 0);
  188. prog.finish_subtree(tree, Classification::c_and, -slot);
  189. }
  190. prog.finish_subtree(tree, Classification::c_or, Classification::j_never, Classification::j_never);
  191. // click_chatter("%s", prog.unparse().c_str());
  192. prog.optimize(0, 0, Classification::offset_max);
  193. // click_chatter("%s", prog.unparse().c_str());
  194. }
  195. int
  196. Classifier::configure(Vector<String> &conf, ErrorHandler *errh)
  197. {
  198. if (conf.size() != noutputs())
  199. return errh->error("need %d arguments, one per output port", noutputs());
  200. Classification::Wordwise::Program prog = empty_program(errh);
  201. parse_program(prog, conf, errh);
  202. if (!errh->nerrors()) {
  203. prog.warn_unused_outputs(noutputs(), errh);
  204. _prog = prog;
  205. return 0;
  206. } else
  207. return -1;
  208. }
  209. String
  210. Classifier::program_string(Element *element, void *)
  211. {
  212. Classifier *c = static_cast<Classifier *>(element);
  213. return c->_prog.unparse();
  214. }
  215. void
  216. Classifier::add_handlers()
  217. {
  218. add_read_handler("program", Classifier::program_string, 0, Handler::CALM);
  219. }
  220. void
  221. Classifier::push(int, Packet *p)
  222. {
  223. checked_output_push(_prog.match(p), p);
  224. }
  225. CLICK_ENDDECLS
  226. ELEMENT_REQUIRES(AlignmentInfo Classification)
  227. EXPORT_ELEMENT(Classifier)
  228. ELEMENT_MT_SAFE(Classifier)