+/*
+ * AIGP handling
+ */
+
+static int
+bgp_aigp_valid(byte *data, uint len, char *err, uint elen)
+{
+ byte *pos = data;
+ char *err_dsc = NULL;
+ uint err_val = 0;
+
+#define BAD(DSC,VAL) ({ err_dsc = DSC; err_val = VAL; goto bad; })
+ while (len)
+ {
+ if (len < 3)
+ BAD("TLV framing error", len);
+
+ /* Process one TLV */
+ uint ptype = pos[0];
+ uint plen = get_u16(pos + 1);
+
+ if (len < plen)
+ BAD("TLV framing error", plen);
+
+ if (plen < 3)
+ BAD("Bad TLV length", plen);
+
+ if ((ptype == BGP_AIGP_METRIC) && (plen != 11))
+ BAD("Bad AIGP TLV length", plen);
+
+ ADVANCE(pos, len, plen);
+ }
+#undef BAD
+
+ return 1;
+
+bad:
+ if (err)
+ if (bsnprintf(err, elen, "%s (%u) at %d", err_dsc, err_val, (int) (pos - data)) < 0)
+ err[0] = 0;
+
+ return 0;
+}
+
+static const byte *
+bgp_aigp_get_tlv(const struct adata *ad, uint type)
+{
+ if (!ad)
+ return NULL;
+
+ uint len = ad->length;
+ const byte *pos = ad->data;
+
+ while (len)
+ {
+ uint ptype = pos[0];
+ uint plen = get_u16(pos + 1);
+
+ if (ptype == type)
+ return pos;
+
+ ADVANCE(pos, len, plen);
+ }
+
+ return NULL;
+}
+
+static const struct adata *
+bgp_aigp_set_tlv(struct linpool *pool, const struct adata *ad, uint type, byte *data, uint dlen)
+{
+ uint len = ad ? ad->length : 0;
+ const byte *pos = ad ? ad->data : NULL;
+ struct adata *res = lp_alloc_adata(pool, len + 3 + dlen);
+ byte *dst = res->data;
+ byte *tlv = NULL;
+ int del = 0;
+
+ while (len)
+ {
+ uint ptype = pos[0];
+ uint plen = get_u16(pos + 1);
+
+ /* Find position for new TLV */
+ if ((ptype >= type) && !tlv)
+ {
+ tlv = dst;
+ dst += 3 + dlen;
+ }
+
+ /* Skip first matching TLV, copy others */
+ if ((ptype == type) && !del)
+ del = 1;
+ else
+ {
+ memcpy(dst, pos, plen);
+ dst += plen;
+ }
+
+ ADVANCE(pos, len, plen);
+ }
+
+ if (!tlv)
+ {
+ tlv = dst;
+ dst += 3 + dlen;
+ }
+
+ /* Store the TLD */
+ put_u8(tlv + 0, type);
+ put_u16(tlv + 1, 3 + dlen);
+ memcpy(tlv + 3, data, dlen);
+
+ /* Update length */
+ res->length = dst - res->data;
+
+ return res;
+}
+
+static u64 UNUSED
+bgp_aigp_get_metric(const struct adata *ad, u64 def)
+{
+ const byte *b = bgp_aigp_get_tlv(ad, BGP_AIGP_METRIC);
+ return b ? get_u64(b + 3) : def;
+}
+
+static const struct adata *
+bgp_aigp_set_metric(struct linpool *pool, const struct adata *ad, u64 metric)
+{
+ byte data[8];
+ put_u64(data, metric);
+ return bgp_aigp_set_tlv(pool, ad, BGP_AIGP_METRIC, data, 8);
+}
+
+int
+bgp_total_aigp_metric_(rte *e, u64 *metric, const struct adata **ad)
+{
+ eattr *a = ea_find(e->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_AIGP));
+ if (!a)
+ return 0;
+
+ const byte *b = bgp_aigp_get_tlv(a->u.ptr, BGP_AIGP_METRIC);
+ if (!b)
+ return 0;
+
+ u64 aigp = get_u64(b + 3);
+ u64 step = e->attrs->igp_metric;
+
+ if (!rte_resolvable(e) || (step >= IGP_METRIC_UNKNOWN))
+ step = BGP_AIGP_MAX;
+
+ if (!step)
+ step = 1;
+
+ *ad = a->u.ptr;
+ *metric = aigp + step;
+ if (*metric < aigp)
+ *metric = BGP_AIGP_MAX;
+
+ return 1;
+}
+
+static inline int
+bgp_init_aigp_metric(rte *e, u64 *metric, const struct adata **ad)
+{
+ if (e->attrs->source == RTS_BGP)
+ return 0;
+
+ *metric = rt_get_igp_metric(e);
+ *ad = NULL;
+ return *metric < IGP_METRIC_UNKNOWN;
+}
+
+