1 #include "massip-addr.h"
2 #include <string.h>
3 
4 
5 /**
6  * Holds the output string, so that we can append to it without
7  * overflowing buffers. The _append_xxx() functions below append
8  * to this string.
9  */
10 typedef struct stream_t {
11     char *buf;
12     size_t offset;
13     size_t length;
14 } stream_t;
15 
16 /**
17  * Append a character to the output string. All the other _append_xxx()
18  * functions call this one, so this is the only one where a
19  * buffer-overflow can occur.
20  */
21 static void
_append_char(stream_t * out,char c)22 _append_char(stream_t *out, char c)
23 {
24     if (out->offset < out->length)
25         out->buf[out->offset++] = c;
26 
27     /* keep the string nul terminated as we build it */
28     if (out->offset < out->length)
29         out->buf[out->offset] = '\0';
30 }
31 
32 static void
_append_ipv6(stream_t * out,const unsigned char * ipv6)33 _append_ipv6(stream_t *out, const unsigned char *ipv6)
34 {
35     static const char hex[] = "0123456789abcdef";
36     size_t i;
37     int is_ellision = 0;
38 
39     /* An IPv6 address is pritned as a series of 2-byte hex words
40      * separated by colons :, for a total of 16-bytes */
41     for (i = 0; i < 16; i += 2) {
42         unsigned n = ipv6[i] << 8 | ipv6[i + 1];
43 
44         /* Handle the ellision case. A series of words with a value
45          * of 0 can be removed completely, replaced by an extra colon */
46         if (n == 0 && !is_ellision) {
47             is_ellision = 1;
48             while (i < 16 && ipv6[i + 2] == 0 && ipv6[i + 3] == 0)
49                 i += 2;
50             _append_char(out, ':');
51 
52             /* test for all-zero address, in which case the output
53              * will be "::". */
54             if (i == 14)
55                 _append_char(out, ':');
56             continue;
57         }
58 
59         /* Print the colon between numbers. Fence-post alert: only colons
60          * between numbers are printed, not at the beginning or end of the
61          * stirng */
62         if (i)
63             _append_char(out, ':');
64 
65         /* Print the digits. Leading zeroes are not printed */
66         if (n >> 12)
67             _append_char(out, hex[(n >> 12) & 0xF]);
68         if (n >> 8)
69             _append_char(out, hex[(n >> 8) & 0xF]);
70         if (n >> 4)
71             _append_char(out, hex[(n >> 4) & 0xF]);
72         _append_char(out, hex[(n >> 0) & 0xF]);
73     }
74 }
75 
ipv6address_fmt(ipv6address a)76 struct ipaddress_formatted ipv6address_fmt(ipv6address a)
77 {
78     struct ipaddress_formatted out;
79     unsigned char tmp[16];
80     size_t i;
81     stream_t s;
82 
83     /*
84      * Convert address into a sequence of bytes. Our code
85      * here represents an IPv6 address as two 64-bit numbers, but
86      * the formatting code above that we copied from a diffent
87      * project represents it as an array of bytes.
88      */
89     for (i=0; i<16; i++) {
90         uint64_t x;
91         if (i<8)
92             x = a.hi;
93         else
94             x = a.lo;
95         x >>= (7 - (i%8)) * 8;
96 
97         tmp[i] = (unsigned char)(x & 0xFF);
98     }
99 
100     /* Call the formatting function */
101     s.buf = out.string;
102     s.offset = 0;
103     s.length = sizeof(out.string);
104     _append_ipv6(&s, tmp);
105 
106     return out;
107 }
108 
109 /**
110  * Append a decimal integer.
111  */
112 static void
_append_decimal(stream_t * out,unsigned long long n)113 _append_decimal(stream_t *out, unsigned long long n)
114 {
115     char tmp[64];
116     size_t tmp_offset = 0;
117 
118     /* Create temporary string */
119     while (n >= 10) {
120         unsigned digit = n % 10;
121         n /= 10;
122         tmp[tmp_offset++] = (char)('0' + digit);
123     }
124 
125     /* the final digit, may be zero */
126     tmp[tmp_offset++] = (char)('0' + n);
127 
128     /* Copy the result backwards */
129     while (tmp_offset)
130         _append_char(out, tmp[--tmp_offset]);
131 }
132 static void
_append_hex2(stream_t * out,unsigned long long n)133 _append_hex2(stream_t *out, unsigned long long n)
134 {
135     static const char hex[17] = "0123456789abcdef";
136 
137     _append_char(out, hex[(n>>4)&0xF]);
138     _append_char(out, hex[(n>>0)&0xF]);
139 }
140 
ipv4address_fmt(ipv4address ip)141 struct ipaddress_formatted ipv4address_fmt(ipv4address ip)
142 {
143     struct ipaddress_formatted out;
144     stream_t s;
145 
146 
147     /* Call the formatting function */
148     s.buf = out.string;
149     s.offset = 0;
150     s.length = sizeof(out.string);
151 
152     _append_decimal(&s, (ip >> 24) & 0xFF);
153     _append_char(&s, '.');
154     _append_decimal(&s, (ip >> 16) & 0xFF);
155     _append_char(&s, '.');
156     _append_decimal(&s, (ip >> 8) & 0xFF);
157     _append_char(&s, '.');
158     _append_decimal(&s, (ip >> 0) & 0xFF);
159 
160     return out;
161 }
162 
macaddress_fmt(macaddress_t mac)163 struct ipaddress_formatted macaddress_fmt(macaddress_t mac)
164 {
165     struct ipaddress_formatted out;
166     stream_t s;
167 
168 
169     /* Call the formatting function */
170     s.buf = out.string;
171     s.offset = 0;
172     s.length = sizeof(out.string);
173 
174     _append_hex2(&s, mac.addr[0]);
175     _append_char(&s, '-');
176     _append_hex2(&s, mac.addr[1]);
177     _append_char(&s, '-');
178     _append_hex2(&s, mac.addr[2]);
179     _append_char(&s, '-');
180     _append_hex2(&s, mac.addr[3]);
181     _append_char(&s, '-');
182     _append_hex2(&s, mac.addr[4]);
183     _append_char(&s, '-');
184     _append_hex2(&s, mac.addr[5]);
185 
186     return out;
187 }
188 
ipaddress_fmt(ipaddress a)189 struct ipaddress_formatted ipaddress_fmt(ipaddress a)
190 {
191     struct ipaddress_formatted out;
192     stream_t s;
193     ipv4address ip = a.ipv4;
194 
195     if (a.version == 6) {
196         return ipv6address_fmt(a.ipv6);
197     }
198 
199 
200     /* Call the formatting function */
201     s.buf = out.string;
202     s.offset = 0;
203     s.length = sizeof(out.string);
204 
205     _append_decimal(&s, (ip >> 24) & 0xFF);
206     _append_char(&s, '.');
207     _append_decimal(&s, (ip >> 16) & 0xFF);
208     _append_char(&s, '.');
209     _append_decimal(&s, (ip >> 8) & 0xFF);
210     _append_char(&s, '.');
211     _append_decimal(&s, (ip >> 0) & 0xFF);
212 
213     return out;
214 }
215 
216 
_count_long(uint64_t number)217 static unsigned _count_long(uint64_t number)
218 {
219     unsigned i;
220     unsigned count = 0;
221     for (i=0; i<64; i++) {
222         if ((number >> i) & 1)
223             count = i + 1;
224     }
225     return count;
226 }
227 
massint128_bitcount(massint128_t number)228 unsigned massint128_bitcount(massint128_t number)
229 {
230     if (number.hi)
231         return _count_long(number.hi) + 64;
232     else
233         return _count_long(number.lo);
234 }
235 
ipv6address_selftest(void)236 int ipv6address_selftest(void)
237 {
238     int x = 0;
239     ipaddress ip;
240 
241     ip.version = 4;
242     ip.ipv4 = 0x01FF00A3;
243 
244     if (strcmp(ipaddress_fmt(ip).string, "1.255.0.163") != 0)
245         x++;
246 
247     return x;
248 }
249 
ipv6address_is_equal_prefixed(ipv6address_t lhs,ipv6address_t rhs,unsigned prefix)250 int ipv6address_is_equal_prefixed(ipv6address_t lhs, ipv6address_t rhs, unsigned prefix)
251 {
252     ipv6address mask;
253 
254     /* If the prefix is bad, then the answer is 'no'. */
255     if (prefix > 128) {
256         return 0;
257     }
258 
259     /* Create the mask from the prefix */
260     if (prefix > 64)
261         mask.hi = ~0ULL;
262     else if (prefix == 0)
263         mask.hi = 0;
264     else
265         mask.hi = ~0ULL << (64 - prefix);
266 
267     if (prefix > 64)
268         mask.lo = ~0ULL << (128 - prefix);
269     else
270         mask.lo = 0;
271 
272     /* Mask off any non-zero bits from both addresses */
273     lhs.hi &= mask.hi;
274     lhs.lo &= mask.lo;
275     rhs.hi &= mask.hi;
276     rhs.lo &= mask.lo;
277 
278     /* Now do a normal compare */
279     return ipv6address_is_equal(lhs, rhs);
280 }
281