summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Lamparter <equinox@diac24.net>2019-06-06 19:10:29 +0200
committerDavid Lamparter <equinox@diac24.net>2019-06-06 20:59:50 +0200
commite128c002261a8f602b8bcb786c990e47c52d8cf8 (patch)
treed5b96409491947af4b52bc40ab1a904c56303d5c
parent111a04f740ce257478029837a9c6ee0f851935a0 (diff)
lib: add optimized inet_ntop()
This commit is brought to you by: "Manuel - Gas Gas Gas" (Initial D) Signed-off-by: David Lamparter <equinox@diac24.net>
-rw-r--r--lib/ntop.c172
-rw-r--r--lib/subdir.am1
2 files changed, 173 insertions, 0 deletions
diff --git a/lib/ntop.c b/lib/ntop.c
new file mode 100644
index 0000000000..5500f988e0
--- /dev/null
+++ b/lib/ntop.c
@@ -0,0 +1,172 @@
+/*
+ * optimized ntop, about 10x faster than libc versions [as of 2019]
+ *
+ * Copyright (c) 2019 David Lamparter, for NetDEF, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "compiler.h"
+
+#define pos (*posx)
+
+static inline void putbyte(uint8_t bytex, char **posx)
+ __attribute__((always_inline)) OPTIMIZE;
+
+static inline void putbyte(uint8_t bytex, char **posx)
+{
+ bool zero = false;
+ int byte = bytex, tmp, a, b;
+
+ if ((tmp = byte - 200) >= 0) {
+ *pos++ = '2';
+ zero = true;
+ byte = tmp;
+ } else if ((tmp = byte - 100) >= 0) {
+ *pos++ = '1';
+ zero = true;
+ byte = tmp;
+ }
+
+ /* make sure the compiler knows the value range of "byte" */
+ assume(byte < 100 && byte >= 0);
+
+ b = byte % 10;
+ a = byte / 10;
+ if (a || zero) {
+ *pos++ = '0' + a;
+ *pos++ = '0' + b;
+ } else
+ *pos++ = '0' + b;
+}
+
+static inline void puthex(uint16_t word, char **posx)
+ __attribute__((always_inline)) OPTIMIZE;
+
+static inline void puthex(uint16_t word, char **posx)
+{
+ const char *digits = "0123456789abcdef";
+ if (word >= 0x1000)
+ *pos++ = digits[(word >> 12) & 0xf];
+ if (word >= 0x100)
+ *pos++ = digits[(word >> 8) & 0xf];
+ if (word >= 0x10)
+ *pos++ = digits[(word >> 4) & 0xf];
+ *pos++ = digits[word & 0xf];
+}
+
+#undef pos
+
+const char *frr_inet_ntop(int af, const void * restrict src,
+ char * restrict dst, socklen_t size)
+ __attribute__((flatten)) DSO_SELF OPTIMIZE;
+
+const char *frr_inet_ntop(int af, const void * restrict src,
+ char * restrict dst, socklen_t size)
+{
+ const uint8_t *b = src;
+ /* 8 * "abcd:" for IPv6
+ * note: the IPv4-embedded IPv6 syntax is only used for ::A.B.C.D,
+ * which isn't longer than 40 chars either. even with ::ffff:A.B.C.D
+ * it's shorter.
+ */
+ char buf[8 * 5], *o = buf;
+ size_t best = 0, bestlen = 0, curlen = 0, i;
+
+ switch (af) {
+ case AF_INET:
+inet4:
+ putbyte(b[0], &o);
+ *o++ = '.';
+ putbyte(b[1], &o);
+ *o++ = '.';
+ putbyte(b[2], &o);
+ *o++ = '.';
+ putbyte(b[3], &o);
+ *o++ = '\0';
+ break;
+ case AF_INET6:
+ for (i = 0; i < 8; i++) {
+ if (b[i * 2] || b[i * 2 + 1]) {
+ if (curlen && curlen > bestlen) {
+ best = i - curlen;
+ bestlen = curlen;
+ }
+ curlen = 0;
+ continue;
+ }
+ curlen++;
+ }
+ if (curlen && curlen > bestlen) {
+ best = i - curlen;
+ bestlen = curlen;
+ }
+ /* do we want ::ffff:A.B.C.D? */
+ if (best == 0 && bestlen == 6) {
+ *o++ = ':';
+ *o++ = ':';
+ b += 12;
+ goto inet4;
+ }
+ if (bestlen == 1)
+ bestlen = 0;
+
+ for (i = 0; i < 8; i++) {
+ if (bestlen && i == best) {
+ if (i == 0)
+ *o++ = ':';
+ *o++ = ':';
+ continue;
+ }
+ if (i > best && i < best + bestlen) {
+ continue;
+ }
+ puthex((b[i * 2] << 8) | b[i * 2 + 1], &o);
+
+ if (i < 7)
+ *o++ = ':';
+ }
+ *o++ = '\0';
+ break;
+ default:
+ return NULL;
+ }
+
+ i = o - buf;
+ if (i > size)
+ return NULL;
+ /* compiler might inline memcpy if it knows the length is short,
+ * although neither gcc nor clang actually do this currently [2019]
+ */
+ assume(i <= 8 * 5);
+ memcpy(dst, buf, i);
+ return dst;
+}
+
+/* we want to override libc inet_ntop, but make sure it shows up in backtraces
+ * as frr_inet_ntop (to avoid confusion while debugging)
+ */
+const char *inet_ntop(int af, const void *src, char *dst, socklen_t size)
+ __attribute__((alias ("frr_inet_ntop"))) DSO_SELF;
diff --git a/lib/subdir.am b/lib/subdir.am
index 50ff1feecc..4e6adced74 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -56,6 +56,7 @@ lib_libfrr_la_SOURCES = \
lib/northbound.c \
lib/northbound_cli.c \
lib/northbound_db.c \
+ lib/ntop.c \
lib/openbsd-tree.c \
lib/pid_output.c \
lib/plist.c \