]>
Commit | Line | Data |
---|---|---|
e128c002 DL |
1 | /* |
2 | * optimized ntop, about 10x faster than libc versions [as of 2019] | |
3 | * | |
4 | * Copyright (c) 2019 David Lamparter, for NetDEF, Inc. | |
5 | * | |
6 | * Permission to use, copy, modify, and distribute this software for any | |
7 | * purpose with or without fee is hereby granted, provided that the above | |
8 | * copyright notice and this permission notice appear in all copies. | |
9 | * | |
10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 | */ | |
18 | ||
19 | #ifdef HAVE_CONFIG_H | |
20 | #include "config.h" | |
21 | #endif | |
22 | ||
23 | #include <stdio.h> | |
24 | #include <stdint.h> | |
25 | #include <stdbool.h> | |
26 | #include <string.h> | |
27 | #include <sys/socket.h> | |
28 | #include <netinet/in.h> | |
29 | #include <arpa/inet.h> | |
30 | ||
31 | #include "compiler.h" | |
32 | ||
33 | #define pos (*posx) | |
34 | ||
35 | static inline void putbyte(uint8_t bytex, char **posx) | |
36 | __attribute__((always_inline)) OPTIMIZE; | |
37 | ||
38 | static inline void putbyte(uint8_t bytex, char **posx) | |
39 | { | |
40 | bool zero = false; | |
41 | int byte = bytex, tmp, a, b; | |
42 | ||
43 | if ((tmp = byte - 200) >= 0) { | |
44 | *pos++ = '2'; | |
45 | zero = true; | |
46 | byte = tmp; | |
47 | } else if ((tmp = byte - 100) >= 0) { | |
48 | *pos++ = '1'; | |
49 | zero = true; | |
50 | byte = tmp; | |
51 | } | |
52 | ||
53 | /* make sure the compiler knows the value range of "byte" */ | |
54 | assume(byte < 100 && byte >= 0); | |
55 | ||
56 | b = byte % 10; | |
57 | a = byte / 10; | |
58 | if (a || zero) { | |
59 | *pos++ = '0' + a; | |
60 | *pos++ = '0' + b; | |
61 | } else | |
62 | *pos++ = '0' + b; | |
63 | } | |
64 | ||
65 | static inline void puthex(uint16_t word, char **posx) | |
66 | __attribute__((always_inline)) OPTIMIZE; | |
67 | ||
68 | static inline void puthex(uint16_t word, char **posx) | |
69 | { | |
70 | const char *digits = "0123456789abcdef"; | |
71 | if (word >= 0x1000) | |
72 | *pos++ = digits[(word >> 12) & 0xf]; | |
73 | if (word >= 0x100) | |
74 | *pos++ = digits[(word >> 8) & 0xf]; | |
75 | if (word >= 0x10) | |
76 | *pos++ = digits[(word >> 4) & 0xf]; | |
77 | *pos++ = digits[word & 0xf]; | |
78 | } | |
79 | ||
80 | #undef pos | |
81 | ||
82 | const char *frr_inet_ntop(int af, const void * restrict src, | |
83 | char * restrict dst, socklen_t size) | |
866f6e40 | 84 | __attribute__((flatten)) OPTIMIZE; |
e128c002 DL |
85 | |
86 | const char *frr_inet_ntop(int af, const void * restrict src, | |
87 | char * restrict dst, socklen_t size) | |
88 | { | |
89 | const uint8_t *b = src; | |
90 | /* 8 * "abcd:" for IPv6 | |
91 | * note: the IPv4-embedded IPv6 syntax is only used for ::A.B.C.D, | |
92 | * which isn't longer than 40 chars either. even with ::ffff:A.B.C.D | |
93 | * it's shorter. | |
94 | */ | |
95 | char buf[8 * 5], *o = buf; | |
96 | size_t best = 0, bestlen = 0, curlen = 0, i; | |
97 | ||
98 | switch (af) { | |
99 | case AF_INET: | |
100 | inet4: | |
101 | putbyte(b[0], &o); | |
102 | *o++ = '.'; | |
103 | putbyte(b[1], &o); | |
104 | *o++ = '.'; | |
105 | putbyte(b[2], &o); | |
106 | *o++ = '.'; | |
107 | putbyte(b[3], &o); | |
108 | *o++ = '\0'; | |
109 | break; | |
110 | case AF_INET6: | |
111 | for (i = 0; i < 8; i++) { | |
112 | if (b[i * 2] || b[i * 2 + 1]) { | |
113 | if (curlen && curlen > bestlen) { | |
114 | best = i - curlen; | |
115 | bestlen = curlen; | |
116 | } | |
117 | curlen = 0; | |
118 | continue; | |
119 | } | |
120 | curlen++; | |
121 | } | |
122 | if (curlen && curlen > bestlen) { | |
123 | best = i - curlen; | |
124 | bestlen = curlen; | |
125 | } | |
126 | /* do we want ::ffff:A.B.C.D? */ | |
127 | if (best == 0 && bestlen == 6) { | |
128 | *o++ = ':'; | |
129 | *o++ = ':'; | |
130 | b += 12; | |
131 | goto inet4; | |
132 | } | |
133 | if (bestlen == 1) | |
134 | bestlen = 0; | |
135 | ||
136 | for (i = 0; i < 8; i++) { | |
137 | if (bestlen && i == best) { | |
138 | if (i == 0) | |
139 | *o++ = ':'; | |
140 | *o++ = ':'; | |
141 | continue; | |
142 | } | |
143 | if (i > best && i < best + bestlen) { | |
144 | continue; | |
145 | } | |
146 | puthex((b[i * 2] << 8) | b[i * 2 + 1], &o); | |
147 | ||
148 | if (i < 7) | |
149 | *o++ = ':'; | |
150 | } | |
151 | *o++ = '\0'; | |
152 | break; | |
153 | default: | |
154 | return NULL; | |
155 | } | |
156 | ||
157 | i = o - buf; | |
158 | if (i > size) | |
159 | return NULL; | |
160 | /* compiler might inline memcpy if it knows the length is short, | |
161 | * although neither gcc nor clang actually do this currently [2019] | |
162 | */ | |
163 | assume(i <= 8 * 5); | |
164 | memcpy(dst, buf, i); | |
165 | return dst; | |
166 | } | |
167 | ||
8d80a842 | 168 | #if !defined(INET_NTOP_NO_OVERRIDE) && !defined(__APPLE__) |
e128c002 DL |
169 | /* we want to override libc inet_ntop, but make sure it shows up in backtraces |
170 | * as frr_inet_ntop (to avoid confusion while debugging) | |
171 | */ | |
172 | const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) | |
866f6e40 | 173 | __attribute__((alias ("frr_inet_ntop"))); |
874035be | 174 | #endif |