PageRenderTime 36ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/elements/ipsec/ipsecroutetable.cc

https://github.com/kohler/click
C++ | 390 lines | 298 code | 42 blank | 50 comment | 83 complexity | 6aaed6f069158bd793d2279064a3349c MD5 | raw file
  1. // -*- c-basic-offset: 4 -*-
  2. /*
  3. * ipsecroutetable.{cc,hh} -- looks up next-hop address in route table and includes support for IPsec ESP tunnels
  4. * between a pair of gateways (check push and cp_ipsec_route)
  5. * Dimitris Syrivelis
  6. *
  7. * Copyright (c) 2006 University of Thessaly, Hellas
  8. *
  9. * This is an extended version of iproutetable
  10. * iproutetable.{cc,hh} -- looks up next-hop address in route table
  11. * Benjie Chen, Eddie Kohler
  12. *
  13. * Copyright (c) 2001 Massachusetts Institute of Technology
  14. * Copyright (c) 2002 International Computer Science Institute
  15. *
  16. *
  17. * Permission is hereby granted, free of charge, to any person
  18. * obtaining a copy of this software and associated documentation
  19. * files (the "Software"), to deal in the Software without
  20. * restriction, subject to the conditions listed in the Click LICENSE
  21. * file. These conditions include: you must preserve this copyright
  22. * notice, and you cannot mention the copyright holders in advertising
  23. * related to the Software without their permission. The Software is
  24. * provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This notice is a
  25. * summary of the Click LICENSE file; the license in that file is
  26. * legally binding.
  27. */
  28. #include <click/config.h>
  29. #include <click/ipaddress.hh>
  30. #include <click/args.hh>
  31. #include <click/error.hh>
  32. #include <click/glue.hh>
  33. #include <click/straccum.hh>
  34. #include <click/router.hh>
  35. #include <click/packet_anno.hh>
  36. #include "ipsecroutetable.hh"
  37. #include "esp.hh"
  38. CLICK_DECLS
  39. //changed to support IPsec extensions
  40. bool
  41. cp_ipsec_route(String s, IPsecRoute *r_store, bool remove_route, Element *context)
  42. {
  43. IPsecRoute r;
  44. //Data to initialize the SADataTuple
  45. unsigned int replay;
  46. uint8_t oowin;
  47. SADataTuple * sa_data;
  48. if (!IPPrefixArg(true).parse(cp_shift_spacevec(s), r.addr, r.mask, context))
  49. return false;
  50. r.addr &= r.mask;
  51. String word = cp_shift_spacevec(s);
  52. if (word == "-")
  53. /* null gateway; do nothing */;
  54. else if (IPAddressArg().parse(word, r.gw, context))
  55. /* do nothing */;
  56. else
  57. goto two_words;
  58. word = cp_shift_spacevec(s);
  59. two_words:
  60. if (IntArg().parse(word, r.port) || (!word && remove_route))
  61. //Ipsec extensions parsing
  62. word = cp_shift_spacevec(s);
  63. if (!word) {
  64. //no further arguments found so no ipsec extensions need to be added for this route
  65. r.spi = SPI(0);
  66. r.sa_data = NULL;
  67. //store routing table
  68. *r_store = r;
  69. return true;
  70. }
  71. Vector<String> words;
  72. words.push_back(word);
  73. cp_spacevec(s, words);
  74. String enc_key, auth_key;
  75. if (Args(words, context, ErrorHandler::default_handler())
  76. .read_mp("SPI", r.spi)
  77. .read_mp("ENCRYPT_KEY", enc_key)
  78. .read_mp("AUTH_KEY", auth_key)
  79. .read_mp("REPLAY", replay)
  80. .read_mp("OOSIZE", oowin)
  81. .complete() < 0)
  82. return false;
  83. if (enc_key.length() != 16 || auth_key.length() != 16) {
  84. click_chatter("key has bad length");
  85. return false;
  86. }
  87. // Create new Security Association Table entry
  88. sa_data = new SADataTuple(enc_key.data(), auth_key.data(), replay, oowin);
  89. ((IPsecRouteTable*)context)->_sa_table.insert(SPI(r.spi),*sa_data);
  90. //Set Tuple reference in the Routing entry
  91. r.sa_data = sa_data;
  92. //store routing table
  93. *r_store = r;
  94. return true;
  95. }
  96. //changed to support ipsec extensions
  97. StringAccum&
  98. IPsecRoute::unparse(StringAccum& sa, bool tabs) const
  99. {
  100. int l = sa.length();
  101. char tab = (tabs ? '\t' : ' ');
  102. sa << addr.unparse_with_mask(mask) << tab;
  103. if (sa.length() < l + 17 && tabs)
  104. sa << '\t';
  105. l = sa.length();
  106. if (gw)
  107. sa << gw << tab;
  108. else
  109. sa << '-' << tab;
  110. if (sa.length() < l + 9 && tabs)
  111. sa << '\t';
  112. if (!real())
  113. sa << "-1";
  114. else
  115. sa << port;
  116. if(spi != 0) {
  117. sa << " |TUNNELED CONNECTION| \n|SPI| |ENC KEY| |AUTH KEY| ||\n";
  118. sa << " |" <<spi<<"|";
  119. sa << sa_data->unparse_entries().c_str() <<"\n";
  120. }
  121. return sa;
  122. }
  123. String
  124. IPsecRoute::unparse() const
  125. {
  126. StringAccum sa;
  127. sa << *this;
  128. return sa.take_string();
  129. }
  130. void *
  131. IPsecRouteTable::cast(const char *name)
  132. {
  133. if (strcmp(name, "IPsecRouteTable") == 0)
  134. return (void *)this;
  135. else
  136. return Element::cast(name);
  137. }
  138. int
  139. IPsecRouteTable::configure(Vector<String> &conf, ErrorHandler *errh)
  140. {
  141. IPsecRoute r;
  142. for (int i = 0; i < conf.size(); i++) {
  143. if (cp_ipsec_route(conf[i], &r, false, this)
  144. && r.port >= 0 && r.port < noutputs())
  145. (void) add_route(r, false, 0, errh);
  146. else
  147. errh->error("argument %d should be 'ADDR/MASK [GATEWAY] OUTPUT'", i+1);
  148. }
  149. return errh->nerrors() ? -1 : 0;
  150. }
  151. int
  152. IPsecRouteTable::add_route(const IPsecRoute&, bool, IPsecRoute*, ErrorHandler *errh)
  153. {
  154. // by default, cannot add routes
  155. return errh->error("cannot add routes to this routing table");
  156. }
  157. int
  158. IPsecRouteTable::remove_route(const IPsecRoute&, IPsecRoute*, ErrorHandler *errh)
  159. {
  160. // by default, cannot remove routes
  161. return errh->error("cannot delete routes from this routing table");
  162. }
  163. int
  164. IPsecRouteTable::lookup_route(IPAddress, IPAddress &, unsigned int&, SADataTuple*&) const
  165. {
  166. return -1; // by default, route lookups fail
  167. }
  168. String
  169. IPsecRouteTable::dump_routes()
  170. {
  171. return String();
  172. }
  173. void
  174. IPsecRouteTable::push(int, Packet *p)
  175. {
  176. IPAddress gw;
  177. uint32_t spi;
  178. SADataTuple * sa_data;
  179. const click_ip *ip = reinterpret_cast< const click_ip *>(p->data());
  180. int port = lookup_route(p->dst_ip_anno(), gw, spi, sa_data);
  181. if (port >= 0) {
  182. switch(port) {
  183. case 1: {
  184. //This packet should be sent over a tunneled connection
  185. //so set proper annotations with references to Security Data to be used by IPsec modules
  186. if((spi == 0) || (sa_data == NULL)) {
  187. click_chatter("No Ipsec tunnel for %s. Wrong tunnel setup", p->dst_ip_anno().unparse().c_str());
  188. }
  189. SET_IPSEC_SPI_ANNO(p,(uint32_t)spi);
  190. //ISSUE: This is 32-bit architecture specific passing a pointer to next module through annotations!!
  191. SET_IPSEC_SA_DATA_REFERENCE_ANNO(p, (uintptr_t)sa_data);
  192. break;
  193. }
  194. case 0: {
  195. //Is this packet an ipsec ESP packet? What if one just needs to communicate with a server
  196. //that runs on this router?
  197. if(ip->ip_p != 50) {
  198. /*This not an IPSEC packet and it should be delivered to the host's linux network stack
  199. In a typical setup one would send anything that is directed to port 2 to Linux */
  200. port = 2;
  201. }
  202. // This is an ipsec packet and belongs to a tunneled connection
  203. // so we set the proper annotation with reference to Security Data Table to be used by IPsec modules
  204. // Careful this enhancement is 32-bit architecture specific!!
  205. struct esp_new * esp =(struct esp_new *)(p->data()+sizeof(click_ip));
  206. sa_data = _sa_table.lookup(SPI(ntohl(esp->esp_spi)));
  207. if(sa_data == NULL) {
  208. click_chatter("Invalid SPI %d, Dropping packet",ntohl(esp->esp_spi));
  209. p->kill();
  210. return;
  211. }
  212. SET_IPSEC_SA_DATA_REFERENCE_ANNO(p, (uintptr_t)sa_data);
  213. break;
  214. }
  215. }; //end of switch
  216. assert(port < noutputs());
  217. if (gw)
  218. p->set_dst_ip_anno(gw);
  219. output(port).push(p);
  220. } else {
  221. static int complained = 0;
  222. if (++complained <= 5)
  223. click_chatter("IPsecRouteTable: no route for %s", p->dst_ip_anno().unparse().c_str());
  224. p->kill();
  225. }
  226. }
  227. int
  228. IPsecRouteTable::run_command(int command, const String &str, Vector<IPsecRoute> * old_routes, ErrorHandler *errh)
  229. {
  230. IPsecRoute route, old_route;
  231. if (!cp_ipsec_route(str, &route, command == CMD_REMOVE, this)
  232. || route.port < (command == CMD_REMOVE ? -1 : 0)
  233. || route.port >= noutputs())
  234. return errh->error("expected 'ADDR/MASK [GATEWAY%s'", (command == CMD_REMOVE ? " OUTPUT]" : "] OUTPUT"));
  235. int r, before = errh->nerrors();
  236. if (command == CMD_ADD)
  237. r = add_route(route, false, &old_route, errh);
  238. else if (command == CMD_SET)
  239. r = add_route(route, true, &old_route, errh);
  240. else
  241. r = remove_route(route, &old_route, errh);
  242. // save old route if in a transaction
  243. if (r >= 0 && old_routes) {
  244. if (old_route.port < 0) { // must come from add_route
  245. old_route = route;
  246. old_route.extra = CMD_ADD;
  247. } else
  248. old_route.extra = command;
  249. old_routes->push_back(old_route);
  250. }
  251. // report common errors
  252. if (r == -EEXIST && errh->nerrors() == before)
  253. errh->error("conflict with existing route '%s'", old_route.unparse().c_str());
  254. if (r == -ENOENT && errh->nerrors() == before)
  255. errh->error("route '%s' not found", route.unparse().c_str());
  256. return r;
  257. }
  258. int
  259. IPsecRouteTable::add_route_handler(const String &conf, Element *e, void *thunk, ErrorHandler *errh)
  260. {
  261. IPsecRouteTable *table = static_cast<IPsecRouteTable *>(e);
  262. return table->run_command((thunk ? CMD_SET : CMD_ADD), conf, 0, errh);
  263. }
  264. int
  265. IPsecRouteTable::remove_route_handler(const String &conf, Element *e, void *, ErrorHandler *errh)
  266. {
  267. IPsecRouteTable *table = static_cast<IPsecRouteTable *>(e);
  268. return table->run_command(CMD_REMOVE, conf, 0, errh);
  269. }
  270. int
  271. IPsecRouteTable::ctrl_handler(const String &conf_in, Element *e, void *, ErrorHandler *errh)
  272. {
  273. IPsecRouteTable *table = static_cast<IPsecRouteTable *>(e);
  274. String conf = cp_uncomment(conf_in);
  275. const char* s = conf.begin(), *end = conf.end();
  276. Vector<IPsecRoute> old_routes;
  277. int r = 0;
  278. while (s < end) {
  279. const char* nl = find(s, end, '\n');
  280. String line = conf.substring(s, nl);
  281. String first_word = cp_shift_spacevec(line);
  282. int command;
  283. if (first_word == "add")
  284. command = CMD_ADD;
  285. else if (first_word == "remove")
  286. command = CMD_REMOVE;
  287. else if (first_word == "set")
  288. command = CMD_SET;
  289. else if (!first_word)
  290. continue;
  291. else {
  292. r = errh->error("bad command '%#s'", first_word.c_str());
  293. goto rollback;
  294. }
  295. if ((r = table->run_command(command, line, &old_routes, errh)) < 0)
  296. goto rollback;
  297. s = nl + 1;
  298. }
  299. return 0;
  300. rollback:
  301. while (old_routes.size()) {
  302. const IPsecRoute& rt = old_routes.back();
  303. if (rt.extra == CMD_REMOVE)
  304. table->add_route(rt, false, 0, errh);
  305. else if (rt.extra == CMD_ADD)
  306. table->remove_route(rt, 0, errh);
  307. else
  308. table->add_route(rt, true, 0, errh);
  309. old_routes.pop_back();
  310. }
  311. return r;
  312. }
  313. String
  314. IPsecRouteTable::table_handler(Element *e, void *)
  315. {
  316. IPsecRouteTable *r = static_cast<IPsecRouteTable*>(e);
  317. return r->dump_routes();
  318. }
  319. int
  320. IPsecRouteTable::lookup_handler(int, String& s, Element* e, const Handler*, ErrorHandler* errh)
  321. {
  322. IPsecRouteTable *table = static_cast<IPsecRouteTable*>(e);
  323. IPAddress a;
  324. if (IPAddressArg().parse(cp_uncomment(s), a, table)) {
  325. IPAddress gw;
  326. uint32_t spi;
  327. SADataTuple * sa_data;
  328. int port = table->lookup_route(a, gw,spi,sa_data);
  329. if (gw)
  330. s = String(port) + " " + gw.unparse();
  331. else
  332. s = String(port);
  333. return 0;
  334. } else
  335. return errh->error("expected IP address, not '%s'", s.c_str());
  336. }
  337. void
  338. IPsecRouteTable::add_handlers()
  339. {
  340. add_write_handler("add", add_route_handler, 0);
  341. add_write_handler("set", add_route_handler, 1);
  342. add_write_handler("remove", remove_route_handler, 0);
  343. add_write_handler("ctrl", ctrl_handler, 0);
  344. add_read_handler("table", table_handler, 0);
  345. set_handler("lookup", Handler::OP_READ | Handler::READ_PARAM, lookup_handler);
  346. }
  347. CLICK_ENDDECLS
  348. ELEMENT_PROVIDES(IPsecRouteTable)