]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Lib: Data type for VPN route distinguishers
authorOndrej Zajicek <santiago@crfreenet.org>
Mon, 16 Dec 2024 17:27:42 +0000 (18:27 +0100)
committerOndrej Zajicek <santiago@crfreenet.org>
Mon, 16 Dec 2024 17:27:42 +0000 (18:27 +0100)
Use a distinct data structure for VPN route distinguishers instead
of just u64.

12 files changed:
conf/cf-lex.l
conf/confbase.Y
filter/config.Y
filter/data.c
filter/data.h
filter/f-inst.c
lib/ip.h
lib/net.c
lib/net.h
proto/bgp/packets.c
proto/l3vpn/l3vpn.c
proto/l3vpn/l3vpn.h

index 637a5d384f29005f4503e44055975e61d8fbdeb6..0119e1f55435ef40c23d66db082768cfd154fc9a 100644 (file)
@@ -151,7 +151,7 @@ WHITE [ \t]
 
 {DIGIT}+:{DIGIT}+ {
   uint len1 UNUSED, len2;
-  u64 l;
+  u64 l, v;
   char *e;
 
   errno = 0;
@@ -163,76 +163,80 @@ WHITE [ \t]
   {
     len1 = 32;
     len2 = 16;
-    cf_lval.i64 = (2ULL << 48) | (((u64) l) << len2);
+    v = (2ULL << 48) | (((u64) l) << len2);
   }
   else
   {
     len1 = 16;
     len2 = 32;
-    cf_lval.i64 = 0 | (((u64) l) << len2);
+    v = 0 | (((u64) l) << len2);
   }
 
   errno = 0;
   l = bstrtoul10(e+1, &e);
   if (!e || *e || (errno == ERANGE) || (l >> len2))
     cf_error("Number out of range");
-  cf_lval.i64 |= l;
+  v |= l;
 
+  cf_lval.rd = rd_from_u64(v);
   return VPN_RD;
 }
 
 [02]:{DIGIT}+:{DIGIT}+ {
   uint len1, len2;
-  u64 l;
+  u64 l, v;
   char *e;
 
   if (yytext[0] == '0')
   {
-    cf_lval.i64 = 0;
     len1 = 16;
     len2 = 32;
+    v = 0;
   }
   else
   {
-    cf_lval.i64 = 2ULL << 48;
     len1 = 32;
     len2 = 16;
+    v = 2ULL << 48;
   }
 
   errno = 0;
   l = bstrtoul10(yytext+2, &e);
   if (!e || (*e != ':') || (errno == ERANGE) || (l >> len1))
     cf_error("ASN out of range");
-  cf_lval.i64 |= ((u64) l) << len2;
+  v |= ((u64) l) << len2;
 
   errno = 0;
   l = bstrtoul10(e+1, &e);
   if (!e || *e || (errno == ERANGE) || (l >> len2))
     cf_error("Number out of range");
-  cf_lval.i64 |= l;
+  v |= l;
 
+  cf_lval.rd = rd_from_u64(v);
   return VPN_RD;
 }
 
 {DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+:{DIGIT}+ {
   unsigned long int l;
   ip4_addr ip4;
+  u64 v;
   char *e;
 
-  cf_lval.i64 = 1ULL << 48;
+  v = 1ULL << 48;
 
   e = strchr(yytext, ':');
   *e++ = '\0';
   if (!ip4_pton(yytext, &ip4))
     cf_error("Invalid IPv4 address %s in Route Distinguisher", yytext);
-  cf_lval.i64 |= ((u64) ip4_to_u32(ip4)) << 16;
+  v |= ((u64) ip4_to_u32(ip4)) << 16;
 
   errno = 0;
   l = bstrtoul10(e, &e);
   if (!e || *e || (errno == ERANGE) || (l >> 16))
     cf_error("Number out of range");
-  cf_lval.i64 |= l;
+  v |= l;
 
+  cf_lval.rd = rd_from_u64(v);
   return VPN_RD;
 }
 
index 5b00937f22aad4867e9cdf3e051fe1ee5362857f..af64e3ff9cfdb45fa902745757fe1ed406ccaba4 100644 (file)
@@ -57,6 +57,7 @@ CF_DECLS
   uint i;
   u32 i32;
   u64 i64;
+  vpn_rd rd;
   ip_addr a;
   ip4_addr ip4;
   ip6_addr ip6;
@@ -109,7 +110,7 @@ CF_DECLS
 %token <i> NUM ENUM_TOKEN
 %token <ip4> IP4
 %token <ip6> IP6
-%token <i64> VPN_RD
+%token <rd> VPN_RD
 %token <s> CF_SYM_KNOWN CF_SYM_UNDEFINED CF_SYM_METHOD_BARE CF_SYM_METHOD_ARGS
 %token <t> TEXT
 %token <bs> BYTETEXT
index 72996850da10eb67a303ef2881334448a7cf7992..4340d5e0e5d33526f4b3c4aa1267e38a3c9ccfea 100644 (file)
@@ -641,7 +641,7 @@ fipa:
 set_atom0:
    NUM    { $$.type = T_INT; $$.val.i = $1; }
  | fipa   { $$ = $1; }
- | VPN_RD { $$.type = T_RD; $$.val.ec = $1; }
+ | VPN_RD { $$.type = T_RD; $$.val.rd = $1; }
  | ENUM_TOKEN { $$.type = pair_a($1); $$.val.i = pair_b($1); }
  | '(' term ')' {
      $$ = cf_eval($2, T_VOID);
@@ -801,7 +801,7 @@ constant:
  | TEXT     { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_STRING, .val.s = $1, }); }
  | BYTETEXT { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BYTESTRING, .val.bs = $1, }); }
  | fipa     { $$ = f_new_inst(FI_CONSTANT, $1); }
- | VPN_RD   { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_RD, .val.ec = $1, }); }
+ | VPN_RD   { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_RD, .val.rd = $1, }); }
  | net_     { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_NET, .val.net = $1, }); }
  | '[' ']' { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_SET, .val.t = NULL, }); }
  | '[' set_items ']' {
index 98e9a25ae1e26abd7ccbc2374677e18ca21c8366..f5a9e5eecf5263290702f6a34179c59693339c7e 100644 (file)
@@ -633,7 +633,7 @@ val_format(const struct f_val *v, buffer *buf)
   case T_QUAD: buffer_print(buf, "%R", v->val.i); return;
   case T_EC:   ec_format(buf2, v->val.ec); buffer_print(buf, "%s", buf2); return;
   case T_LC:   lc_format(buf2, v->val.lc); buffer_print(buf, "%s", buf2); return;
-  case T_RD:   rd_format(v->val.ec, buf2, 1024); buffer_print(buf, "%s", buf2); return;
+  case T_RD:   rd_format(v->val.rd, buf2, 1024); buffer_print(buf, "%s", buf2); return;
   case T_PREFIX_SET: trie_format(v->val.ti, buf); return;
   case T_SET:  tree_format(v->val.t, buf); return;
   case T_ENUM: buffer_print(buf, "(enum %x)%u", v->type, v->val.i); return;
index 811c4a802d842819deab35917d1875b730bd4159..f36820cba4d20ab4a82449c4e99afe276e62ca60 100644 (file)
@@ -85,6 +85,7 @@ struct f_val {
     uint i;
     u64 ec;
     lcomm lc;
+    vpn_rd rd;
     ip_addr ip;
     const net_addr *net;
     const char *s;
index efc82d2fe9e6979f2147854611382b4604de0795..f94580a662e7acaf5868b7730e2b1d0645050231 100644 (file)
     METHOD_CONSTRUCTOR("rd");
     if (!net_is_vpn(v1.val.net))
       runtime( "VPN address expected" );
-    RESULT(T_RD, ec, net_rd(v1.val.net));
+    RESULT(T_RD, rd, net_rd(v1.val.net));
   }
 
   /* Get first ASN from AS PATH */
index 661331ec2b99ac042857f004600e1638150d465a..bae05261a5b363c274d469cfefc78c55fb0235d3 100644 (file)
--- a/lib/ip.h
+++ b/lib/ip.h
@@ -392,36 +392,6 @@ static inline ip6_addr ip6_hton(ip6_addr a)
 static inline ip6_addr ip6_ntoh(ip6_addr a)
 { return _MI6(ntohl(_I0(a)), ntohl(_I1(a)), ntohl(_I2(a)), ntohl(_I3(a))); }
 
-#define MPLS_MAX_LABEL 0x100000
-
-#define MPLS_MAX_LABEL_STACK 8
-#define MPLS_MAX_LABEL_STRING MPLS_MAX_LABEL_STACK*12 + 5
-typedef struct mpls_label_stack {
-  uint len;
-  u32 stack[MPLS_MAX_LABEL_STACK];
-} mpls_label_stack;
-
-static inline int ACCESS_READ(1, 2)
-mpls_get(const char *buf, int buflen, u32 *stack)
-{
-  for (int i=0; (i<MPLS_MAX_LABEL_STACK) && (i*4+3 < buflen); i++)
-  {
-    u32 s = get_u32(buf + i*4);
-    stack[i] = s >> 12;
-    if (s & 0x100)
-      return i+1;
-  }
-  return -1;
-}
-
-static inline int
-mpls_put(char *buf, int len, u32 *stack)
-{
-  for (int i=0; i<len; i++)
-    put_u32(buf + i*4, stack[i] << 12 | (i+1 == len ? 0x100 : 0));
-
-  return len*4;
-}
 
 /*
  *     Unaligned data access (in network order)
@@ -481,4 +451,84 @@ int ip6_pton(const char *a, ip6_addr *o);
 
 char *ip_scope_text(uint);
 
+
+/*
+ *     MPLS labels
+ */
+
+#define MPLS_MAX_LABEL 0x100000
+
+#define MPLS_MAX_LABEL_STACK 8
+#define MPLS_MAX_LABEL_STRING MPLS_MAX_LABEL_STACK*12 + 5
+typedef struct mpls_label_stack {
+  uint len;
+  u32 stack[MPLS_MAX_LABEL_STACK];
+} mpls_label_stack;
+
+static inline int ACCESS_READ(1, 2)
+mpls_get(const char *buf, int buflen, u32 *stack)
+{
+  for (int i=0; (i<MPLS_MAX_LABEL_STACK) && (i*4+3 < buflen); i++)
+  {
+    u32 s = get_u32(buf + i*4);
+    stack[i] = s >> 12;
+    if (s & 0x100)
+      return i+1;
+  }
+  return -1;
+}
+
+static inline int
+mpls_put(char *buf, int len, u32 *stack)
+{
+  for (int i=0; i<len; i++)
+    put_u32(buf + i*4, stack[i] << 12 | (i+1 == len ? 0x100 : 0));
+
+  return len*4;
+}
+
+
+/*
+ *     VPN route distinguishers
+ */
+
+/* Using 2x u32 to avoid u64 alignment */
+typedef struct vpn_rd {
+  u32 hi;
+  u32 lo;
+} vpn_rd;
+
+#define RD_NONE                (vpn_rd){}
+
+static inline vpn_rd rd_from_u64(u64 val)
+{ return (vpn_rd){.hi = val >> 32, .lo = val }; }
+
+static inline u64 rd_to_u64(vpn_rd rd)
+{ return (((u64) rd.hi) << 32) | rd.lo; }
+
+static inline int rd_equal(vpn_rd a, vpn_rd b)
+{ return a.hi == b.hi && a.lo == b.lo; }
+
+static inline int rd_zero(vpn_rd a)
+{ return !a.hi && !a.lo; }
+
+static inline int rd_nonzero(vpn_rd a)
+{ return a.hi || a.lo; }
+
+static inline int rd_compare(vpn_rd a, vpn_rd b)
+{ return uint_cmp(a.hi, b.hi) ?: uint_cmp(a.lo, b.lo); }
+
+static inline u64 rd_hash0(vpn_rd rd, u32 p, u64 acc)
+{ return u32_hash0(rd.hi, p, u32_hash0(rd.lo, p, acc)); }
+
+static inline vpn_rd get_rd(const void *buf)
+{ return (vpn_rd) { .hi = get_u32(buf), .lo = get_u32(buf + 4) }; }
+
+static inline void * put_rd(void *buf, vpn_rd rd)
+{
+  put_u32(buf, rd.hi);
+  put_u32(buf+4, rd.lo);
+  return buf+8;
+}
+
 #endif
index 3bd7bc6712cf07dabdc1189794f551461844b31a..e83cdce7c0946a26d69f15a45d883f2761d74e2e 100644 (file)
--- a/lib/net.c
+++ b/lib/net.c
@@ -77,8 +77,10 @@ STATIC_ASSERT(sizeof(net_addr_aspa)  ==  8);
 
 
 int
-rd_format(const u64 rd, char *buf, int buflen)
+rd_format(const vpn_rd rd_, char *buf, int buflen)
 {
+  u64 rd = rd_to_u64(rd_);
+
   switch (rd >> 48)
   {
     case 0: return bsnprintf(buf, buflen, "%u:%u", (u32) (rd >> 32), (u32) rd);
index 61ed37ba51b8db00bf8011cd32ff270199f91bd5..fe5c0d8c7ceee66d3ac61483b8b6ff8d9f2df298 100644 (file)
--- a/lib/net.h
+++ b/lib/net.h
@@ -73,7 +73,7 @@ typedef struct net_addr_vpn4 {
   u8 pxlen;
   u16 length;
   ip4_addr prefix;
-  u64 rd;
+  vpn_rd rd;
 } net_addr_vpn4;
 
 typedef struct net_addr_vpn6 {
@@ -82,7 +82,7 @@ typedef struct net_addr_vpn6 {
   u16 length;
   ip6_addr prefix;
   u32 padding;
-  u64 rd;
+  vpn_rd rd;
 } net_addr_vpn6;
 
 typedef struct net_addr_roa4 {
@@ -206,10 +206,10 @@ static inline void net_fill_ip4(net_addr *a, ip4_addr prefix, uint pxlen)
 static inline void net_fill_ip6(net_addr *a, ip6_addr prefix, uint pxlen)
 { *(net_addr_ip6 *)a = NET_ADDR_IP6(prefix, pxlen); }
 
-static inline void net_fill_vpn4(net_addr *a, ip4_addr prefix, uint pxlen, u64 rd)
+static inline void net_fill_vpn4(net_addr *a, ip4_addr prefix, uint pxlen, vpn_rd rd)
 { *(net_addr_vpn4 *)a = NET_ADDR_VPN4(prefix, pxlen, rd); }
 
-static inline void net_fill_vpn6(net_addr *a, ip6_addr prefix, uint pxlen, u64 rd)
+static inline void net_fill_vpn6(net_addr *a, ip6_addr prefix, uint pxlen, vpn_rd rd)
 { *(net_addr_vpn6 *)a = NET_ADDR_VPN6(prefix, pxlen, rd); }
 
 static inline void net_fill_roa4(net_addr *a, ip4_addr prefix, uint pxlen, uint max_pxlen, u32 asn)
@@ -340,7 +340,7 @@ static inline uint net_pxlen(const net_addr *a)
 
 ip_addr net_pxmask(const net_addr *a);
 
-static inline u64 net_rd(const net_addr *a)
+static inline vpn_rd net_rd(const net_addr *a)
 {
   switch (a->type)
   {
@@ -349,7 +349,7 @@ static inline u64 net_rd(const net_addr *a)
   case NET_VPN6:
     return ((net_addr_vpn6 *)a)->rd;
   }
-  return 0;
+  return RD_NONE;
 }
 
 
@@ -410,10 +410,10 @@ static inline int net_zero_ip6(const net_addr_ip6 *a)
 { return !a->pxlen && ip6_zero(a->prefix); }
 
 static inline int net_zero_vpn4(const net_addr_vpn4 *a)
-{ return !a->pxlen && ip4_zero(a->prefix) && !a->rd; }
+{ return !a->pxlen && ip4_zero(a->prefix) && rd_zero(a->rd); }
 
 static inline int net_zero_vpn6(const net_addr_vpn6 *a)
-{ return !a->pxlen && ip6_zero(a->prefix) && !a->rd; }
+{ return !a->pxlen && ip6_zero(a->prefix) && rd_zero(a->rd); }
 
 static inline int net_zero_roa4(const net_addr_roa4 *a)
 { return !a->pxlen && ip4_zero(a->prefix) && !a->max_pxlen && !a->asn; }
@@ -441,10 +441,10 @@ static inline int net_compare_ip6(const net_addr_ip6 *a, const net_addr_ip6 *b)
 { return ip6_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen); }
 
 static inline int net_compare_vpn4(const net_addr_vpn4 *a, const net_addr_vpn4 *b)
-{ return u64_cmp(a->rd, b->rd) ?: ip4_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen); }
+{ return rd_compare(a->rd, b->rd) ?: ip4_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen); }
 
 static inline int net_compare_vpn6(const net_addr_vpn6 *a, const net_addr_vpn6 *b)
-{ return u64_cmp(a->rd, b->rd) ?: ip6_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen); }
+{ return rd_compare(a->rd, b->rd) ?: ip6_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen); }
 
 static inline int net_compare_roa4(const net_addr_roa4 *a, const net_addr_roa4 *b)
 { return ip4_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen) ?: uint_cmp(a->max_pxlen, b->max_pxlen) ?: uint_cmp(a->asn, b->asn); }
@@ -526,13 +526,13 @@ static inline u32 net_hash_ip6(const net_addr_ip6 *n)
 static inline u32 net_hash_vpn4(const net_addr_vpn4 *n)
 {
   u64 acc = ip4_hash0(n->prefix, HASH_PARAM, 0) ^ (n->pxlen << 26);
-  return hash_value(u64_hash0(n->rd, HASH_PARAM, acc));
+  return hash_value(rd_hash0(n->rd, HASH_PARAM, acc));
 }
 
 static inline u32 net_hash_vpn6(const net_addr_vpn6 *n)
 {
   u64 acc = ip6_hash0(n->prefix, HASH_PARAM, 0) ^ (n->pxlen << 26);
-  return hash_value(u64_hash0(n->rd, HASH_PARAM, acc));
+  return hash_value(rd_hash0(n->rd, HASH_PARAM, acc));
 }
 
 static inline u32 net_hash_roa4(const net_addr_roa4 *n)
@@ -637,7 +637,7 @@ void net_normalize(net_addr *N);
 
 int net_classify(const net_addr *N);
 int net_format(const net_addr *N, char *buf, int buflen) ACCESS_WRITE(2, 3);
-int rd_format(const u64 rd, char *buf, int buflen) ACCESS_WRITE(2, 3);
+int rd_format(const vpn_rd rd, char *buf, int buflen) ACCESS_WRITE(2, 3);
 
 static inline int ipa_in_px4(ip4_addr a, ip4_addr prefix, uint pxlen)
 { return ip4_zero(ip4_and(ip4_xor(a, prefix), ip4_mkmask(pxlen))); }
index 02bce7e7399c6e48e22269c2f7d57d837e09d758..8b78cc13215d8c969c6549b93ab1c5493aaed0ad 100644 (file)
@@ -1807,7 +1807,7 @@ bgp_encode_nlri_vpn4(struct bgp_write_state *s, struct bgp_bucket *buck, byte *b
       bgp_encode_mpls_labels(s, s->mpls_labels, &pos, &size, pos - 1);
 
     /* Encode route distinguisher */
-    put_u64(pos, net->rd);
+    put_rd(pos, net->rd);
     ADVANCE(pos, size, 8);
 
     /* Encode prefix body */
@@ -1858,7 +1858,7 @@ bgp_decode_nlri_vpn4(struct bgp_parse_state *s, byte *pos, uint len, rta *a)
     if (l < 64)
       bgp_parse_error(s, 1);
 
-    u64 rd = get_u64(pos);
+    vpn_rd rd = get_rd(pos);
     ADVANCE(pos, len, 8);
     l -= 64;
 
@@ -1907,7 +1907,7 @@ bgp_encode_nlri_vpn6(struct bgp_write_state *s, struct bgp_bucket *buck, byte *b
       bgp_encode_mpls_labels(s, s->mpls_labels, &pos, &size, pos - 1);
 
     /* Encode route distinguisher */
-    put_u64(pos, net->rd);
+    put_rd(pos, net->rd);
     ADVANCE(pos, size, 8);
 
     /* Encode prefix body */
@@ -1958,7 +1958,7 @@ bgp_decode_nlri_vpn6(struct bgp_parse_state *s, byte *pos, uint len, rta *a)
     if (l < 64)
       bgp_parse_error(s, 1);
 
-    u64 rd = get_u64(pos);
+    vpn_rd rd = get_rd(pos);
     ADVANCE(pos, len, 8);
     l -= 64;
 
index 8b56cd734d7f194487b0a1c2f1a6481f767ff277..ec9eeb75281344e2b1d957143dff1a3f06026b29 100644 (file)
@@ -177,14 +177,14 @@ l3vpn_rt_notify(struct proto *P, struct channel *c0, net *net, rte *new, rte *ol
 
   case NET_VPN4:
     net_fill_ip4(n, net4_prefix(n0), net4_pxlen(n0));
-    src = rt_get_source(&p->p, ((const net_addr_vpn4 *) n0)->rd);
+    src = rt_get_source(&p->p, rd_to_u64(((const net_addr_vpn4 *) n0)->rd));
     dst = p->ip4_channel;
     export = 0;
     break;
 
   case NET_VPN6:
     net_fill_ip6(n, net6_prefix(n0), net6_pxlen(n0));
-    src = rt_get_source(&p->p, ((const net_addr_vpn6 *) n0)->rd);
+    src = rt_get_source(&p->p, rd_to_u64(((const net_addr_vpn6 *) n0)->rd));
     dst = p->ip6_channel;
     export = 0;
     break;
@@ -331,7 +331,7 @@ l3vpn_postconfig(struct proto_config *CF)
   if (!proto_cf_find_channel(CF, NET_MPLS))
     cf_error("MPLS channel not specified");
 
-  if (!cf->rd)
+  if (rd_zero(cf->rd))
     cf_error("Route distinguisher not specified");
 
   if (!cf->import_target && !cf->export_target)
@@ -410,7 +410,7 @@ l3vpn_reconfigure(struct proto *P, struct proto_config *CF)
       !proto_configure_channel(P, &P->mpls_channel, proto_cf_find_channel(CF, NET_MPLS)))
     return 0;
 
-  if (p->rd != cf->rd)
+  if (!rd_equal(p->rd, cf->rd))
     return 0;
 
   int import_changed = !same_tree(p->import_target, cf->import_target);
index 52a9f36d5cc0bdb6af7592f331feb7cf4e979ac0..1cce28faacd84cd509fc4e36c0ab7747a2dde209 100644 (file)
@@ -13,7 +13,7 @@
 struct l3vpn_config {
   struct proto_config c;
 
-  u64 rd;
+  vpn_rd rd;
   struct f_tree *import_target;
   struct f_tree *export_target;
 };
@@ -25,7 +25,7 @@ struct l3vpn_proto {
   struct channel *vpn4_channel;
   struct channel *vpn6_channel;
 
-  u64 rd;
+  vpn_rd rd;
   struct f_tree *import_target;
   struct f_tree *export_target;
   u32 *export_target_data;