]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Follow-up work on integration
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Mon, 21 Dec 2015 02:27:41 +0000 (03:27 +0100)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Mon, 21 Dec 2015 02:33:18 +0000 (03:33 +0100)
18 files changed:
filter/config.Y
lib/net.c
lib/net.h
lib/printf.c
lib/socket.h
lib/unaligned.h
nest/iface.c
nest/rt-fib.c
proto/ospf/config.Y
proto/ospf/ospf.c
proto/ospf/ospf.h
proto/ospf/rt.c
proto/rip/config.Y
proto/rip/packets.c
proto/rip/rip.c
proto/rip/rip.h
sysdep/linux/netlink.c
tools/Makefile.in

index 92ac6fa58064a9fc9e77937d1ecdcc1e9dfaa5ff..7e9f4104ed9e2ec58d6dc29ae410d57fef9be76a 100644 (file)
@@ -205,7 +205,6 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv)
       cf_error("Can't operate with key of non-integer/IPv4 type in EC constructor");
   }
 
-#ifndef IPV6
   /* IP->Quad implicit conversion */
   else if (tk->code == 'C') {
     c1 = 1;
@@ -223,7 +222,6 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv)
     else
       cf_error("Can't operate with key of non-integer/IPv4 type in EC constructor");
   }
-#endif
 
   if (tv->code == 'c') {
     if (tv->aux != T_INT)
index b851871adfb961d670dfad2ebd6f0dbaad909fa7..09f23c7a1237085b2e8b2feeb009253308e21a49 100644 (file)
--- a/lib/net.c
+++ b/lib/net.c
@@ -31,15 +31,15 @@ net_format(const net_addr *N, char *buf, int buflen)
 {
   net_addr_union *n = (void *) N;
 
-  /* FIXME: quick hack */
+  /* XXXX fix %I vs %R */
   switch (n->n.type)
   {
   case NET_IP4:
-    return bsnprintf(buf, buflen, "%I/%d", n->ip4.prefix, n->ip4.pxlen);
+    return bsnprintf(buf, buflen, "%R/%d", n->ip4.prefix, n->ip4.pxlen);
   case NET_IP6:
     return bsnprintf(buf, buflen, "%I/%d", n->ip6.prefix, n->ip6.pxlen);
   case NET_VPN4:
-    return bsnprintf(buf, buflen, "%u:%u %I/%d", (u32) (n->vpn4.rd >> 32), (u32) n->vpn4.rd, n->vpn4.prefix, n->vpn4.pxlen);
+    return bsnprintf(buf, buflen, "%u:%u %R/%d", (u32) (n->vpn4.rd >> 32), (u32) n->vpn4.rd, n->vpn4.prefix, n->vpn4.pxlen);
   case NET_VPN6:
     return bsnprintf(buf, buflen, "%u:%u %I/%d", (u32) (n->vpn6.rd >> 32), (u32) n->vpn6.rd, n->vpn6.prefix, n->vpn6.pxlen);
   }
index ee9b382ec80fe953e7218478d413b17f686d2c05..9439c6dbe538a625b4af8c1d6959985df5671296 100644 (file)
--- a/lib/net.h
+++ b/lib/net.h
@@ -199,20 +199,20 @@ static inline void net_copy_vpn6(net_addr_vpn6 *dst, const net_addr_vpn6 *src)
 
 
 static inline u32 net_hash_ip4(const net_addr_ip4 *n)
-{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
+{ return ip4_hash32(n->prefix) ^ ((u32) n->pxlen << 26); }
 
 static inline u32 net_hash_ip6(const net_addr_ip6 *n)
-{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
+{ return ip6_hash32(n->prefix) ^ ((u32) n->pxlen << 26); }
 
 /* XXXX */
 static inline u32 u64_hash(u32 a)
 { return u32_hash(a); }
 
 static inline u32 net_hash_vpn4(const net_addr_vpn4 *n)
-{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26) ^ u64_hash(n->rd); }
+{ return ip4_hash32(n->prefix) ^ ((u32) n->pxlen << 26) ^ u64_hash(n->rd); }
 
 static inline u32 net_hash_vpn6(const net_addr_vpn6 *n)
-{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26) ^ u64_hash(n->rd); }
+{ return ip6_hash32(n->prefix) ^ ((u32) n->pxlen << 26) ^ u64_hash(n->rd); }
 
 
 static inline int net_validate_ip4(const net_addr_ip4 *n)
index 9db5363b7888e7d5916dadd303fc85e8a2087798..071fc953a82fb2f8204121da3a52068f92167a2c 100644 (file)
@@ -292,9 +292,13 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
                case 'I': {
                        ip_addr a = va_arg(args, ip_addr);
                        if (flags & SPECIAL)
-                               ipa_ntox(a, ipbuf);
+                               ip6_ntox(ipa_to_ip6(a), ipbuf);
                        else {
-                               ipa_ntop(a, ipbuf);
+                               // XXXX better IPv4 / IPv6 distinction
+                               if (ipa_is_ip4(a))
+                                       ip4_ntop(ipa_to_ip4(a), ipbuf);
+                               else
+                                       ip6_ntop(ipa_to_ip6(a), ipbuf);
                                if (field_width == 1)
                                        field_width = (ipa_is_ip4(a) ? IP4_MAX_TEXT_LENGTH : IP6_MAX_TEXT_LENGTH);
                        }
@@ -319,11 +323,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
                /* Router/Network ID - essentially IPv4 address in u32 value */
                case 'R':
                        x = va_arg(args, u32);
-                       bsprintf(ipbuf, "%d.%d.%d.%d",
-                                ((x >> 24) & 0xff),
-                                ((x >> 16) & 0xff),
-                                ((x >> 8) & 0xff),
-                                (x & 0xff));
+                       ip4_ntop(ip4_from_u32(x), ipbuf);
                        s = ipbuf;
                        goto str;
 
index fbea92aa79570775d354335b10e593e79e82c26e..b067eb542b9d78a6e09dde7dd1e6ebeaa6b29cfd 100644 (file)
@@ -67,18 +67,14 @@ void sk_set_tbsize(sock *s, uint val);      /* Resize TX buffer, keeping content */
 void sk_set_tbuf(sock *s, void *tbuf); /* Switch TX buffer, NULL-> return to internal */
 void sk_dump_all(void);
 
-static inline int sk_send_buffer_empty(sock *sk)
-{ return sk->tbuf == sk->tpos; }
-
+static inline int sk_is_ipv4(sock *s)
+{ return s->af == AF_INET; }
 
-#ifdef IPV6
-#define sk_is_ipv4(X) 0
-#define sk_is_ipv6(X) 1
-#else
-#define sk_is_ipv4(X) 1
-#define sk_is_ipv6(X) 0
-#endif
+static inline int sk_is_ipv6(sock *s)
+{ return s->af == AF_INET6; }
 
+static inline int sk_send_buffer_empty(sock *sk)
+{ return sk->tbuf == sk->tpos; }
 
 int sk_setup_multicast(sock *s);       /* Prepare UDP or IP socket for multicasting */
 int sk_join_group(sock *s, ip_addr maddr);     /* Join multicast group on sk iface */
index dc777fbff6d352c10721cb69c8d59da2ac0fda34..130b247914b93db0800d2fc0e93951b9632526df 100644 (file)
@@ -17,6 +17,7 @@
  *  if possible.
  */
 
+#include "lib/endian.h"
 #include "lib/string.h"
 
 static inline u16
index 3eaa5ebd4c43f2d947d2fd3d0ab435cc5e17bc16..e9f55f0ac2046aab7de055ea4d8bccd9b5bce40f 100644 (file)
@@ -532,10 +532,8 @@ ifa_update(struct ifa *a)
        break;
       }
 
-#ifndef IPV6
-  if ((i->flags & IF_BROADCAST) && !ipa_nonzero(a->brd))
+  if ((a->prefix.type == NET_IP4) && (i->flags & IF_BROADCAST) && ipa_zero(a->brd))
     log(L_ERR "Missing broadcast address for interface %s", i->name);
-#endif
 
   b = mb_alloc(if_pool, sizeof(struct ifa));
   memcpy(b, a, sizeof(struct ifa));
index ff7f5c00f6c948821a7ea95cfe45415d3e8e35f8..474236e001b857a388d2c2c965381182b2e1cfd2 100644 (file)
@@ -59,7 +59,7 @@ static void
 fib_ht_alloc(struct fib *f)
 {
   f->hash_size = 1 << f->hash_order;
-  f->hash_shift = 16 - f->hash_order;
+  f->hash_shift = 32 - f->hash_order;
   if (f->hash_order > HASH_HI_MAX - HASH_HI_STEP)
     f->entries_max = ~0;
   else
@@ -168,7 +168,7 @@ fib_rehash(struct fib *f, int step)
     struct fib_node *e = f->hash_table[FIB_HASH(f, a, t)];             \
     while (e && !net_equal_##t(CAST(t) e->addr, CAST(t) a))            \
       e = e->next;                                                     \
-    fib_node_to_user(f, e);                                            \
+    e ? fib_node_to_user(f, e) : NULL;                                 \
   })
 
 #define FIB_INSERT(f,a,e,t)                                            \
@@ -247,7 +247,7 @@ fib_insert(struct fib *f, const net_addr *a, struct fib_node *e)
 void *
 fib_get(struct fib *f, const net_addr *a)
 {
-  char *b = fib_find(f, a);
+  void *b = fib_find(f, a);
   if (b)
     return b;
 
@@ -256,7 +256,7 @@ fib_get(struct fib *f, const net_addr *a)
   else
     b = mb_alloc(f->fib_pool, f->node_size + a->length);
 
-  struct fib_node *e = (void *) (b + f->node_offset);
+  struct fib_node *e = fib_user_to_node(f, b);
   e->readers = NULL;
   e->flags = 0;
   e->uid = 0;
@@ -522,6 +522,7 @@ found:
 void
 fib_check(struct fib *f)
 {
+#if 0
   uint i, ec, lo, nulls;
 
   ec = 0;
@@ -557,6 +558,8 @@ fib_check(struct fib *f)
     }
   if (ec != f->entries)
     bug("fib_check: invalid entry count (%d != %d)", ec, f->entries);
+#endif
+  return;
 }
 
 /*
index e8fb7f2e0a322620635849c57097ef74eb531a91..6d8fa7551ebc27a038034e623edf6d014cf991e0 100644 (file)
@@ -130,21 +130,27 @@ CF_KEYWORDS(ELIGIBLE, POLL, NETWORKS, HIDDEN, VIRTUAL, CHECK, LINK, ONLY, BFD)
 CF_KEYWORDS(RX, BUFFER, LARGE, NORMAL, STUBNET, HIDDEN, SUMMARY, TAG, EXTERNAL)
 CF_KEYWORDS(WAIT, DELAY, LSADB, ECMP, LIMIT, WEIGHT, NSSA, TRANSLATOR, STABILITY)
 CF_KEYWORDS(GLOBAL, LSID, ROUTER, SELF, INSTANCE, REAL, NETMASK, TX, PRIORITY, LENGTH)
-CF_KEYWORDS(SECONDARY, MERGE, LSA, SUPPRESSION)
+CF_KEYWORDS(SECONDARY, MERGE, LSA, SUPPRESSION, OSPF2, OSPF3)
 
 %type <ld> lsadb_args
-%type <i> nbma_eligible
+%type <i> ospf_variant nbma_eligible
 
 CF_GRAMMAR
 
 CF_ADDTO(proto, ospf_proto '}' { ospf_proto_finish(); } )
 
-ospf_proto_start: proto_start OSPF {
+ospf_variant:
+   OSPF  { $$ = 1; }
+ | OSPF2 { $$ = 1; }
+ | OSPF3 { $$ = 0; }
+ ;
+
+ospf_proto_start: proto_start ospf_variant {
      this_proto = proto_config_new(&proto_ospf, $1);
      init_list(&OSPF_CFG->area_list);
      init_list(&OSPF_CFG->vlink_list);
      OSPF_CFG->tick = OSPF_DEFAULT_TICK;
-     OSPF_CFG->ospf2 = OSPF_IS_V2;
+     OSPF_CFG->ospf2 = $2;
   }
  ;
 
index 983f76f13bdd6ec9352f2f675319186a1587eb89..8008e4d8a49e920eb6805da8cc1c9d65cd32bcd2 100644 (file)
@@ -648,6 +648,9 @@ ospf_reconfigure(struct proto *P, struct proto_config *c)
   if (proto_get_router_id(c) != p->router_id)
     return 0;
 
+  if (p->ospf2 != new->ospf2)
+    return 0;
+
   if (p->rfc1583 != new->rfc1583)
     return 0;
 
index 3e96b5112cea84b400ec4121f130bcca52cda6f5..f4f91de1a412185f2bcb59ff7569b1724de6fe52 100644 (file)
 #endif
 
 
-#ifdef IPV6
-#define OSPF_IS_V2 0
-#else
-#define OSPF_IS_V2 1
-#endif
-
-// FIXME: MAX_PREFIX_LENGTH
-
 #define OSPF_TRACE(flags, msg, args...) \
   do { if ((p->p.debug & flags) || OSPF_FORCE_DEBUG) \
     log(L_TRACE "%s: " msg, p->p.name , ## args ); } while(0)
@@ -837,16 +829,12 @@ static inline void ospf_notify_net_lsa(struct ospf_iface *ifa)
 static inline void ospf_notify_link_lsa(struct ospf_iface *ifa)
 { ifa->update_link_lsa = 1; }
 
-
-#define ospf_is_v2(X) OSPF_IS_V2
-#define ospf_is_v3(X) (!OSPF_IS_V2)
-/*
 static inline int ospf_is_v2(struct ospf_proto *p)
 { return p->ospf2; }
 
 static inline int ospf_is_v3(struct ospf_proto *p)
 { return ! p->ospf2; }
-*/
+
 static inline int ospf_get_version(struct ospf_proto *p)
 { return ospf_is_v2(p) ? 2 : 3; }
 
index 0adc387193dcf8909c8171eb9929cbacbdaa77fc..0031089c3e1008190183a5f17735635e96c3775e 100644 (file)
@@ -1428,7 +1428,7 @@ loop:
 static void *
 ospf_fib_route(struct fib *f, ip_addr a)
 {
-  if (ospf_is_v2(p))
+  if (f->addr_type == NET_IP4)
     return ospf_fib_route_ip4(f, ipa_to_ip4(a), IP4_MAX_PREFIX_LENGTH);
   else
     return ospf_fib_route_ip6(f, ipa_to_ip6(a), IP6_MAX_PREFIX_LENGTH);
index 29ea7eb176a48d3edcd82c16e1ff65f33a83399a..d81c42d77257b33b13abcd8564de7c1a2a7f924b 100644 (file)
@@ -32,24 +32,29 @@ rip_check_auth(void)
 
 CF_DECLS
 
-CF_KEYWORDS(RIP, ECMP, LIMIT, WEIGHT, INFINITY, METRIC, UPDATE, TIMEOUT,
+CF_KEYWORDS(RIP, RIPNG, ECMP, LIMIT, WEIGHT, INFINITY, METRIC, UPDATE, TIMEOUT,
            GARBAGE, PORT, ADDRESS, MODE, BROADCAST, MULTICAST, PASSIVE,
            VERSION, SPLIT, HORIZON, POISON, REVERSE, CHECK, ZERO, TIME, BFD,
            AUTHENTICATION, NONE, PLAINTEXT, CRYPTOGRAPHIC, MD5, TTL, SECURITY,
            RX, TX, BUFFER, LENGTH, PRIORITY, ONLY, LINK, RIP_METRIC, RIP_TAG)
 
-%type <i> rip_auth
+%type <i> rip_variant rip_auth
 
 CF_GRAMMAR
 
 CF_ADDTO(proto, rip_proto)
 
-rip_proto_start: proto_start RIP
+rip_variant:
+   RIP   { $$ = 1; }
+ | RIPNG { $$ = 0; }
+ ;
+
+rip_proto_start: proto_start rip_variant
 {
   this_proto = proto_config_new(&proto_rip, $1);
   init_list(&RIP_CFG->patt_list);
 
-  RIP_CFG->rip2 = RIP_IS_V2;
+  RIP_CFG->rip2 = $2;
   RIP_CFG->infinity = RIP_DEFAULT_INFINITY;
 
   RIP_CFG->min_timeout_time = 60;
index 85f0bea55257966d8d5bac0081378b5db57c7499..2e83a4635e255fd684d5c4a7ed43ad11534321ff 100644 (file)
@@ -736,7 +736,8 @@ rip_open_socket(struct rip_iface *ifa)
   sk->tos = ifa->cf->tx_tos;
   sk->priority = ifa->cf->tx_priority;
   sk->ttl = ifa->cf->ttl_security ? 255 : 1;
-  sk->flags = SKF_LADDR_RX | ((ifa->cf->ttl_security == 1) ? SKF_TTL_RX : 0);
+  sk->flags = SKF_LADDR_RX | (rip_is_ng(p) ? SKF_V6ONLY : 0) |
+    ((ifa->cf->ttl_security == 1) ? SKF_TTL_RX : 0);
 
   /* sk->rbsize and sk->tbsize are handled in rip_iface_update_buffers() */
 
index d8c3b6a843e8256ba260d87ff89e251418d26b15..f90d7711d19acf8d6e4b44502778b51358d29d07 100644 (file)
@@ -1097,11 +1097,12 @@ rip_start(struct proto *P)
   struct rip_config *cf = (void *) (P->cf);
 
   init_list(&p->iface_list);
-  fib_init(&p->rtable, P->pool, rip_is_v2(p) ? NET_IP4 : NET_IP6,
+  fib_init(&p->rtable, P->pool, cf->rip2 ? NET_IP4 : NET_IP6,
           sizeof(struct rip_entry), OFFSETOF(struct rip_entry, n), 0, NULL);
   p->rte_slab = sl_new(P->pool, sizeof(struct rip_rte));
   p->timer = tm_new_set(P->pool, rip_timer, p, 0, 0);
 
+  p->rip2 = cf->rip2;
   p->ecmp = cf->ecmp;
   p->infinity = cf->infinity;
   p->triggered = 0;
@@ -1121,6 +1122,9 @@ rip_reconfigure(struct proto *P, struct proto_config *c)
   struct rip_config *new = (void *) c;
   // struct rip_config *old = (void *) (P->cf);
 
+  if (new->rip2 != p->rip2)
+    return 0;
+
   if (new->infinity != p->infinity)
     return 0;
 
index 97c5ab3f3267442701c7162892936b33f401060e..d1c9933c1b00657f23a8b7ebd8f17ebc389e94ac 100644 (file)
 #include "lib/timer.h"
 
 
-#ifdef IPV6
-#define RIP_IS_V2 0
-#else
-#define RIP_IS_V2 1
-#endif
-
 #define RIP_V1                 1
 #define RIP_V2                 2
 
@@ -98,6 +92,7 @@ struct rip_proto
   slab *rte_slab;                      /* Slab for internal routes (struct rip_rte) */
   timer *timer;                                /* Main protocol timer */
 
+  u8 rip2;                             /* RIPv2 (IPv4) or RIPng (IPv6) */
   u8 ecmp;                             /* Maximum number of nexthops in ECMP route, or 0 */
   u8 infinity;                         /* Maximum metric value, representing infinity */
   u8 triggered;                                /* Logical AND of interface want_triggered values */
@@ -190,16 +185,11 @@ struct rip_rte
 #define EA_RIP_METRIC          EA_CODE(EAP_RIP, 0)
 #define EA_RIP_TAG             EA_CODE(EAP_RIP, 1)
 
-#define rip_is_v2(X) RIP_IS_V2
-#define rip_is_ng(X) (!RIP_IS_V2)
-
-/*
 static inline int rip_is_v2(struct rip_proto *p)
 { return p->rip2; }
 
 static inline int rip_is_ng(struct rip_proto *p)
 { return ! p->rip2; }
-*/
 
 static inline void
 rip_reset_tx_session(struct rip_proto *p, struct rip_iface *ifa)
index 5015eba32e828099924f359e546a44cf29603d69..b3d0a9ab24986edc0f60b61104f9be924dc79152 100644 (file)
@@ -477,12 +477,12 @@ nl_parse_multipath(struct krt_proto *p, struct rtattr *ra)
       nl_parse_attrs(RTNH_DATA(nh), mpnh_attr_want4, a, sizeof(a));
       if (a[RTA_GATEWAY])
        {
-         memcpy(&rv->gw, RTA_DATA(a[RTA_GATEWAY]), sizeof(ip_addr));
-         ipa_ntoh(rv->gw);
+         rv->gw = rta_get_ipa(a[RTA_GATEWAY]);
 
-         neighbor *ng = neigh_find2(&p->p, &rv->gw, rv->iface,
-                                    (nh->rtnh_flags & RTNH_F_ONLINK) ? NEF_ONLINK : 0);
-         if (!ng || (ng->scope == SCOPE_HOST))
+         neighbor *nbr;
+         nbr = neigh_find2(&p->p, &rv->gw, rv->iface,
+                           (nh->rtnh_flags & RTNH_F_ONLINK) ? NEF_ONLINK : 0);
+         if (!nbr || (nbr->scope == SCOPE_HOST))
            return NULL;
        }
       else
@@ -1165,18 +1165,17 @@ nl_parse_route(struct nlmsghdr *h, int scan)
 
       if (a[RTA_GATEWAY])
        {
-         neighbor *ng;
          ra.dest = RTD_ROUTER;
-         memcpy(&ra.gw, RTA_DATA(a[RTA_GATEWAY]), sizeof(ra.gw));
-         ipa_ntoh(ra.gw);
+         ra.gw = rta_get_ipa(a[RTA_GATEWAY]);
 
          /* Silently skip strange 6to4 routes */
          if ((i->rtm_family == AF_INET6) && ipa_in_net(ra.gw, IPA_NONE, 96))
            return;
 
-         ng = neigh_find2(&p->p, &ra.gw, ra.iface,
-                          (i->rtm_flags & RTNH_F_ONLINK) ? NEF_ONLINK : 0);
-         if (!ng || (ng->scope == SCOPE_HOST))
+         neighbor *nbr;
+         nbr = neigh_find2(&p->p, &ra.gw, ra.iface,
+                           (i->rtm_flags & RTNH_F_ONLINK) ? NEF_ONLINK : 0);
+         if (!nbr || (nbr->scope == SCOPE_HOST))
            {
              log(L_ERR "KRT: Received route %N with strange next-hop %I", net->n.addr, ra.gw);
              return;
index 01bb7a7c1459bbbf12ba670baaf1af8e3c5cf956..5de323ab4e85adfb78bb6c7daa11ad15b891f540 100644 (file)
@@ -72,15 +72,15 @@ tags:
 
 install: all
        $(INSTALL) -d $(DESTDIR)/$(sbindir) $(DESTDIR)/$(sysconfdir) $(DESTDIR)/@runtimedir@
-       $(INSTALL_PROGRAM) $(exedir)/bird $(DESTDIR)/$(sbindir)/bird@SUFFIX@
-       $(INSTALL_PROGRAM) $(exedir)/birdcl $(DESTDIR)/$(sbindir)/birdcl@SUFFIX@
+       $(INSTALL_PROGRAM) $(exedir)/bird $(DESTDIR)/$(sbindir)/bird
+       $(INSTALL_PROGRAM) $(exedir)/birdcl $(DESTDIR)/$(sbindir)/birdcl
        if test -n "@CLIENT@" ; then                                                            \
-               $(INSTALL_PROGRAM) $(exedir)/birdc $(DESTDIR)/$(sbindir)/birdc@SUFFIX@ ;        \
+               $(INSTALL_PROGRAM) $(exedir)/birdc $(DESTDIR)/$(sbindir)/birdc ;                \
        fi
        if ! test -f $(DESTDIR)/@CONFIG_FILE@ ; then                                            \
                $(INSTALL_DATA) $(srcdir)/doc/bird.conf.example $(DESTDIR)/@CONFIG_FILE@ ;      \
        else                                                                                    \
-               echo "Not overwriting old bird@SUFFIX@.conf" ;                                  \
+               echo "Not overwriting old bird.conf" ;                                          \
        fi
 
 install-docs: