bgp_lcommunity.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. /* BGP Large Communities Attribute
  2. Copyright (C) 2016 Keyur Patel <keyur@arrcus.com>
  3. This file is part of GNU Zebra.
  4. GNU Zebra is free software; you can redistribute it and/or modify it
  5. under the terms of the GNU General Public License as published by the
  6. Free Software Foundation; either version 2, or (at your option) any
  7. later version.
  8. GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the Free
  14. Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
  15. 02111-1307, USA. */
  16. #include <zebra.h>
  17. #include "hash.h"
  18. #include "memory.h"
  19. #include "prefix.h"
  20. #include "command.h"
  21. #include "filter.h"
  22. #include "bgpd/bgpd.h"
  23. #include "bgpd/bgp_lcommunity.h"
  24. #include "bgpd/bgp_aspath.h"
  25. /* Hash of community attribute. */
  26. static struct hash *lcomhash;
  27. /* Allocate a new lcommunities. */
  28. static struct lcommunity *
  29. lcommunity_new (void)
  30. {
  31. return (struct lcommunity *) XCALLOC (MTYPE_LCOMMUNITY,
  32. sizeof (struct lcommunity));
  33. }
  34. /* Allocate lcommunities. */
  35. void
  36. lcommunity_free (struct lcommunity **lcom)
  37. {
  38. if ((*lcom)->val)
  39. XFREE (MTYPE_LCOMMUNITY_VAL, (*lcom)->val);
  40. if ((*lcom)->str)
  41. XFREE (MTYPE_LCOMMUNITY_STR, (*lcom)->str);
  42. XFREE (MTYPE_LCOMMUNITY, *lcom);
  43. lcom = NULL;
  44. }
  45. /* Add a new Large Communities value to Large Communities
  46. Attribute structure. When the value is already exists in the
  47. structure, we don't add the value. Newly added value is sorted by
  48. numerical order. When the value is added to the structure return 1
  49. else return 0. */
  50. static int
  51. lcommunity_add_val (struct lcommunity *lcom, struct lcommunity_val *lval)
  52. {
  53. u_int8_t *p;
  54. int ret;
  55. int c;
  56. /* When this is fist value, just add it. */
  57. if (lcom->val == NULL)
  58. {
  59. lcom->size++;
  60. lcom->val = XMALLOC (MTYPE_LCOMMUNITY_VAL, lcom_length (lcom));
  61. memcpy (lcom->val, lval->val, LCOMMUNITY_SIZE);
  62. return 1;
  63. }
  64. /* If the value already exists in the structure return 0. */
  65. c = 0;
  66. for (p = lcom->val; c < lcom->size; p += LCOMMUNITY_SIZE, c++)
  67. {
  68. ret = memcmp (p, lval->val, LCOMMUNITY_SIZE);
  69. if (ret == 0)
  70. return 0;
  71. if (ret > 0)
  72. break;
  73. }
  74. /* Add the value to the structure with numerical sorting. */
  75. lcom->size++;
  76. lcom->val = XREALLOC (MTYPE_LCOMMUNITY_VAL, lcom->val, lcom_length (lcom));
  77. memmove (lcom->val + (c + 1) * LCOMMUNITY_SIZE,
  78. lcom->val + c * LCOMMUNITY_SIZE,
  79. (lcom->size - 1 - c) * LCOMMUNITY_SIZE);
  80. memcpy (lcom->val + c * LCOMMUNITY_SIZE, lval->val, LCOMMUNITY_SIZE);
  81. return 1;
  82. }
  83. /* This function takes pointer to Large Communites strucutre then
  84. create a new Large Communities structure by uniq and sort each
  85. Large Communities value. */
  86. struct lcommunity *
  87. lcommunity_uniq_sort (struct lcommunity *lcom)
  88. {
  89. int i;
  90. struct lcommunity *new;
  91. struct lcommunity_val *lval;
  92. if (! lcom)
  93. return NULL;
  94. new = lcommunity_new ();
  95. for (i = 0; i < lcom->size; i++)
  96. {
  97. lval = (struct lcommunity_val *) (lcom->val + (i * LCOMMUNITY_SIZE));
  98. lcommunity_add_val (new, lval);
  99. }
  100. return new;
  101. }
  102. /* Parse Large Communites Attribute in BGP packet. */
  103. struct lcommunity *
  104. lcommunity_parse (u_int8_t *pnt, u_short length)
  105. {
  106. struct lcommunity tmp;
  107. struct lcommunity *new;
  108. /* Length check. */
  109. if (length % LCOMMUNITY_SIZE)
  110. return NULL;
  111. /* Prepare tmporary structure for making a new Large Communities
  112. Attribute. */
  113. tmp.size = length / LCOMMUNITY_SIZE;
  114. tmp.val = pnt;
  115. /* Create a new Large Communities Attribute by uniq and sort each
  116. Large Communities value */
  117. new = lcommunity_uniq_sort (&tmp);
  118. return lcommunity_intern (new);
  119. }
  120. /* Duplicate the Large Communities Attribute structure. */
  121. struct lcommunity *
  122. lcommunity_dup (struct lcommunity *lcom)
  123. {
  124. struct lcommunity *new;
  125. new = XCALLOC (MTYPE_LCOMMUNITY, sizeof (struct lcommunity));
  126. new->size = lcom->size;
  127. if (new->size)
  128. {
  129. new->val = XMALLOC (MTYPE_LCOMMUNITY_VAL, lcom->size * LCOMMUNITY_SIZE);
  130. memcpy (new->val, lcom->val, lcom->size * LCOMMUNITY_SIZE);
  131. }
  132. else
  133. new->val = NULL;
  134. return new;
  135. }
  136. /* Retrun string representation of communities attribute. */
  137. char *
  138. lcommunity_str (struct lcommunity *lcom)
  139. {
  140. if (! lcom->str)
  141. lcom->str = lcommunity_lcom2str (lcom, LCOMMUNITY_FORMAT_DISPLAY);
  142. return lcom->str;
  143. }
  144. /* Merge two Large Communities Attribute structure. */
  145. struct lcommunity *
  146. lcommunity_merge (struct lcommunity *lcom1, struct lcommunity *lcom2)
  147. {
  148. if (lcom1->val)
  149. lcom1->val = XREALLOC (MTYPE_LCOMMUNITY_VAL, lcom1->val,
  150. (lcom1->size + lcom2->size) * LCOMMUNITY_SIZE);
  151. else
  152. lcom1->val = XMALLOC (MTYPE_LCOMMUNITY_VAL,
  153. (lcom1->size + lcom2->size) * LCOMMUNITY_SIZE);
  154. memcpy (lcom1->val + (lcom1->size * LCOMMUNITY_SIZE),
  155. lcom2->val, lcom2->size * LCOMMUNITY_SIZE);
  156. lcom1->size += lcom2->size;
  157. return lcom1;
  158. }
  159. /* Intern Large Communities Attribute. */
  160. struct lcommunity *
  161. lcommunity_intern (struct lcommunity *lcom)
  162. {
  163. struct lcommunity *find;
  164. assert (lcom->refcnt == 0);
  165. find = (struct lcommunity *) hash_get (lcomhash, lcom, hash_alloc_intern);
  166. if (find != lcom)
  167. lcommunity_free (&lcom);
  168. find->refcnt++;
  169. if (! find->str)
  170. find->str = lcommunity_lcom2str (find, LCOMMUNITY_FORMAT_DISPLAY);
  171. return find;
  172. }
  173. /* Unintern Large Communities Attribute. */
  174. void
  175. lcommunity_unintern (struct lcommunity **lcom)
  176. {
  177. struct lcommunity *ret;
  178. if ((*lcom)->refcnt)
  179. (*lcom)->refcnt--;
  180. /* Pull off from hash. */
  181. if ((*lcom)->refcnt == 0)
  182. {
  183. /* Large community must be in the hash. */
  184. ret = (struct lcommunity *) hash_release (lcomhash, *lcom);
  185. assert (ret != NULL);
  186. lcommunity_free (lcom);
  187. }
  188. }
  189. /* Utility function to make hash key. */
  190. unsigned int
  191. lcommunity_hash_make (void *arg)
  192. {
  193. const struct lcommunity *lcom = arg;
  194. int size = lcom->size * LCOMMUNITY_SIZE;
  195. u_int8_t *pnt = lcom->val;
  196. unsigned int key = 0;
  197. int c;
  198. for (c = 0; c < size; c += LCOMMUNITY_SIZE)
  199. {
  200. key += pnt[c];
  201. key += pnt[c + 1];
  202. key += pnt[c + 2];
  203. key += pnt[c + 3];
  204. key += pnt[c + 4];
  205. key += pnt[c + 5];
  206. key += pnt[c + 6];
  207. key += pnt[c + 7];
  208. key += pnt[c + 8];
  209. key += pnt[c + 9];
  210. key += pnt[c + 10];
  211. key += pnt[c + 11];
  212. }
  213. return key;
  214. }
  215. /* Compare two Large Communities Attribute structure. */
  216. int
  217. lcommunity_cmp (const void *arg1, const void *arg2)
  218. {
  219. const struct lcommunity *lcom1 = arg1;
  220. const struct lcommunity *lcom2 = arg2;
  221. return (lcom1->size == lcom2->size
  222. && memcmp (lcom1->val, lcom2->val, lcom1->size * LCOMMUNITY_SIZE) == 0);
  223. }
  224. /* Return communities hash. */
  225. struct hash *
  226. lcommunity_hash (void)
  227. {
  228. return lcomhash;
  229. }
  230. /* Initialize Large Comminities related hash. */
  231. void
  232. lcommunity_init (void)
  233. {
  234. lcomhash = hash_create (lcommunity_hash_make, lcommunity_cmp);
  235. }
  236. void
  237. lcommunity_finish (void)
  238. {
  239. hash_free (lcomhash);
  240. lcomhash = NULL;
  241. }
  242. /* Large Communities token enum. */
  243. enum lcommunity_token
  244. {
  245. lcommunity_token_unknown = 0,
  246. lcommunity_token_val,
  247. };
  248. /* Get next Large Communities token from the string. */
  249. static const char *
  250. lcommunity_gettoken (const char *str, struct lcommunity_val *lval,
  251. enum lcommunity_token *token)
  252. {
  253. const char *p = str;
  254. /* Skip white space. */
  255. while (isspace ((int) *p))
  256. {
  257. p++;
  258. str++;
  259. }
  260. /* Check the end of the line. */
  261. if (*p == '\0')
  262. return NULL;
  263. /* Community value. */
  264. if (isdigit ((int) *p))
  265. {
  266. int separator = 0;
  267. int digit = 0;
  268. u_int32_t globaladmin = 0;
  269. u_int32_t localdata1 = 0;
  270. u_int32_t localdata2 = 0;
  271. while (isdigit ((int) *p) || *p == ':')
  272. {
  273. if (*p == ':')
  274. {
  275. if (separator == 2)
  276. {
  277. *token = lcommunity_token_unknown;
  278. return NULL;
  279. }
  280. else
  281. {
  282. separator++;
  283. digit = 0;
  284. if (separator == 1) {
  285. globaladmin = localdata2;
  286. } else {
  287. localdata1 = localdata2;
  288. }
  289. localdata2 = 0;
  290. }
  291. }
  292. else
  293. {
  294. digit = 1;
  295. localdata2 *= 10;
  296. localdata2 += (*p - '0');
  297. }
  298. p++;
  299. }
  300. if (! digit)
  301. {
  302. *token = lcommunity_token_unknown;
  303. return NULL;
  304. }
  305. /*
  306. * Copy the large comm.
  307. */
  308. lval->val[0] = (globaladmin >> 24) & 0xff;
  309. lval->val[1] = (globaladmin >> 16) & 0xff;
  310. lval->val[2] = (globaladmin >> 8) & 0xff;
  311. lval->val[3] = globaladmin & 0xff;
  312. lval->val[4] = (localdata1 >> 24) & 0xff;
  313. lval->val[5] = (localdata1 >> 16) & 0xff;
  314. lval->val[6] = (localdata1 >> 8) & 0xff;
  315. lval->val[7] = localdata1 & 0xff;
  316. lval->val[8] = (localdata2 >> 24) & 0xff;
  317. lval->val[9] = (localdata2 >> 16) & 0xff;
  318. lval->val[10] = (localdata2 >> 8) & 0xff;
  319. lval->val[11] = localdata2 & 0xff;
  320. *token = lcommunity_token_val;
  321. return p;
  322. }
  323. *token = lcommunity_token_unknown;
  324. return p;
  325. }
  326. /*
  327. Convert string to large community attribute.
  328. When type is already known, please specify both str and type.
  329. When string includes keyword for each large community value.
  330. Please specify keyword_included as non-zero value.
  331. */
  332. struct lcommunity *
  333. lcommunity_str2com (const char *str)
  334. {
  335. struct lcommunity *lcom = NULL;
  336. enum lcommunity_token token = lcommunity_token_unknown;
  337. struct lcommunity_val lval;
  338. while ((str = lcommunity_gettoken (str, &lval, &token)))
  339. {
  340. switch (token)
  341. {
  342. case lcommunity_token_val:
  343. if (lcom == NULL)
  344. lcom = lcommunity_new ();
  345. lcommunity_add_val (lcom, &lval);
  346. break;
  347. case lcommunity_token_unknown:
  348. default:
  349. if (lcom)
  350. lcommunity_free (&lcom);
  351. return NULL;
  352. }
  353. }
  354. return lcom;
  355. }
  356. int
  357. lcommunity_include (struct lcommunity *lcom, u_char *ptr)
  358. {
  359. int i;
  360. u_char *lcom_ptr;
  361. for (i = 0; i < lcom->size; i++) {
  362. lcom_ptr = lcom->val + (i * LCOMMUNITY_SIZE);
  363. if (memcmp (ptr, lcom_ptr, LCOMMUNITY_SIZE) == 0)
  364. return 1;
  365. }
  366. return 0;
  367. }
  368. /* Convert large community attribute to string.
  369. The large coms will be in 65535:65531:0 format.
  370. */
  371. char *
  372. lcommunity_lcom2str (struct lcommunity *lcom, int format)
  373. {
  374. int i;
  375. u_int8_t *pnt;
  376. #define LCOMMUNITY_STR_DEFAULT_LEN 40
  377. int str_size;
  378. int str_pnt;
  379. char *str_buf;
  380. int len = 0;
  381. int first = 1;
  382. u_int32_t globaladmin, localdata1, localdata2;
  383. if (lcom->size == 0)
  384. {
  385. str_buf = XMALLOC (MTYPE_LCOMMUNITY_STR, 1);
  386. str_buf[0] = '\0';
  387. return str_buf;
  388. }
  389. /* Prepare buffer. */
  390. str_buf = XMALLOC (MTYPE_LCOMMUNITY_STR, LCOMMUNITY_STR_DEFAULT_LEN + 1);
  391. str_size = LCOMMUNITY_STR_DEFAULT_LEN + 1;
  392. str_pnt = 0;
  393. for (i = 0; i < lcom->size; i++)
  394. {
  395. /* Make it sure size is enough. */
  396. while (str_pnt + LCOMMUNITY_STR_DEFAULT_LEN >= str_size)
  397. {
  398. str_size *= 2;
  399. str_buf = XREALLOC (MTYPE_LCOMMUNITY_STR, str_buf, str_size);
  400. }
  401. /* Space between each value. */
  402. if (! first)
  403. str_buf[str_pnt++] = ' ';
  404. pnt = lcom->val + (i * 12);
  405. globaladmin = (*pnt++ << 24);
  406. globaladmin |= (*pnt++ << 16);
  407. globaladmin |= (*pnt++ << 8);
  408. globaladmin |= (*pnt++);
  409. localdata1 = (*pnt++ << 24);
  410. localdata1 |= (*pnt++ << 16);
  411. localdata1 |= (*pnt++ << 8);
  412. localdata1 |= (*pnt++);
  413. localdata2 = (*pnt++ << 24);
  414. localdata2 |= (*pnt++ << 16);
  415. localdata2 |= (*pnt++ << 8);
  416. localdata2 |= (*pnt++);
  417. len = sprintf( str_buf + str_pnt, "%u:%u:%u", globaladmin,
  418. localdata1, localdata2);
  419. str_pnt += len;
  420. first = 0;
  421. }
  422. return str_buf;
  423. }
  424. int
  425. lcommunity_match (const struct lcommunity *lcom1,
  426. const struct lcommunity *lcom2)
  427. {
  428. int i = 0;
  429. int j = 0;
  430. if (lcom1 == NULL && lcom2 == NULL)
  431. return 1;
  432. if (lcom1 == NULL || lcom2 == NULL)
  433. return 0;
  434. if (lcom1->size < lcom2->size)
  435. return 0;
  436. /* Every community on com2 needs to be on com1 for this to match */
  437. while (i < lcom1->size && j < lcom2->size)
  438. {
  439. if (memcmp (lcom1->val + (i*12), lcom2->val + (j*12), LCOMMUNITY_SIZE) == 0)
  440. j++;
  441. i++;
  442. }
  443. if (j == lcom2->size)
  444. return 1;
  445. else
  446. return 0;
  447. }
  448. /* Delete one lcommunity. */
  449. void
  450. lcommunity_del_val (struct lcommunity *lcom, u_char *ptr)
  451. {
  452. int i = 0;
  453. int c = 0;
  454. if (! lcom->val)
  455. return;
  456. while (i < lcom->size)
  457. {
  458. if (memcmp (lcom->val + i*LCOMMUNITY_SIZE, ptr, LCOMMUNITY_SIZE) == 0)
  459. {
  460. c = lcom->size -i -1;
  461. if (c > 0)
  462. memmove (lcom->val + i*LCOMMUNITY_SIZE, lcom->val + (i + 1)*LCOMMUNITY_SIZE, c * LCOMMUNITY_SIZE);
  463. lcom->size--;
  464. if (lcom->size > 0)
  465. lcom->val = XREALLOC (MTYPE_COMMUNITY_VAL, lcom->val,
  466. lcom_length (lcom));
  467. else
  468. {
  469. XFREE (MTYPE_COMMUNITY_VAL, lcom->val);
  470. lcom->val = NULL;
  471. }
  472. return;
  473. }
  474. i++;
  475. }
  476. }