Line data Source code
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 : #define pos (*posx)
23 :
24 : static inline void putbyte(uint8_t bytex, char **posx)
25 : __attribute__((always_inline)) OPTIMIZE;
26 :
27 708 : static inline void putbyte(uint8_t bytex, char **posx)
28 : {
29 708 : bool zero = false;
30 708 : int byte = bytex, tmp, a, b;
31 :
32 708 : tmp = byte - 200;
33 708 : if (tmp >= 0) {
34 79 : *pos++ = '2';
35 79 : zero = true;
36 79 : byte = tmp;
37 : } else {
38 629 : tmp = byte - 100;
39 629 : if (tmp >= 0) {
40 79 : *pos++ = '1';
41 79 : zero = true;
42 79 : byte = tmp;
43 : }
44 : }
45 :
46 : /* make sure the compiler knows the value range of "byte" */
47 708 : assume(byte < 100 && byte >= 0);
48 :
49 708 : b = byte % 10;
50 708 : a = byte / 10;
51 708 : if (a || zero) {
52 316 : *pos++ = '0' + a;
53 316 : *pos++ = '0' + b;
54 : } else
55 392 : *pos++ = '0' + b;
56 : }
57 :
58 : static inline void puthex(uint16_t word, char **posx)
59 : __attribute__((always_inline)) OPTIMIZE;
60 :
61 972 : static inline void puthex(uint16_t word, char **posx)
62 : {
63 972 : const char *digits = "0123456789abcdef";
64 972 : if (word >= 0x1000)
65 557 : *pos++ = digits[(word >> 12) & 0xf];
66 972 : if (word >= 0x100)
67 678 : *pos++ = digits[(word >> 8) & 0xf];
68 972 : if (word >= 0x10)
69 799 : *pos++ = digits[(word >> 4) & 0xf];
70 972 : *pos++ = digits[word & 0xf];
71 : }
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 502 : const char *frr_inet_ntop(int af, const void * restrict src,
80 : char * restrict dst, socklen_t size)
81 : {
82 502 : 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 502 : char buf[8 * 5], *o = buf;
89 502 : size_t best = 0, bestlen = 0, curlen = 0, i;
90 :
91 502 : switch (af) {
92 : case AF_INET:
93 177 : inet4:
94 177 : putbyte(b[0], &o);
95 177 : *o++ = '.';
96 177 : putbyte(b[1], &o);
97 177 : *o++ = '.';
98 177 : putbyte(b[2], &o);
99 177 : *o++ = '.';
100 177 : putbyte(b[3], &o);
101 177 : *o++ = '\0';
102 177 : break;
103 : case AF_INET6:
104 2925 : for (i = 0; i < 8; i++) {
105 2600 : if (b[i * 2] || b[i * 2 + 1]) {
106 972 : if (curlen && curlen > bestlen) {
107 143 : best = i - curlen;
108 143 : bestlen = curlen;
109 : }
110 972 : curlen = 0;
111 972 : continue;
112 : }
113 1628 : curlen++;
114 : }
115 325 : if (curlen && curlen > bestlen) {
116 182 : best = i - curlen;
117 182 : bestlen = curlen;
118 : }
119 : /* do we want ::ffff:A.B.C.D? */
120 325 : if (best == 0 && bestlen == 6) {
121 0 : *o++ = ':';
122 0 : *o++ = ':';
123 0 : b += 12;
124 0 : goto inet4;
125 : }
126 325 : if (bestlen == 1)
127 0 : bestlen = 0;
128 :
129 2925 : for (i = 0; i < 8; i++) {
130 2600 : if (bestlen && i == best) {
131 325 : if (i == 0)
132 10 : *o++ = ':';
133 325 : *o++ = ':';
134 325 : continue;
135 : }
136 2275 : if (i > best && i < best + bestlen) {
137 1303 : continue;
138 : }
139 972 : puthex((b[i * 2] << 8) | b[i * 2 + 1], &o);
140 :
141 972 : if (i < 7)
142 829 : *o++ = ':';
143 : }
144 325 : *o++ = '\0';
145 325 : break;
146 : default:
147 : return NULL;
148 : }
149 :
150 502 : i = o - buf;
151 502 : if (i > size)
152 : 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 502 : assume(i <= 8 * 5);
157 502 : memcpy(dst, buf, i);
158 502 : return dst;
159 : }
160 :
161 : #if !defined(INET_NTOP_NO_OVERRIDE)
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
|