Line | Count | Source (jump to first uncovered line) |
1 | | // SPDX-License-Identifier: ISC |
2 | | /* |
3 | | * optimized ntop, about 10x faster than libc versions [as of 2019] |
4 | | * |
5 | | * Copyright (c) 2019 David Lamparter, for NetDEF, Inc. |
6 | | */ |
7 | | |
8 | | #ifdef HAVE_CONFIG_H |
9 | | #include "config.h" |
10 | | #endif |
11 | | |
12 | | #include <stdio.h> |
13 | | #include <stdint.h> |
14 | | #include <stdbool.h> |
15 | | #include <string.h> |
16 | | #include <sys/socket.h> |
17 | | #include <netinet/in.h> |
18 | | #include <arpa/inet.h> |
19 | | |
20 | | #include "compiler.h" |
21 | | |
22 | 2.44M | #define pos (*posx) |
23 | | |
24 | | static inline void putbyte(uint8_t bytex, char **posx) |
25 | | __attribute__((always_inline)) OPTIMIZE; |
26 | | |
27 | | static inline void putbyte(uint8_t bytex, char **posx) |
28 | 1.04M | { |
29 | 1.04M | bool zero = false; |
30 | 1.04M | int byte = bytex, tmp, a, b; |
31 | | |
32 | 1.04M | tmp = byte - 200; |
33 | 1.04M | if (tmp >= 0) { |
34 | 277k | *pos++ = '2'; |
35 | 277k | zero = true; |
36 | 277k | byte = tmp; |
37 | 768k | } else { |
38 | 768k | tmp = byte - 100; |
39 | 768k | if (tmp >= 0) { |
40 | 208k | *pos++ = '1'; |
41 | 208k | zero = true; |
42 | 208k | byte = tmp; |
43 | 208k | } |
44 | 768k | } |
45 | | |
46 | | /* make sure the compiler knows the value range of "byte" */ |
47 | 1.04M | assume(byte < 100 && byte >= 0); |
48 | | |
49 | 1.04M | b = byte % 10; |
50 | 1.04M | a = byte / 10; |
51 | 1.04M | if (a || zero) { |
52 | 909k | *pos++ = '0' + a; |
53 | 909k | *pos++ = '0' + b; |
54 | 909k | } else |
55 | 137k | *pos++ = '0' + b; |
56 | 1.04M | } |
57 | | |
58 | | static inline void puthex(uint16_t word, char **posx) |
59 | | __attribute__((always_inline)) OPTIMIZE; |
60 | | |
61 | | static inline void puthex(uint16_t word, char **posx) |
62 | 0 | { |
63 | 0 | const char *digits = "0123456789abcdef"; |
64 | 0 | if (word >= 0x1000) |
65 | 0 | *pos++ = digits[(word >> 12) & 0xf]; |
66 | 0 | if (word >= 0x100) |
67 | 0 | *pos++ = digits[(word >> 8) & 0xf]; |
68 | 0 | if (word >= 0x10) |
69 | 0 | *pos++ = digits[(word >> 4) & 0xf]; |
70 | 0 | *pos++ = digits[word & 0xf]; |
71 | 0 | } |
72 | | |
73 | | #undef pos |
74 | | |
75 | | const char *frr_inet_ntop(int af, const void * restrict src, |
76 | | char * restrict dst, socklen_t size) |
77 | | __attribute__((flatten)) OPTIMIZE; |
78 | | |
79 | | const char *frr_inet_ntop(int af, const void * restrict src, |
80 | | char * restrict dst, socklen_t size) |
81 | 261k | { |
82 | 261k | const uint8_t *b = src; |
83 | | /* 8 * "abcd:" for IPv6 |
84 | | * note: the IPv4-embedded IPv6 syntax is only used for ::A.B.C.D, |
85 | | * which isn't longer than 40 chars either. even with ::ffff:A.B.C.D |
86 | | * it's shorter. |
87 | | */ |
88 | 261k | char buf[8 * 5], *o = buf; |
89 | 261k | size_t best = 0, bestlen = 0, curlen = 0, i; |
90 | | |
91 | 261k | switch (af) { |
92 | 261k | case AF_INET: |
93 | 261k | inet4: |
94 | 261k | putbyte(b[0], &o); |
95 | 261k | *o++ = '.'; |
96 | 261k | putbyte(b[1], &o); |
97 | 261k | *o++ = '.'; |
98 | 261k | putbyte(b[2], &o); |
99 | 261k | *o++ = '.'; |
100 | 261k | putbyte(b[3], &o); |
101 | 261k | *o++ = '\0'; |
102 | 261k | break; |
103 | 0 | case AF_INET6: |
104 | 0 | for (i = 0; i < 8; i++) { |
105 | 0 | if (b[i * 2] || b[i * 2 + 1]) { |
106 | 0 | if (curlen && curlen > bestlen) { |
107 | 0 | best = i - curlen; |
108 | 0 | bestlen = curlen; |
109 | 0 | } |
110 | 0 | curlen = 0; |
111 | 0 | continue; |
112 | 0 | } |
113 | 0 | curlen++; |
114 | 0 | } |
115 | 0 | if (curlen && curlen > bestlen) { |
116 | 0 | best = i - curlen; |
117 | 0 | bestlen = curlen; |
118 | 0 | } |
119 | | /* do we want ::ffff:A.B.C.D? */ |
120 | 0 | if (best == 0 && bestlen == 6) { |
121 | 0 | *o++ = ':'; |
122 | 0 | *o++ = ':'; |
123 | 0 | b += 12; |
124 | 0 | goto inet4; |
125 | 0 | } |
126 | 0 | if (bestlen == 1) |
127 | 0 | bestlen = 0; |
128 | |
|
129 | 0 | for (i = 0; i < 8; i++) { |
130 | 0 | if (bestlen && i == best) { |
131 | 0 | if (i == 0) |
132 | 0 | *o++ = ':'; |
133 | 0 | *o++ = ':'; |
134 | 0 | continue; |
135 | 0 | } |
136 | 0 | if (i > best && i < best + bestlen) { |
137 | 0 | continue; |
138 | 0 | } |
139 | 0 | puthex((b[i * 2] << 8) | b[i * 2 + 1], &o); |
140 | |
|
141 | 0 | if (i < 7) |
142 | 0 | *o++ = ':'; |
143 | 0 | } |
144 | 0 | *o++ = '\0'; |
145 | 0 | break; |
146 | 0 | default: |
147 | 0 | return NULL; |
148 | 261k | } |
149 | | |
150 | 261k | i = o - buf; |
151 | 261k | if (i > size) |
152 | 0 | return NULL; |
153 | | /* compiler might inline memcpy if it knows the length is short, |
154 | | * although neither gcc nor clang actually do this currently [2019] |
155 | | */ |
156 | 261k | assume(i <= 8 * 5); |
157 | 261k | memcpy(dst, buf, i); |
158 | 261k | return dst; |
159 | 261k | } |
160 | | |
161 | | #if !defined(INET_NTOP_NO_OVERRIDE) && !defined(__APPLE__) |
162 | | /* we want to override libc inet_ntop, but make sure it shows up in backtraces |
163 | | * as frr_inet_ntop (to avoid confusion while debugging) |
164 | | */ |
165 | | const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) |
166 | | __attribute__((alias ("frr_inet_ntop"))); |
167 | | #endif |