nhrp_cache.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. /* NHRP cache
  2. * Copyright (c) 2014-2015 Timo Teräs
  3. *
  4. * This file is free software: you may copy, redistribute and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 2 of the License, or
  7. * (at your option) any later version.
  8. */
  9. #include "zebra.h"
  10. #include "memory.h"
  11. #include "thread.h"
  12. #include "hash.h"
  13. #include "nhrpd.h"
  14. #include "netlink.h"
  15. unsigned long nhrp_cache_counts[NHRP_CACHE_NUM_TYPES];
  16. const char * const nhrp_cache_type_str[] = {
  17. [NHRP_CACHE_INVALID] = "invalid",
  18. [NHRP_CACHE_INCOMPLETE] = "incomplete",
  19. [NHRP_CACHE_NEGATIVE] = "negative",
  20. [NHRP_CACHE_CACHED] = "cached",
  21. [NHRP_CACHE_DYNAMIC] = "dynamic",
  22. [NHRP_CACHE_NHS] = "nhs",
  23. [NHRP_CACHE_STATIC] = "static",
  24. [NHRP_CACHE_LOCAL] = "local",
  25. };
  26. static unsigned int nhrp_cache_protocol_key(void *peer_data)
  27. {
  28. struct nhrp_cache *p = peer_data;
  29. return sockunion_hash(&p->remote_addr);
  30. }
  31. static int nhrp_cache_protocol_cmp(const void *cache_data, const void *key_data)
  32. {
  33. const struct nhrp_cache *a = cache_data;
  34. const struct nhrp_cache *b = key_data;
  35. return sockunion_same(&a->remote_addr, &b->remote_addr);
  36. }
  37. static void *nhrp_cache_alloc(void *data)
  38. {
  39. struct nhrp_cache *p, *key = data;
  40. p = XMALLOC(MTYPE_NHRP_CACHE, sizeof(struct nhrp_cache));
  41. if (p) {
  42. *p = (struct nhrp_cache) {
  43. .cur.type = NHRP_CACHE_INVALID,
  44. .new.type = NHRP_CACHE_INVALID,
  45. .remote_addr = key->remote_addr,
  46. .ifp = key->ifp,
  47. .notifier_list = NOTIFIER_LIST_INITIALIZER(&p->notifier_list),
  48. };
  49. nhrp_cache_counts[p->cur.type]++;
  50. }
  51. return p;
  52. }
  53. static void nhrp_cache_free(struct nhrp_cache *c)
  54. {
  55. struct nhrp_interface *nifp = c->ifp->info;
  56. zassert(c->cur.type == NHRP_CACHE_INVALID && c->cur.peer == NULL);
  57. zassert(c->new.type == NHRP_CACHE_INVALID && c->new.peer == NULL);
  58. nhrp_cache_counts[c->cur.type]--;
  59. notifier_call(&c->notifier_list, NOTIFY_CACHE_DELETE);
  60. zassert(!notifier_active(&c->notifier_list));
  61. hash_release(nifp->cache_hash, c);
  62. XFREE(MTYPE_NHRP_CACHE, c);
  63. }
  64. struct nhrp_cache *nhrp_cache_get(struct interface *ifp, union sockunion *remote_addr, int create)
  65. {
  66. struct nhrp_interface *nifp = ifp->info;
  67. struct nhrp_cache key;
  68. if (!nifp->cache_hash) {
  69. nifp->cache_hash = hash_create(nhrp_cache_protocol_key, nhrp_cache_protocol_cmp);
  70. if (!nifp->cache_hash)
  71. return NULL;
  72. }
  73. key.remote_addr = *remote_addr;
  74. key.ifp = ifp;
  75. return hash_get(nifp->cache_hash, &key, create ? nhrp_cache_alloc : NULL);
  76. }
  77. static int nhrp_cache_do_free(struct thread *t)
  78. {
  79. struct nhrp_cache *c = THREAD_ARG(t);
  80. c->t_timeout = NULL;
  81. nhrp_cache_free(c);
  82. return 0;
  83. }
  84. static int nhrp_cache_do_timeout(struct thread *t)
  85. {
  86. struct nhrp_cache *c = THREAD_ARG(t);
  87. c->t_timeout = NULL;
  88. if (c->cur.type != NHRP_CACHE_INVALID)
  89. nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL);
  90. return 0;
  91. }
  92. static void nhrp_cache_update_route(struct nhrp_cache *c)
  93. {
  94. struct prefix pfx;
  95. struct nhrp_peer *p = c->cur.peer;
  96. sockunion2hostprefix(&c->remote_addr, &pfx);
  97. if (p && nhrp_peer_check(p, 1)) {
  98. netlink_update_binding(p->ifp, &c->remote_addr, &p->vc->remote.nbma);
  99. nhrp_route_announce(1, c->cur.type, &pfx, c->ifp, NULL, c->cur.mtu);
  100. if (c->cur.type >= NHRP_CACHE_DYNAMIC) {
  101. nhrp_route_update_nhrp(&pfx, c->ifp);
  102. c->nhrp_route_installed = 1;
  103. } else if (c->nhrp_route_installed) {
  104. nhrp_route_update_nhrp(&pfx, NULL);
  105. c->nhrp_route_installed = 0;
  106. }
  107. if (!c->route_installed) {
  108. notifier_call(&c->notifier_list, NOTIFY_CACHE_UP);
  109. c->route_installed = 1;
  110. }
  111. } else {
  112. if (c->nhrp_route_installed) {
  113. nhrp_route_update_nhrp(&pfx, NULL);
  114. c->nhrp_route_installed = 0;
  115. }
  116. if (c->route_installed) {
  117. sockunion2hostprefix(&c->remote_addr, &pfx);
  118. notifier_call(&c->notifier_list, NOTIFY_CACHE_DOWN);
  119. nhrp_route_announce(0, c->cur.type, &pfx, NULL, NULL, 0);
  120. c->route_installed = 0;
  121. }
  122. }
  123. }
  124. static void nhrp_cache_peer_notifier(struct notifier_block *n, unsigned long cmd)
  125. {
  126. struct nhrp_cache *c = container_of(n, struct nhrp_cache, peer_notifier);
  127. switch (cmd) {
  128. case NOTIFY_PEER_UP:
  129. nhrp_cache_update_route(c);
  130. break;
  131. case NOTIFY_PEER_DOWN:
  132. case NOTIFY_PEER_IFCONFIG_CHANGED:
  133. notifier_call(&c->notifier_list, NOTIFY_CACHE_DOWN);
  134. nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL);
  135. break;
  136. case NOTIFY_PEER_NBMA_CHANGING:
  137. if (c->cur.type == NHRP_CACHE_DYNAMIC)
  138. c->cur.peer->vc->abort_migration = 1;
  139. break;
  140. }
  141. }
  142. static void nhrp_cache_reset_new(struct nhrp_cache *c)
  143. {
  144. THREAD_OFF(c->t_auth);
  145. if (list_hashed(&c->newpeer_notifier.notifier_entry))
  146. nhrp_peer_notify_del(c->new.peer, &c->newpeer_notifier);
  147. nhrp_peer_unref(c->new.peer);
  148. memset(&c->new, 0, sizeof(c->new));
  149. c->new.type = NHRP_CACHE_INVALID;
  150. }
  151. static void nhrp_cache_update_timers(struct nhrp_cache *c)
  152. {
  153. THREAD_OFF(c->t_timeout);
  154. switch (c->cur.type) {
  155. case NHRP_CACHE_INVALID:
  156. if (!c->t_auth)
  157. THREAD_TIMER_MSEC_ON(master, c->t_timeout, nhrp_cache_do_free, c, 10);
  158. break;
  159. default:
  160. if (c->cur.expires)
  161. THREAD_TIMER_ON(master, c->t_timeout, nhrp_cache_do_timeout, c, c->cur.expires - recent_relative_time().tv_sec);
  162. break;
  163. }
  164. }
  165. static void nhrp_cache_authorize_binding(struct nhrp_reqid *r, void *arg)
  166. {
  167. struct nhrp_cache *c = container_of(r, struct nhrp_cache, eventid);
  168. char buf[SU_ADDRSTRLEN];
  169. debugf(NHRP_DEBUG_COMMON, "cache: %s %s: %s",
  170. c->ifp->name, sockunion2str(&c->remote_addr, buf, sizeof buf),
  171. (const char *) arg);
  172. nhrp_reqid_free(&nhrp_event_reqid, r);
  173. if (arg && strcmp(arg, "accept") == 0) {
  174. if (c->cur.peer) {
  175. netlink_update_binding(c->cur.peer->ifp, &c->remote_addr, NULL);
  176. nhrp_peer_notify_del(c->cur.peer, &c->peer_notifier);
  177. nhrp_peer_unref(c->cur.peer);
  178. }
  179. nhrp_cache_counts[c->cur.type]--;
  180. nhrp_cache_counts[c->new.type]++;
  181. c->cur = c->new;
  182. c->cur.peer = nhrp_peer_ref(c->cur.peer);
  183. nhrp_cache_reset_new(c);
  184. if (c->cur.peer)
  185. nhrp_peer_notify_add(c->cur.peer, &c->peer_notifier, nhrp_cache_peer_notifier);
  186. nhrp_cache_update_route(c);
  187. notifier_call(&c->notifier_list, NOTIFY_CACHE_BINDING_CHANGE);
  188. } else {
  189. nhrp_cache_reset_new(c);
  190. }
  191. nhrp_cache_update_timers(c);
  192. }
  193. static int nhrp_cache_do_auth_timeout(struct thread *t)
  194. {
  195. struct nhrp_cache *c = THREAD_ARG(t);
  196. c->t_auth = NULL;
  197. nhrp_cache_authorize_binding(&c->eventid, (void *) "timeout");
  198. return 0;
  199. }
  200. static void nhrp_cache_newpeer_notifier(struct notifier_block *n, unsigned long cmd)
  201. {
  202. struct nhrp_cache *c = container_of(n, struct nhrp_cache, newpeer_notifier);
  203. switch (cmd) {
  204. case NOTIFY_PEER_UP:
  205. if (nhrp_peer_check(c->new.peer, 1)) {
  206. evmgr_notify("authorize-binding", c, nhrp_cache_authorize_binding);
  207. THREAD_TIMER_ON(master, c->t_auth, nhrp_cache_do_auth_timeout, c, 10);
  208. }
  209. break;
  210. case NOTIFY_PEER_DOWN:
  211. case NOTIFY_PEER_IFCONFIG_CHANGED:
  212. nhrp_cache_reset_new(c);
  213. break;
  214. }
  215. }
  216. int nhrp_cache_update_binding(struct nhrp_cache *c, enum nhrp_cache_type type, int holding_time, struct nhrp_peer *p, uint32_t mtu, union sockunion *nbma_oa)
  217. {
  218. if (c->cur.type > type || c->new.type > type) {
  219. nhrp_peer_unref(p);
  220. return 0;
  221. }
  222. /* Sanitize MTU */
  223. switch (sockunion_family(&c->remote_addr)) {
  224. case AF_INET:
  225. if (mtu < 576 || mtu >= 1500)
  226. mtu = 0;
  227. /* Opennhrp announces nbma mtu, but we use protocol mtu.
  228. * This heuristic tries to fix up it. */
  229. if (mtu > 1420) mtu = (mtu & -16) - 80;
  230. break;
  231. default:
  232. mtu = 0;
  233. break;
  234. }
  235. nhrp_cache_reset_new(c);
  236. if (c->cur.type == type && c->cur.peer == p && c->cur.mtu == mtu) {
  237. if (holding_time > 0) c->cur.expires = recent_relative_time().tv_sec + holding_time;
  238. if (nbma_oa) c->cur.remote_nbma_natoa = *nbma_oa;
  239. else memset(&c->cur.remote_nbma_natoa, 0, sizeof c->cur.remote_nbma_natoa);
  240. nhrp_peer_unref(p);
  241. } else {
  242. c->new.type = type;
  243. c->new.peer = p;
  244. c->new.mtu = mtu;
  245. if (nbma_oa) c->new.remote_nbma_natoa = *nbma_oa;
  246. if (holding_time > 0)
  247. c->new.expires = recent_relative_time().tv_sec + holding_time;
  248. else if (holding_time < 0)
  249. c->new.type = NHRP_CACHE_INVALID;
  250. if (c->new.type == NHRP_CACHE_INVALID ||
  251. c->new.type >= NHRP_CACHE_STATIC ||
  252. c->map) {
  253. nhrp_cache_authorize_binding(&c->eventid, (void *) "accept");
  254. } else {
  255. nhrp_peer_notify_add(c->new.peer, &c->newpeer_notifier, nhrp_cache_newpeer_notifier);
  256. nhrp_cache_newpeer_notifier(&c->newpeer_notifier, NOTIFY_PEER_UP);
  257. THREAD_TIMER_ON(master, c->t_auth, nhrp_cache_do_auth_timeout, c, 60);
  258. }
  259. }
  260. nhrp_cache_update_timers(c);
  261. return 1;
  262. }
  263. void nhrp_cache_set_used(struct nhrp_cache *c, int used)
  264. {
  265. c->used = used;
  266. if (c->used)
  267. notifier_call(&c->notifier_list, NOTIFY_CACHE_USED);
  268. }
  269. struct nhrp_cache_iterator_ctx {
  270. void (*cb)(struct nhrp_cache *, void *);
  271. void *ctx;
  272. };
  273. static void nhrp_cache_iterator(struct hash_backet *b, void *ctx)
  274. {
  275. struct nhrp_cache_iterator_ctx *ic = ctx;
  276. ic->cb(b->data, ic->ctx);
  277. }
  278. void nhrp_cache_foreach(struct interface *ifp, void (*cb)(struct nhrp_cache *, void *), void *ctx)
  279. {
  280. struct nhrp_interface *nifp = ifp->info;
  281. struct nhrp_cache_iterator_ctx ic = {
  282. .cb = cb,
  283. .ctx = ctx,
  284. };
  285. if (nifp->cache_hash)
  286. hash_iterate(nifp->cache_hash, nhrp_cache_iterator, &ic);
  287. }
  288. void nhrp_cache_notify_add(struct nhrp_cache *c, struct notifier_block *n, notifier_fn_t fn)
  289. {
  290. notifier_add(n, &c->notifier_list, fn);
  291. }
  292. void nhrp_cache_notify_del(struct nhrp_cache *c, struct notifier_block *n)
  293. {
  294. notifier_del(n);
  295. }