1 /*
2  * Project    : ipv6calc
3  * File       : libeui64.c
4  * Version    : $Id: ac04b718a59935f2d7430dc1deed00c86386bb68 $
5  * Copyright  : 2001-2019 by Peter Bieringer <pb (at) bieringer.de>
6  *
7  * Information:
8  *  Function library EUI-64 identifier handling
9  */
10 
11 #include <stdio.h>
12 #include <string.h>
13 #include <stdlib.h>
14 
15 #include "libeui64.h"
16 #include "libieee.h"
17 
18 #include "libipv6calc.h"
19 #include "libipv6calcdebug.h"
20 #include "libipv6addr.h"
21 
22 static char ChSet[] = "0123456789abcdefABCDEF:- ";
23 
24 
25 /* function MAC address to EUI format
26  *
27  * in : macaddrp
28  * out: ipv6addrp
29  * ret: ==0: ok, !=0: error
30  */
create_eui64_from_mac(ipv6calc_ipv6addr * ipv6addrp,ipv6calc_macaddr * macaddrp)31 int create_eui64_from_mac(ipv6calc_ipv6addr *ipv6addrp, ipv6calc_macaddr *macaddrp) {
32 	int retval = 1;
33 
34 	DEBUGPRINT_NA(DEBUG_libeui64, "called");
35 
36 	/* clear IPv6 structure */
37 	ipv6addr_clear(ipv6addrp);
38 
39 	/* create EUI-64 from MAC-48 */
40 	ipv6addrp->in6_addr.s6_addr[ 8] = macaddrp->addr[0] ^ 0x02;
41    	ipv6addrp->in6_addr.s6_addr[ 9] = macaddrp->addr[1];
42    	ipv6addrp->in6_addr.s6_addr[10] = macaddrp->addr[2];
43    	ipv6addrp->in6_addr.s6_addr[11] = 0xff;
44 	ipv6addrp->in6_addr.s6_addr[12] = 0xfe;
45    	ipv6addrp->in6_addr.s6_addr[13] = macaddrp->addr[3];
46    	ipv6addrp->in6_addr.s6_addr[14] = macaddrp->addr[4];
47    	ipv6addrp->in6_addr.s6_addr[15] = macaddrp->addr[5];
48 
49 	ipv6addrp->prefixlength = 64;
50 	ipv6addrp->flag_prefixuse = 0;
51 
52 	ipv6addrp->flag_valid = 1;
53 
54    	retval = 0;
55 	return (retval);
56 };
57 
58 
59 /*
60  * stores the EUI-64 structure in a string
61  *
62  * in:  eui64addr_p = EUI-64 address structure ptr
63  * out: *resultstring = EUI-64 address string
64  * ret: ==0: ok, !=0: error
65  */
libeui64_eui64addrstruct_to_string(const ipv6calc_eui64addr * eui64addr_p,char * resultstring,const size_t resultstring_length,const uint32_t formatoptions)66 int libeui64_eui64addrstruct_to_string(const ipv6calc_eui64addr *eui64addr_p, char *resultstring, const size_t resultstring_length, const uint32_t formatoptions) {
67 	char tempstring[IPV6CALC_STRING_MAX];
68 
69 	/* address */
70 	snprintf(tempstring, sizeof(tempstring), "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", \
71 		(unsigned int) eui64addr_p->addr[0],	\
72 		(unsigned int) eui64addr_p->addr[1],	\
73 		(unsigned int) eui64addr_p->addr[2],	\
74 		(unsigned int) eui64addr_p->addr[3],	\
75 		(unsigned int) eui64addr_p->addr[4],	\
76 		(unsigned int) eui64addr_p->addr[5],	\
77 		(unsigned int) eui64addr_p->addr[6],	\
78 		(unsigned int) eui64addr_p->addr[7]);
79 
80 	if ( (formatoptions & FORMATOPTION_machinereadable) != 0 ) {
81 		snprintf(resultstring, resultstring_length, "EUI64=%s", tempstring);
82 	} else {
83 		snprintf(resultstring, resultstring_length, "%s", tempstring);
84 	};
85 
86 	return(0);
87 };
88 
89 
90 /* function 48-bit EUI-64 address to eui64addr_structure
91  *
92  * in : *addrstring = EUI-64 address
93  * out: *resultstring = result
94  * ret: ==0: ok, !=0: error
95  */
libeui64_addr_to_eui64addrstruct(const char * addrstring,char * resultstring,const size_t resultstring_length,ipv6calc_eui64addr * eui64addrp)96 int libeui64_addr_to_eui64addrstruct(const char *addrstring, char *resultstring, const size_t resultstring_length, ipv6calc_eui64addr *eui64addrp) {
97 	int retval = 1, result, i, ccolons = 0, cdashes = 0, cspaces = 0;
98 	size_t cnt;
99 	int temp[8];
100 
101 	DEBUGPRINT_NA(DEBUG_libeui64, "called");
102 
103 	/* check length */
104 	if ( ( strlen(addrstring) < 15 ) || ( strlen(addrstring) > 23 ) ) {
105 		snprintf(resultstring, resultstring_length, "Error in given 64-bit EUI-64 address, has not 15 to 23 chars!");
106 		retval = 1;
107 		return (retval);
108 	};
109 
110 	/* check for hex chars and ":"/"-"/" " only content */
111 	cnt = strspn(addrstring, ChSet);
112 	if ( cnt < strlen(addrstring) ) {
113 		snprintf(resultstring, resultstring_length, "Error in given EUI-64 address, '%s' has illegal char on position %d (%c)!", addrstring, (int) cnt+1, addrstring[cnt]);
114 		retval = 1;
115 		return (retval);
116 
117 	};
118 
119 	/* count ":" or "-" or " " must be 7 x "-" */
120 	for (i = 0; i < (int) strlen(addrstring); i++) {
121 		if (addrstring[i] == ':') {
122 			ccolons++;
123 		} else if (addrstring[i] == '-') {
124 			cdashes++;
125 		} else if (addrstring[i] == ' ') {
126 			cspaces++;
127 		};
128 	};
129 
130 	if ( ! ( (ccolons == 7 && cdashes == 0 && cspaces == 0) || (ccolons == 0 && cdashes == 7 && cspaces == 0)  || (ccolons == 0 && cdashes == 0 && cspaces == 7) || (ccolons == 0 && cdashes == 0 && cspaces == 0 && strlen(addrstring) == 16)) ) {
131 		snprintf(resultstring, resultstring_length, "Error in given EUI-64 address, '%s' is not valid (number of colons/dashes/spaces is not 7)!", addrstring);
132 		retval = 1;
133 		return (retval);
134 	};
135 
136 	/* scan address into array */
137 	if ( ccolons == 7 ) {
138 		result = sscanf(addrstring, "%x:%x:%x:%x:%x:%x:%x:%x", &temp[0], &temp[1], &temp[2], &temp[3], &temp[4], &temp[5], &temp[6], &temp[7]);
139 	} else if ( cdashes == 7 ) {
140 		result = sscanf(addrstring, "%x-%x-%x-%x-%x-%x-%x-%x", &temp[0], &temp[1], &temp[2], &temp[3], &temp[4], &temp[5], &temp[6], &temp[7]);
141 	} else if ( cspaces == 7 ) {
142 		result = sscanf(addrstring, "%x %x %x %x %x %x %x %x", &temp[0], &temp[1], &temp[2], &temp[3], &temp[4], &temp[5], &temp[6], &temp[7]);
143 	} else if ( cdashes == 0 ) {
144 		result = sscanf(addrstring, "%2x%2x%2x%2x%2x%2x%2x%2x", &temp[0], &temp[1], &temp[2], &temp[3], &temp[4], &temp[5], &temp[6], &temp[7]);
145 	} else {
146 		snprintf(resultstring, resultstring_length, "Error in given EUI-64 address, unexpected failure on scanning '%s'!", addrstring);
147 		retval = 1;
148 		return (retval);
149 	};
150 
151 	if ( result != 8 ) {
152 		snprintf(resultstring, resultstring_length, "Error in given EUI-64 address, splitting of '%s' return %d items instead of 8!", addrstring, result);
153 		retval = 1;
154 		return (retval);
155 	};
156 
157 	/* check address words range */
158 	for ( i = 0; i <= 7; i++ ) {
159 		if ( ( temp[i] < 0x0 ) || ( temp[i] > 0xff ) )    {
160 			snprintf(resultstring, resultstring_length, "Error in given EUI-64 address, '%s' is not valid on position %d!", addrstring, i);
161 			retval = 1;
162 			return (retval);
163 		};
164 	};
165 
166 	/* copy address */
167 	for ( i = 0; i <= 7; i++ ) {
168 		eui64addrp->addr[i] = (uint8_t) temp[i];
169 	};
170 
171 	eui64addrp->flag_valid = 1;
172 
173    	retval = 0;
174 	return (retval);
175 };
176 
177 
178 /*
179  * clear EUI-64 addr
180  *
181  * mod: *addrstring = EUI-64 address
182  */
libeui64_clear(ipv6calc_eui64addr * eui64addrp)183 void libeui64_clear(ipv6calc_eui64addr *eui64addrp) {
184 	int i;
185 
186 	DEBUGPRINT_NA(DEBUG_libeui64, "called");
187 
188 	for ( i = 0; i <= 7; i++ ) {
189 		eui64addrp->addr[i] = 0;
190 	};
191 
192 	return;
193 };
194 
195 
196 /*
197  * clear EUI64 addr_structure
198  *
199  * mod: *addrstring = EUI64 address
200  */
libeui64_clearall(ipv6calc_eui64addr * eui64addrp)201 void libeui64_clearall(ipv6calc_eui64addr *eui64addrp) {
202 	libeui64_clear(eui64addrp);
203 
204 	DEBUGPRINT_NA(DEBUG_libeui64, "called");
205 
206 	/* Clear valid flag */
207 	eui64addrp->flag_valid = 0;
208 
209 	return;
210 };
211 
212 
213 /*
214  * anonymize EUI-64 addr
215  *
216  * mod: *addrstring = EUI-64 address
217  */
libeui64_anonymize(ipv6calc_eui64addr * eui64addrp,const s_ipv6calc_anon_set * ipv6calc_anon_set_p)218 void libeui64_anonymize(ipv6calc_eui64addr *eui64addrp, const s_ipv6calc_anon_set *ipv6calc_anon_set_p) {
219 	int mask = 0, i, j;
220 	uint8_t bit_ul = 0;
221 
222 	DEBUGPRINT_WA(DEBUG_libeui64, "called: EUI-64=%08x%08x method=%d", EUI64_00_31(eui64addrp->addr), EUI64_32_63(eui64addrp->addr), ipv6calc_anon_set_p->method);
223 
224 	// if (ipv6calc_anon_set_p->method == ANON_METHOD_ZEROIZE) { TODO: different implementations
225 		if (ipv6calc_anon_set_p->mask_autoadjust == 1) {
226 			DEBUGPRINT_NA(DEBUG_libeui64, "mask-autoadjust is set, autoselect proper mask");
227 
228 			if ((eui64addrp->addr[0] & 0x2) == 0) {
229 				// global address
230 				if ((eui64addrp->addr[3] == 0xff) && (eui64addrp->addr[4] == 0xfe)) {
231 					// expanded EUI-48
232 					mask = 40; // 24 + 16 bits
233 					DEBUGPRINT_WA(DEBUG_libeui64, "EUI-64 is a expanded EUI-48, change mask: %d", mask);
234 				} else {
235 					mask = 24;
236 				};
237 
238 				if (libieee_check_oui36_iab(EUI64_00_23(eui64addrp->addr)) == 1) {
239 					// OUI-36/IAB
240 					mask += 12; // increase by 12 bits
241 					DEBUGPRINT_WA(DEBUG_libeui64, "EUI-64 contains OUI-36/IAB, change mask: %d", mask);
242 				} else if (libieee_check_oui28(EUI64_00_23(eui64addrp->addr)) == 1) {
243 					// OUI-28
244 					mask += 4; // increase by 4 bits
245 					DEBUGPRINT_WA(DEBUG_libeui64, "EUI-64 contains OUI-28, change mask: %d", mask);
246 				};
247 
248 				DEBUGPRINT_WA(DEBUG_libeui64, "EUI-64 is a global one, source of mask: automagic: %d", mask);
249 			} else {
250 				// local address, honor mask_eui64
251 				mask = ipv6calc_anon_set_p->mask_eui64;
252 				DEBUGPRINT_WA(DEBUG_libeui64, "EUI-64 is a local one, source of mask: mask-iid option: %d", mask);
253 			};
254 
255 			if (ipv6calc_anon_set_p->mask_eui64 > mask) {
256 				mask = ipv6calc_anon_set_p->mask_eui64;
257 				DEBUGPRINT_WA(DEBUG_libeui64, "specified mask is higher than autoselected one, change to specified: %d", mask);
258 			};
259 		} else {
260 			DEBUGPRINT_WA(DEBUG_libeui64, "mask-autoadjust is not set, use always given mask: %d", mask);
261 			mask = ipv6calc_anon_set_p->mask_eui64;
262 		};
263 
264 		// save universal/local bit
265 		bit_ul = eui64addrp->addr[0] & 0x02;
266 
267 		DEBUGPRINT_WA(DEBUG_libeui64, "zeroize EUI-64 with masked bits: %d (u/l=%s)", mask, (bit_ul == 2) ? "local" : "universal");
268 
269 		if (mask == 64) {
270 			// nothing to do
271 		} else if (mask > 0) {
272 			j = mask >> 3;
273 
274 			for (i = 7; i >= 0; i--) {
275 				DEBUGPRINT_WA(DEBUG_libeui64, "zeroize EUI-64: mask=%02d i=%d j=%d", mask, i, j);
276 				if (j < i) {
277 					DEBUGPRINT_WA(DEBUG_libeui64, "zeroize EUI-64: byte %d", i);
278 					eui64addrp->addr[i] = 0x00;
279 				} else if (j == i) {
280 					DEBUGPRINT_WA(DEBUG_libeui64, "zeroize EUI-64: mask byte %d with %02x (offset: %d)", i, (0xff00 >> (mask % 0x8)) & 0xff, (mask % 0x8));
281 					eui64addrp->addr[i] &= (0xff00 >> (mask % 0x8)) & 0xff;
282 				} else {
283 					DEBUGPRINT_NA(DEBUG_libeui64, "zeroize EUI-64: finished");
284 					break;
285 				};
286 			};
287 		} else {
288 			libeui64_clear(eui64addrp);
289 		};
290 
291 		// restore universal/local bit
292 		eui64addrp->addr[0] = (eui64addrp->addr[0] & 0xfd) | bit_ul;
293 	// };
294 
295 	DEBUGPRINT_WA(DEBUG_libeui64, "anonymization finished, return: %08x%08x", EUI64_00_31(eui64addrp->addr), EUI64_32_63(eui64addrp->addr));
296 
297 	return;
298 };
299