Annotation of embedaddon/quagga/bgpd/bgp_mpath.c, revision 1.1.1.2
1.1 misho 1: /* $QuaggaId: Format:%an, %ai, %h$ $
2: *
3: * BGP Multipath
4: * Copyright (C) 2010 Google Inc.
5: *
6: * This file is part of Quagga
7: *
8: * Quagga is free software; you can redistribute it and/or modify it
9: * under the terms of the GNU General Public License as published by the
10: * Free Software Foundation; either version 2, or (at your option) any
11: * later version.
12: *
13: * Quagga is distributed in the hope that it will be useful, but
14: * WITHOUT ANY WARRANTY; without even the implied warranty of
15: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16: * General Public License for more details.
17: *
18: * You should have received a copy of the GNU General Public License
19: * along with Quagga; see the file COPYING. If not, write to the Free
20: * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21: * 02111-1307, USA.
22: */
23:
24: #include <zebra.h>
25:
26: #include "command.h"
27: #include "prefix.h"
28: #include "linklist.h"
29: #include "sockunion.h"
30: #include "memory.h"
31:
32: #include "bgpd/bgpd.h"
33: #include "bgpd/bgp_table.h"
34: #include "bgpd/bgp_route.h"
35: #include "bgpd/bgp_attr.h"
36: #include "bgpd/bgp_debug.h"
37: #include "bgpd/bgp_aspath.h"
38: #include "bgpd/bgp_community.h"
39: #include "bgpd/bgp_ecommunity.h"
40: #include "bgpd/bgp_mpath.h"
41:
42: /*
43: * bgp_maximum_paths_set
44: *
45: * Record maximum-paths configuration for BGP instance
46: */
47: int
48: bgp_maximum_paths_set (struct bgp *bgp, afi_t afi, safi_t safi,
49: int peertype, u_int16_t maxpaths)
50: {
51: if (!bgp || (afi >= AFI_MAX) || (safi >= SAFI_MAX))
52: return -1;
53:
54: switch (peertype)
55: {
56: case BGP_PEER_IBGP:
57: bgp->maxpaths[afi][safi].maxpaths_ibgp = maxpaths;
58: break;
59: case BGP_PEER_EBGP:
60: bgp->maxpaths[afi][safi].maxpaths_ebgp = maxpaths;
61: break;
62: default:
63: return -1;
64: }
65:
66: return 0;
67: }
68:
69: /*
70: * bgp_maximum_paths_unset
71: *
72: * Remove maximum-paths configuration from BGP instance
73: */
74: int
75: bgp_maximum_paths_unset (struct bgp *bgp, afi_t afi, safi_t safi,
76: int peertype)
77: {
78: if (!bgp || (afi >= AFI_MAX) || (safi >= SAFI_MAX))
79: return -1;
80:
81: switch (peertype)
82: {
83: case BGP_PEER_IBGP:
84: bgp->maxpaths[afi][safi].maxpaths_ibgp = BGP_DEFAULT_MAXPATHS;
85: break;
86: case BGP_PEER_EBGP:
87: bgp->maxpaths[afi][safi].maxpaths_ebgp = BGP_DEFAULT_MAXPATHS;
88: break;
89: default:
90: return -1;
91: }
92:
93: return 0;
94: }
95:
96: /*
97: * bgp_info_nexthop_cmp
98: *
99: * Compare the nexthops of two paths. Return value is less than, equal to,
100: * or greater than zero if bi1 is respectively less than, equal to,
101: * or greater than bi2.
102: */
103: static int
104: bgp_info_nexthop_cmp (struct bgp_info *bi1, struct bgp_info *bi2)
105: {
106: struct attr_extra *ae1, *ae2;
107: int compare;
108:
109: ae1 = bi1->attr->extra;
110: ae2 = bi2->attr->extra;
111:
112: compare = IPV4_ADDR_CMP (&bi1->attr->nexthop, &bi2->attr->nexthop);
113:
114: if (!compare && ae1 && ae2 && (ae1->mp_nexthop_len == ae2->mp_nexthop_len))
115: {
116: switch (ae1->mp_nexthop_len)
117: {
118: case 4:
119: case 12:
120: compare = IPV4_ADDR_CMP (&ae1->mp_nexthop_global_in,
121: &ae2->mp_nexthop_global_in);
122: break;
123: #ifdef HAVE_IPV6
124: case 16:
125: compare = IPV6_ADDR_CMP (&ae1->mp_nexthop_global,
126: &ae2->mp_nexthop_global);
127: break;
128: case 32:
129: compare = IPV6_ADDR_CMP (&ae1->mp_nexthop_global,
130: &ae2->mp_nexthop_global);
131: if (!compare)
132: compare = IPV6_ADDR_CMP (&ae1->mp_nexthop_local,
133: &ae2->mp_nexthop_local);
134: break;
135: #endif /* HAVE_IPV6 */
136: }
137: }
138:
139: return compare;
140: }
141:
142: /*
143: * bgp_info_mpath_cmp
144: *
145: * This function determines our multipath list ordering. By ordering
146: * the list we can deterministically select which paths are included
147: * in the multipath set. The ordering also helps in detecting changes
148: * in the multipath selection so we can detect whether to send an
149: * update to zebra.
150: *
151: * The order of paths is determined first by received nexthop, and then
152: * by peer address if the nexthops are the same.
153: */
154: static int
155: bgp_info_mpath_cmp (void *val1, void *val2)
156: {
157: struct bgp_info *bi1, *bi2;
158: int compare;
159:
160: bi1 = val1;
161: bi2 = val2;
162:
163: compare = bgp_info_nexthop_cmp (bi1, bi2);
164:
165: if (!compare)
166: compare = sockunion_cmp (bi1->peer->su_remote, bi2->peer->su_remote);
167:
168: return compare;
169: }
170:
171: /*
172: * bgp_mp_list_init
173: *
174: * Initialize the mp_list, which holds the list of multipaths
175: * selected by bgp_best_selection
176: */
177: void
178: bgp_mp_list_init (struct list *mp_list)
179: {
180: assert (mp_list);
181: memset (mp_list, 0, sizeof (struct list));
182: mp_list->cmp = bgp_info_mpath_cmp;
183: }
184:
185: /*
186: * bgp_mp_list_clear
187: *
188: * Clears all entries out of the mp_list
189: */
190: void
191: bgp_mp_list_clear (struct list *mp_list)
192: {
193: assert (mp_list);
194: list_delete_all_node (mp_list);
195: }
196:
197: /*
198: * bgp_mp_list_add
199: *
200: * Adds a multipath entry to the mp_list
201: */
202: void
203: bgp_mp_list_add (struct list *mp_list, struct bgp_info *mpinfo)
204: {
205: assert (mp_list && mpinfo);
206: listnode_add_sort (mp_list, mpinfo);
207: }
208:
209: /*
210: * bgp_info_mpath_new
211: *
212: * Allocate and zero memory for a new bgp_info_mpath element
213: */
214: static struct bgp_info_mpath *
215: bgp_info_mpath_new (void)
216: {
217: struct bgp_info_mpath *new_mpath;
218: new_mpath = XCALLOC (MTYPE_BGP_MPATH_INFO, sizeof (struct bgp_info_mpath));
219: return new_mpath;
220: }
221:
222: /*
223: * bgp_info_mpath_free
224: *
225: * Release resources for a bgp_info_mpath element and zero out pointer
226: */
227: void
228: bgp_info_mpath_free (struct bgp_info_mpath **mpath)
229: {
230: if (mpath && *mpath)
231: {
232: if ((*mpath)->mp_attr)
233: bgp_attr_unintern (&(*mpath)->mp_attr);
234: XFREE (MTYPE_BGP_MPATH_INFO, *mpath);
235: *mpath = NULL;
236: }
237: }
238:
239: /*
240: * bgp_info_mpath_get
241: *
242: * Fetch the mpath element for the given bgp_info. Used for
243: * doing lazy allocation.
244: */
245: static struct bgp_info_mpath *
246: bgp_info_mpath_get (struct bgp_info *binfo)
247: {
248: struct bgp_info_mpath *mpath;
249: if (!binfo->mpath)
250: {
251: mpath = bgp_info_mpath_new();
252: if (!mpath)
253: return NULL;
254: binfo->mpath = mpath;
255: mpath->mp_info = binfo;
256: }
257: return binfo->mpath;
258: }
259:
260: /*
261: * bgp_info_mpath_enqueue
262: *
263: * Enqueue a path onto the multipath list given the previous multipath
264: * list entry
265: */
266: static void
267: bgp_info_mpath_enqueue (struct bgp_info *prev_info, struct bgp_info *binfo)
268: {
269: struct bgp_info_mpath *prev, *mpath;
270:
271: prev = bgp_info_mpath_get (prev_info);
272: mpath = bgp_info_mpath_get (binfo);
273: if (!prev || !mpath)
274: return;
275:
276: mpath->mp_next = prev->mp_next;
277: mpath->mp_prev = prev;
278: if (prev->mp_next)
279: prev->mp_next->mp_prev = mpath;
280: prev->mp_next = mpath;
281:
282: SET_FLAG (binfo->flags, BGP_INFO_MULTIPATH);
283: }
284:
285: /*
286: * bgp_info_mpath_dequeue
287: *
288: * Remove a path from the multipath list
289: */
290: void
291: bgp_info_mpath_dequeue (struct bgp_info *binfo)
292: {
293: struct bgp_info_mpath *mpath = binfo->mpath;
294: if (!mpath)
295: return;
296: if (mpath->mp_prev)
297: mpath->mp_prev->mp_next = mpath->mp_next;
298: if (mpath->mp_next)
299: mpath->mp_next->mp_prev = mpath->mp_prev;
300: mpath->mp_next = mpath->mp_prev = NULL;
301: UNSET_FLAG (binfo->flags, BGP_INFO_MULTIPATH);
302: }
303:
304: /*
305: * bgp_info_mpath_next
306: *
307: * Given a bgp_info, return the next multipath entry
308: */
309: struct bgp_info *
310: bgp_info_mpath_next (struct bgp_info *binfo)
311: {
312: if (!binfo->mpath || !binfo->mpath->mp_next)
313: return NULL;
314: return binfo->mpath->mp_next->mp_info;
315: }
316:
317: /*
318: * bgp_info_mpath_first
319: *
320: * Given bestpath bgp_info, return the first multipath entry.
321: */
322: struct bgp_info *
323: bgp_info_mpath_first (struct bgp_info *binfo)
324: {
325: return bgp_info_mpath_next (binfo);
326: }
327:
328: /*
329: * bgp_info_mpath_count
330: *
331: * Given the bestpath bgp_info, return the number of multipath entries
332: */
333: u_int32_t
334: bgp_info_mpath_count (struct bgp_info *binfo)
335: {
336: if (!binfo->mpath)
337: return 0;
338: return binfo->mpath->mp_count;
339: }
340:
341: /*
342: * bgp_info_mpath_count_set
343: *
344: * Sets the count of multipaths into bestpath's mpath element
345: */
346: static void
347: bgp_info_mpath_count_set (struct bgp_info *binfo, u_int32_t count)
348: {
349: struct bgp_info_mpath *mpath;
350: if (!count && !binfo->mpath)
351: return;
352: mpath = bgp_info_mpath_get (binfo);
353: if (!mpath)
354: return;
355: mpath->mp_count = count;
356: }
357:
358: /*
359: * bgp_info_mpath_attr
360: *
361: * Given bestpath bgp_info, return aggregated attribute set used
362: * for advertising the multipath route
363: */
364: struct attr *
365: bgp_info_mpath_attr (struct bgp_info *binfo)
366: {
367: if (!binfo->mpath)
368: return NULL;
369: return binfo->mpath->mp_attr;
370: }
371:
372: /*
373: * bgp_info_mpath_attr_set
374: *
375: * Sets the aggregated attribute into bestpath's mpath element
376: */
377: static void
378: bgp_info_mpath_attr_set (struct bgp_info *binfo, struct attr *attr)
379: {
380: struct bgp_info_mpath *mpath;
381: if (!attr && !binfo->mpath)
382: return;
383: mpath = bgp_info_mpath_get (binfo);
384: if (!mpath)
385: return;
386: mpath->mp_attr = attr;
387: }
388:
389: /*
390: * bgp_info_mpath_update
391: *
392: * Compare and sync up the multipath list with the mp_list generated by
393: * bgp_best_selection
394: */
395: void
396: bgp_info_mpath_update (struct bgp_node *rn, struct bgp_info *new_best,
397: struct bgp_info *old_best, struct list *mp_list,
398: struct bgp_maxpaths_cfg *mpath_cfg)
399: {
400: u_int16_t maxpaths, mpath_count, old_mpath_count;
401: struct listnode *mp_node, *mp_next_node;
402: struct bgp_info *cur_mpath, *new_mpath, *next_mpath, *prev_mpath;
403: int mpath_changed, debug;
404: char pfx_buf[INET_ADDRSTRLEN], nh_buf[2][INET_ADDRSTRLEN];
405:
406: mpath_changed = 0;
407: maxpaths = BGP_DEFAULT_MAXPATHS;
408: mpath_count = 0;
409: cur_mpath = NULL;
410: old_mpath_count = 0;
411: prev_mpath = new_best;
412: mp_node = listhead (mp_list);
413: debug = BGP_DEBUG (events, EVENTS);
414:
415: if (debug)
416: prefix2str (&rn->p, pfx_buf, sizeof (pfx_buf));
417:
418: if (new_best)
419: {
420: mpath_count++;
421: if (new_best != old_best)
422: bgp_info_mpath_dequeue (new_best);
1.1.1.2 ! misho 423: maxpaths = (new_best->peer->sort == BGP_PEER_IBGP) ?
1.1 misho 424: mpath_cfg->maxpaths_ibgp : mpath_cfg->maxpaths_ebgp;
425: }
426:
427: if (old_best)
428: {
429: cur_mpath = bgp_info_mpath_first (old_best);
430: old_mpath_count = bgp_info_mpath_count (old_best);
431: bgp_info_mpath_count_set (old_best, 0);
432: bgp_info_mpath_dequeue (old_best);
433: }
434:
435: /*
436: * We perform an ordered walk through both lists in parallel.
437: * The reason for the ordered walk is that if there are paths
438: * that were previously multipaths and are still multipaths, the walk
439: * should encounter them in both lists at the same time. Otherwise
440: * there will be paths that are in one list or another, and we
441: * will deal with these separately.
442: *
443: * Note that new_best might be somewhere in the mp_list, so we need
444: * to skip over it
445: */
446: while (mp_node || cur_mpath)
447: {
448: /*
449: * We can bail out of this loop if all existing paths on the
450: * multipath list have been visited (for cleanup purposes) and
451: * the maxpath requirement is fulfulled
452: */
453: if (!cur_mpath && (mpath_count >= maxpaths))
454: break;
455:
456: mp_next_node = mp_node ? listnextnode (mp_node) : NULL;
457: next_mpath = cur_mpath ? bgp_info_mpath_next (cur_mpath) : NULL;
458:
459: /*
460: * If equal, the path was a multipath and is still a multipath.
461: * Insert onto new multipath list if maxpaths allows.
462: */
463: if (mp_node && (listgetdata (mp_node) == cur_mpath))
464: {
465: list_delete_node (mp_list, mp_node);
466: bgp_info_mpath_dequeue (cur_mpath);
467: if ((mpath_count < maxpaths) &&
468: bgp_info_nexthop_cmp (prev_mpath, cur_mpath))
469: {
470: bgp_info_mpath_enqueue (prev_mpath, cur_mpath);
471: prev_mpath = cur_mpath;
472: mpath_count++;
473: }
474: else
475: {
476: mpath_changed = 1;
477: if (debug)
478: zlog_debug ("%s remove mpath nexthop %s peer %s", pfx_buf,
479: inet_ntop (AF_INET, &cur_mpath->attr->nexthop,
480: nh_buf[0], sizeof (nh_buf[0])),
481: sockunion2str (cur_mpath->peer->su_remote,
482: nh_buf[1], sizeof (nh_buf[1])));
483: }
484: mp_node = mp_next_node;
485: cur_mpath = next_mpath;
486: continue;
487: }
488:
489: if (cur_mpath && (!mp_node ||
490: (bgp_info_mpath_cmp (cur_mpath,
491: listgetdata (mp_node)) < 0)))
492: {
493: /*
494: * If here, we have an old multipath and either the mp_list
495: * is finished or the next mp_node points to a later
496: * multipath, so we need to purge this path from the
497: * multipath list
498: */
499: bgp_info_mpath_dequeue (cur_mpath);
500: mpath_changed = 1;
501: if (debug)
502: zlog_debug ("%s remove mpath nexthop %s peer %s", pfx_buf,
503: inet_ntop (AF_INET, &cur_mpath->attr->nexthop,
504: nh_buf[0], sizeof (nh_buf[0])),
505: sockunion2str (cur_mpath->peer->su_remote,
506: nh_buf[1], sizeof (nh_buf[1])));
507: cur_mpath = next_mpath;
508: }
509: else
510: {
511: /*
512: * If here, we have a path on the mp_list that was not previously
513: * a multipath (due to non-equivalance or maxpaths exceeded),
514: * or the matching multipath is sorted later in the multipath
515: * list. Before we enqueue the path on the new multipath list,
516: * make sure its not on the old_best multipath list or referenced
517: * via next_mpath:
518: * - If next_mpath points to this new path, update next_mpath to
519: * point to the multipath after this one
520: * - Dequeue the path from the multipath list just to make sure
521: */
522: new_mpath = listgetdata (mp_node);
523: list_delete_node (mp_list, mp_node);
524: if ((mpath_count < maxpaths) && (new_mpath != new_best) &&
525: bgp_info_nexthop_cmp (prev_mpath, new_mpath))
526: {
527: if (new_mpath == next_mpath)
528: next_mpath = bgp_info_mpath_next (new_mpath);
529: bgp_info_mpath_dequeue (new_mpath);
530:
531: bgp_info_mpath_enqueue (prev_mpath, new_mpath);
532: prev_mpath = new_mpath;
533: mpath_changed = 1;
534: mpath_count++;
535: if (debug)
536: zlog_debug ("%s add mpath nexthop %s peer %s", pfx_buf,
537: inet_ntop (AF_INET, &new_mpath->attr->nexthop,
538: nh_buf[0], sizeof (nh_buf[0])),
539: sockunion2str (new_mpath->peer->su_remote,
540: nh_buf[1], sizeof (nh_buf[1])));
541: }
542: mp_node = mp_next_node;
543: }
544: }
545:
546: if (new_best)
547: {
548: bgp_info_mpath_count_set (new_best, mpath_count-1);
549: if (mpath_changed || (bgp_info_mpath_count (new_best) != old_mpath_count))
550: SET_FLAG (new_best->flags, BGP_INFO_MULTIPATH_CHG);
551: }
552: }
553:
554: /*
555: * bgp_mp_dmed_deselect
556: *
557: * Clean up multipath information for BGP_INFO_DMED_SELECTED path that
558: * is not selected as best path
559: */
560: void
561: bgp_mp_dmed_deselect (struct bgp_info *dmed_best)
562: {
563: struct bgp_info *mpinfo, *mpnext;
564:
565: if (!dmed_best)
566: return;
567:
568: for (mpinfo = bgp_info_mpath_first (dmed_best); mpinfo; mpinfo = mpnext)
569: {
570: mpnext = bgp_info_mpath_next (mpinfo);
571: bgp_info_mpath_dequeue (mpinfo);
572: }
573:
574: bgp_info_mpath_count_set (dmed_best, 0);
575: UNSET_FLAG (dmed_best->flags, BGP_INFO_MULTIPATH_CHG);
576: assert (bgp_info_mpath_first (dmed_best) == 0);
577: }
578:
579: /*
580: * bgp_info_mpath_aggregate_update
581: *
582: * Set the multipath aggregate attribute. We need to see if the
583: * aggregate has changed and then set the ATTR_CHANGED flag on the
584: * bestpath info so that a peer update will be generated. The
585: * change is detected by generating the current attribute,
586: * interning it, and then comparing the interned pointer with the
587: * current value. We can skip this generate/compare step if there
588: * is no change in multipath selection and no attribute change in
589: * any multipath.
590: */
591: void
592: bgp_info_mpath_aggregate_update (struct bgp_info *new_best,
593: struct bgp_info *old_best)
594: {
595: struct bgp_info *mpinfo;
596: struct aspath *aspath;
597: struct aspath *asmerge;
598: struct attr *new_attr, *old_attr;
599: u_char origin, attr_chg;
600: struct community *community, *commerge;
601: struct ecommunity *ecomm, *ecommerge;
602: struct attr_extra *ae;
603: struct attr attr = { 0 };
604:
605: if (old_best && (old_best != new_best) &&
606: (old_attr = bgp_info_mpath_attr (old_best)))
607: {
608: bgp_attr_unintern (&old_attr);
609: bgp_info_mpath_attr_set (old_best, NULL);
610: }
611:
612: if (!new_best)
613: return;
614:
615: if (!bgp_info_mpath_count (new_best))
616: {
617: if ((new_attr = bgp_info_mpath_attr (new_best)))
618: {
619: bgp_attr_unintern (&new_attr);
620: bgp_info_mpath_attr_set (new_best, NULL);
621: SET_FLAG (new_best->flags, BGP_INFO_ATTR_CHANGED);
622: }
623: return;
624: }
625:
626: /*
627: * Bail out here if the following is true:
628: * - MULTIPATH_CHG bit is not set on new_best, and
629: * - No change in bestpath, and
630: * - ATTR_CHANGED bit is not set on new_best or any of the multipaths
631: */
632: if (!CHECK_FLAG (new_best->flags, BGP_INFO_MULTIPATH_CHG) &&
633: (old_best == new_best))
634: {
635: attr_chg = 0;
636:
637: if (CHECK_FLAG (new_best->flags, BGP_INFO_ATTR_CHANGED))
638: attr_chg = 1;
639: else
640: for (mpinfo = bgp_info_mpath_first (new_best); mpinfo;
641: mpinfo = bgp_info_mpath_next (mpinfo))
642: {
643: if (CHECK_FLAG (mpinfo->flags, BGP_INFO_ATTR_CHANGED))
644: {
645: attr_chg = 1;
646: break;
647: }
648: }
649:
650: if (!attr_chg)
651: {
652: assert (bgp_info_mpath_attr (new_best));
653: return;
654: }
655: }
656:
657: bgp_attr_dup (&attr, new_best->attr);
658:
659: /* aggregate attribute from multipath constituents */
660: aspath = aspath_dup (attr.aspath);
661: origin = attr.origin;
662: community = attr.community ? community_dup (attr.community) : NULL;
663: ae = attr.extra;
664: ecomm = (ae && ae->ecommunity) ? ecommunity_dup (ae->ecommunity) : NULL;
665:
666: for (mpinfo = bgp_info_mpath_first (new_best); mpinfo;
667: mpinfo = bgp_info_mpath_next (mpinfo))
668: {
669: asmerge = aspath_aggregate (aspath, mpinfo->attr->aspath);
670: aspath_free (aspath);
671: aspath = asmerge;
672:
673: if (origin < mpinfo->attr->origin)
674: origin = mpinfo->attr->origin;
675:
676: if (mpinfo->attr->community)
677: {
678: if (community)
679: {
680: commerge = community_merge (community, mpinfo->attr->community);
681: community = community_uniq_sort (commerge);
682: community_free (commerge);
683: }
684: else
685: community = community_dup (mpinfo->attr->community);
686: }
687:
688: ae = mpinfo->attr->extra;
689: if (ae && ae->ecommunity)
690: {
691: if (ecomm)
692: {
693: ecommerge = ecommunity_merge (ecomm, ae->ecommunity);
694: ecomm = ecommunity_uniq_sort (ecommerge);
695: ecommunity_free (&ecommerge);
696: }
697: else
698: ecomm = ecommunity_dup (ae->ecommunity);
699: }
700: }
701:
702: attr.aspath = aspath;
703: attr.origin = origin;
704: if (community)
705: {
706: attr.community = community;
707: attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES);
708: }
709: if (ecomm)
710: {
711: ae = bgp_attr_extra_get (&attr);
712: ae->ecommunity = ecomm;
713: attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES);
714: }
715:
716: /* Zap multipath attr nexthop so we set nexthop to self */
717: attr.nexthop.s_addr = 0;
718: #ifdef HAVE_IPV6
719: if (attr.extra)
720: memset (&attr.extra->mp_nexthop_global, 0, sizeof (struct in6_addr));
721: #endif /* HAVE_IPV6 */
722:
723: /* TODO: should we set ATOMIC_AGGREGATE and AGGREGATOR? */
724:
725: new_attr = bgp_attr_intern (&attr);
726: bgp_attr_extra_free (&attr);
727:
728: if (new_attr != bgp_info_mpath_attr (new_best))
729: {
730: if ((old_attr = bgp_info_mpath_attr (new_best)))
731: bgp_attr_unintern (&old_attr);
732: bgp_info_mpath_attr_set (new_best, new_attr);
733: SET_FLAG (new_best->flags, BGP_INFO_ATTR_CHANGED);
734: }
735: else
736: bgp_attr_unintern (&new_attr);
737: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>