pim_hello.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. /*
  2. PIM for Quagga
  3. Copyright (C) 2008 Everton da Silva Marques
  4. This program is free software; you can redistribute it 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. This program is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; see the file COPYING; if not, write to the
  14. Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
  15. MA 02110-1301 USA
  16. $QuaggaId: $Format:%an, %ai, %h$ $
  17. */
  18. #include <zebra.h>
  19. #include "log.h"
  20. #include "pimd.h"
  21. #include "pim_pim.h"
  22. #include "pim_str.h"
  23. #include "pim_tlv.h"
  24. #include "pim_util.h"
  25. #include "pim_hello.h"
  26. #include "pim_iface.h"
  27. #include "pim_neighbor.h"
  28. #include "pim_upstream.h"
  29. static void on_trace(const char *label,
  30. struct interface *ifp, struct in_addr src)
  31. {
  32. if (PIM_DEBUG_PIM_TRACE) {
  33. char src_str[100];
  34. pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
  35. zlog_debug("%s: from %s on %s",
  36. label, src_str, ifp->name);
  37. }
  38. }
  39. static void tlv_trace_bool(const char *label, const char *tlv_name,
  40. const char *ifname, struct in_addr src_addr,
  41. int isset, int value)
  42. {
  43. if (isset) {
  44. char src_str[100];
  45. pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
  46. zlog_debug("%s: PIM hello option from %s on interface %s: %s=%d",
  47. label,
  48. src_str, ifname,
  49. tlv_name, value);
  50. }
  51. }
  52. static void tlv_trace_uint16(const char *label, const char *tlv_name,
  53. const char *ifname, struct in_addr src_addr,
  54. int isset, uint16_t value)
  55. {
  56. if (isset) {
  57. char src_str[100];
  58. pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
  59. zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u",
  60. label,
  61. src_str, ifname,
  62. tlv_name, value);
  63. }
  64. }
  65. static void tlv_trace_uint32(const char *label, const char *tlv_name,
  66. const char *ifname, struct in_addr src_addr,
  67. int isset, uint32_t value)
  68. {
  69. if (isset) {
  70. char src_str[100];
  71. pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
  72. zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u",
  73. label,
  74. src_str, ifname,
  75. tlv_name, value);
  76. }
  77. }
  78. static void tlv_trace_uint32_hex(const char *label, const char *tlv_name,
  79. const char *ifname, struct in_addr src_addr,
  80. int isset, uint32_t value)
  81. {
  82. if (isset) {
  83. char src_str[100];
  84. pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
  85. zlog_debug("%s: PIM hello option from %s on interface %s: %s=%08x",
  86. label,
  87. src_str, ifname,
  88. tlv_name, value);
  89. }
  90. }
  91. #if 0
  92. static void tlv_trace(const char *label, const char *tlv_name,
  93. const char *ifname, struct in_addr src_addr,
  94. int isset)
  95. {
  96. if (isset) {
  97. char src_str[100];
  98. pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
  99. zlog_debug("%s: PIM hello option from %s on interface %s: %s",
  100. label,
  101. src_str, ifname,
  102. tlv_name);
  103. }
  104. }
  105. #endif
  106. static void tlv_trace_list(const char *label, const char *tlv_name,
  107. const char *ifname, struct in_addr src_addr,
  108. int isset, struct list *addr_list)
  109. {
  110. if (isset) {
  111. char src_str[100];
  112. pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
  113. zlog_debug("%s: PIM hello option from %s on interface %s: %s size=%d list=%x",
  114. label,
  115. src_str, ifname,
  116. tlv_name,
  117. addr_list ? ((int) listcount(addr_list)) : -1,
  118. (unsigned) addr_list);
  119. }
  120. }
  121. #define FREE_ADDR_LIST \
  122. if (hello_option_addr_list) { \
  123. list_delete(hello_option_addr_list); \
  124. }
  125. #define FREE_ADDR_LIST_THEN_RETURN(code) \
  126. { \
  127. FREE_ADDR_LIST \
  128. return (code); \
  129. }
  130. int pim_hello_recv(struct interface *ifp,
  131. struct in_addr src_addr,
  132. char *tlv_buf, int tlv_buf_size)
  133. {
  134. struct pim_interface *pim_ifp;
  135. struct pim_neighbor *neigh;
  136. char *tlv_curr;
  137. char *tlv_pastend;
  138. pim_hello_options hello_options = 0; /* bit array recording options found */
  139. uint16_t hello_option_holdtime = 0;
  140. uint16_t hello_option_propagation_delay = 0;
  141. uint16_t hello_option_override_interval = 0;
  142. uint32_t hello_option_dr_priority = 0;
  143. uint32_t hello_option_generation_id = 0;
  144. struct list *hello_option_addr_list = 0;
  145. on_trace(__PRETTY_FUNCTION__, ifp, src_addr);
  146. pim_ifp = ifp->info;
  147. zassert(pim_ifp);
  148. ++pim_ifp->pim_ifstat_hello_recv;
  149. /*
  150. Parse PIM hello TLVs
  151. */
  152. zassert(tlv_buf_size >= 0);
  153. tlv_curr = tlv_buf;
  154. tlv_pastend = tlv_buf + tlv_buf_size;
  155. while (tlv_curr < tlv_pastend) {
  156. uint16_t option_type;
  157. uint16_t option_len;
  158. int remain = tlv_pastend - tlv_curr;
  159. if (remain < PIM_TLV_MIN_SIZE) {
  160. char src_str[100];
  161. pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
  162. zlog_warn("%s: short PIM hello TLV size=%d < min=%d from %s on interface %s",
  163. __PRETTY_FUNCTION__,
  164. remain, PIM_TLV_MIN_SIZE,
  165. src_str, ifp->name);
  166. FREE_ADDR_LIST_THEN_RETURN(-1);
  167. }
  168. option_type = PIM_TLV_GET_TYPE(tlv_curr);
  169. tlv_curr += PIM_TLV_TYPE_SIZE;
  170. option_len = PIM_TLV_GET_LENGTH(tlv_curr);
  171. tlv_curr += PIM_TLV_LENGTH_SIZE;
  172. if ((tlv_curr + option_len) > tlv_pastend) {
  173. char src_str[100];
  174. pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
  175. zlog_warn("%s: long PIM hello TLV type=%d length=%d > max=%d from %s on interface %s",
  176. __PRETTY_FUNCTION__,
  177. option_type, option_len, tlv_pastend - tlv_curr,
  178. src_str, ifp->name);
  179. FREE_ADDR_LIST_THEN_RETURN(-2);
  180. }
  181. if (PIM_DEBUG_PIM_TRACE) {
  182. char src_str[100];
  183. pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
  184. zlog_debug("%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %s on %s",
  185. __PRETTY_FUNCTION__,
  186. remain,
  187. option_type, option_len,
  188. src_str, ifp->name);
  189. }
  190. switch (option_type) {
  191. case PIM_MSG_OPTION_TYPE_HOLDTIME:
  192. if (pim_tlv_parse_holdtime(ifp->name, src_addr,
  193. &hello_options,
  194. &hello_option_holdtime,
  195. option_len,
  196. tlv_curr)) {
  197. FREE_ADDR_LIST_THEN_RETURN(-3);
  198. }
  199. break;
  200. case PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY:
  201. if (pim_tlv_parse_lan_prune_delay(ifp->name, src_addr,
  202. &hello_options,
  203. &hello_option_propagation_delay,
  204. &hello_option_override_interval,
  205. option_len,
  206. tlv_curr)) {
  207. FREE_ADDR_LIST_THEN_RETURN(-4);
  208. }
  209. break;
  210. case PIM_MSG_OPTION_TYPE_DR_PRIORITY:
  211. if (pim_tlv_parse_dr_priority(ifp->name, src_addr,
  212. &hello_options,
  213. &hello_option_dr_priority,
  214. option_len,
  215. tlv_curr)) {
  216. FREE_ADDR_LIST_THEN_RETURN(-5);
  217. }
  218. break;
  219. case PIM_MSG_OPTION_TYPE_GENERATION_ID:
  220. if (pim_tlv_parse_generation_id(ifp->name, src_addr,
  221. &hello_options,
  222. &hello_option_generation_id,
  223. option_len,
  224. tlv_curr)) {
  225. FREE_ADDR_LIST_THEN_RETURN(-6);
  226. }
  227. break;
  228. case PIM_MSG_OPTION_TYPE_ADDRESS_LIST:
  229. if (pim_tlv_parse_addr_list(ifp->name, src_addr,
  230. &hello_options,
  231. &hello_option_addr_list,
  232. option_len,
  233. tlv_curr)) {
  234. return -7;
  235. }
  236. break;
  237. case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH:
  238. if (PIM_DEBUG_PIM_TRACE) {
  239. char src_str[100];
  240. pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
  241. zlog_debug("%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %s on interface %s",
  242. __PRETTY_FUNCTION__,
  243. option_type, option_len,
  244. src_str, ifp->name);
  245. }
  246. break;
  247. default:
  248. {
  249. char src_str[100];
  250. pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
  251. zlog_warn("%s: ignoring unknown PIM hello TLV type=%d length=%d from %s on interface %s",
  252. __PRETTY_FUNCTION__,
  253. option_type, option_len,
  254. src_str, ifp->name);
  255. }
  256. }
  257. tlv_curr += option_len;
  258. }
  259. /*
  260. Check received PIM hello options
  261. */
  262. if (PIM_DEBUG_PIM_TRACE) {
  263. tlv_trace_uint16(__PRETTY_FUNCTION__, "holdtime",
  264. ifp->name, src_addr,
  265. PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME),
  266. hello_option_holdtime);
  267. tlv_trace_uint16(__PRETTY_FUNCTION__, "propagation_delay",
  268. ifp->name, src_addr,
  269. PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
  270. hello_option_propagation_delay);
  271. tlv_trace_uint16(__PRETTY_FUNCTION__, "override_interval",
  272. ifp->name, src_addr,
  273. PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
  274. hello_option_override_interval);
  275. tlv_trace_bool(__PRETTY_FUNCTION__, "can_disable_join_suppression",
  276. ifp->name, src_addr,
  277. PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
  278. PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION));
  279. tlv_trace_uint32(__PRETTY_FUNCTION__, "dr_priority",
  280. ifp->name, src_addr,
  281. PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_DR_PRIORITY),
  282. hello_option_dr_priority);
  283. tlv_trace_uint32_hex(__PRETTY_FUNCTION__, "generation_id",
  284. ifp->name, src_addr,
  285. PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID),
  286. hello_option_generation_id);
  287. tlv_trace_list(__PRETTY_FUNCTION__, "address_list",
  288. ifp->name, src_addr,
  289. PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_ADDRESS_LIST),
  290. hello_option_addr_list);
  291. }
  292. if (!PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) {
  293. char src_str[100];
  294. pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
  295. zlog_warn("%s: PIM hello missing holdtime from %s on interface %s",
  296. __PRETTY_FUNCTION__,
  297. src_str, ifp->name);
  298. }
  299. /*
  300. New neighbor?
  301. */
  302. neigh = pim_neighbor_find(ifp, src_addr);
  303. if (!neigh) {
  304. /* Add as new neighbor */
  305. neigh = pim_neighbor_add(ifp, src_addr,
  306. hello_options,
  307. hello_option_holdtime,
  308. hello_option_propagation_delay,
  309. hello_option_override_interval,
  310. hello_option_dr_priority,
  311. hello_option_generation_id,
  312. hello_option_addr_list);
  313. if (!neigh) {
  314. char src_str[100];
  315. pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
  316. zlog_warn("%s: failure creating PIM neighbor %s on interface %s",
  317. __PRETTY_FUNCTION__,
  318. src_str, ifp->name);
  319. FREE_ADDR_LIST_THEN_RETURN(-8);
  320. }
  321. /* actual addr list has been saved under neighbor */
  322. return 0;
  323. }
  324. /*
  325. Received generation ID ?
  326. */
  327. if (PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID)) {
  328. /* GenID mismatch ? */
  329. if (!PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_GENERATION_ID) ||
  330. (hello_option_generation_id != neigh->generation_id)) {
  331. /* GenID changed */
  332. pim_upstream_rpf_genid_changed(neigh->source_addr);
  333. /* GenID mismatch, then replace neighbor */
  334. if (PIM_DEBUG_PIM_TRACE) {
  335. char src_str[100];
  336. pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
  337. zlog_debug("%s: GenId mismatch new=%08x old=%08x: replacing neighbor %s on %s",
  338. __PRETTY_FUNCTION__,
  339. hello_option_generation_id,
  340. neigh->generation_id,
  341. src_str, ifp->name);
  342. }
  343. pim_upstream_rpf_genid_changed(neigh->source_addr);
  344. pim_neighbor_delete(ifp, neigh, "GenID mismatch");
  345. neigh = pim_neighbor_add(ifp, src_addr,
  346. hello_options,
  347. hello_option_holdtime,
  348. hello_option_propagation_delay,
  349. hello_option_override_interval,
  350. hello_option_dr_priority,
  351. hello_option_generation_id,
  352. hello_option_addr_list);
  353. if (!neigh) {
  354. char src_str[100];
  355. pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
  356. zlog_warn("%s: failure re-creating PIM neighbor %s on interface %s",
  357. __PRETTY_FUNCTION__,
  358. src_str, ifp->name);
  359. FREE_ADDR_LIST_THEN_RETURN(-9);
  360. }
  361. /* actual addr list is saved under neighbor */
  362. return 0;
  363. } /* GenId mismatch: replace neighbor */
  364. } /* GenId received */
  365. /*
  366. Update existing neighbor
  367. */
  368. pim_neighbor_update(neigh,
  369. hello_options,
  370. hello_option_holdtime,
  371. hello_option_dr_priority,
  372. hello_option_addr_list);
  373. /* actual addr list is saved under neighbor */
  374. return 0;
  375. }
  376. int pim_hello_build_tlv(const char *ifname,
  377. char *tlv_buf, int tlv_buf_size,
  378. uint16_t holdtime,
  379. uint32_t dr_priority,
  380. uint32_t generation_id,
  381. uint16_t propagation_delay,
  382. uint16_t override_interval,
  383. int can_disable_join_suppression,
  384. struct list *ifconnected)
  385. {
  386. char *curr = tlv_buf;
  387. char *pastend = tlv_buf + tlv_buf_size;
  388. char *tmp;
  389. /*
  390. * Append options
  391. */
  392. /* Holdtime */
  393. curr = pim_tlv_append_uint16(curr,
  394. pastend,
  395. PIM_MSG_OPTION_TYPE_HOLDTIME,
  396. holdtime);
  397. if (!curr) {
  398. zlog_warn("%s: could not set PIM hello Holdtime option for interface %s",
  399. __PRETTY_FUNCTION__, ifname);
  400. return -1;
  401. }
  402. /* LAN Prune Delay */
  403. tmp = pim_tlv_append_2uint16(curr,
  404. pastend,
  405. PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY,
  406. propagation_delay,
  407. override_interval);
  408. if (!tmp) {
  409. zlog_warn("%s: could not set PIM LAN Prune Delay option for interface %s",
  410. __PRETTY_FUNCTION__, ifname);
  411. return -1;
  412. }
  413. if (can_disable_join_suppression) {
  414. *((uint8_t*)(curr) + 4) |= 0x80; /* enable T bit */
  415. }
  416. curr = tmp;
  417. /* DR Priority */
  418. curr = pim_tlv_append_uint32(curr,
  419. pastend,
  420. PIM_MSG_OPTION_TYPE_DR_PRIORITY,
  421. dr_priority);
  422. if (!curr) {
  423. zlog_warn("%s: could not set PIM hello DR Priority option for interface %s",
  424. __PRETTY_FUNCTION__, ifname);
  425. return -2;
  426. }
  427. /* Generation ID */
  428. curr = pim_tlv_append_uint32(curr,
  429. pastend,
  430. PIM_MSG_OPTION_TYPE_GENERATION_ID,
  431. generation_id);
  432. if (!curr) {
  433. zlog_warn("%s: could not set PIM hello Generation ID option for interface %s",
  434. __PRETTY_FUNCTION__, ifname);
  435. return -3;
  436. }
  437. /* Secondary Address List */
  438. if (ifconnected) {
  439. curr = pim_tlv_append_addrlist_ucast(curr,
  440. pastend,
  441. ifconnected);
  442. if (!curr) {
  443. zlog_warn("%s: could not set PIM hello Secondary Address List option for interface %s",
  444. __PRETTY_FUNCTION__, ifname);
  445. return -4;
  446. }
  447. }
  448. return curr - tlv_buf;
  449. }
  450. /*
  451. RFC 4601: 4.3.1. Sending Hello Messages
  452. Thus, if a router needs to send a Join/Prune or Assert message on an
  453. interface on which it has not yet sent a Hello message with the
  454. currently configured IP address, then it MUST immediately send the
  455. relevant Hello message without waiting for the Hello Timer to
  456. expire, followed by the Join/Prune or Assert message.
  457. */
  458. void pim_hello_require(struct interface *ifp)
  459. {
  460. struct pim_interface *pim_ifp;
  461. zassert(ifp);
  462. pim_ifp = ifp->info;
  463. zassert(pim_ifp);
  464. if (pim_ifp->pim_ifstat_hello_sent)
  465. return;
  466. pim_hello_restart_now(ifp); /* Send hello and restart timer */
  467. }