1/*
2 * Copyright (c) 2002-2007, 2011, 2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * Modification History
26 *
27 * August 5, 2002	Allan Nathanson <ajn@apple.com>
28 * - initial revision
29 */
30
31
32#include "eventmon.h"
33#include "cache.h"
34#include "ev_ipv6.h"
35
36#define s6_addr16 __u6_addr.__u6_addr16
37
38#ifdef	NOTYET
39#ifndef	kSCPropNetIPv6ScopeID
40#define kSCPropNetIPv6ScopeID			SCSTR("ScopeID")
41#endif
42#endif	/* NOTYET */
43
44
45static void
46appendAddress(CFMutableDictionaryRef dict, CFStringRef key, struct sockaddr_in6 *sin6)
47{
48	CFStringRef		addr;
49	CFArrayRef		addrs;
50	CFMutableArrayRef	newAddrs;
51	char			str[64];
52
53	addrs = CFDictionaryGetValue(dict, key);
54	if (addrs) {
55		newAddrs = CFArrayCreateMutableCopy(NULL, 0, addrs);
56	} else {
57		newAddrs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
58	}
59
60	if (inet_ntop(AF_INET6, (const void *)&sin6->sin6_addr, str, sizeof(str)) == NULL) {
61		SCLog(TRUE, LOG_ERR, CFSTR("inet_ntop() failed: %s"), strerror(errno));
62		str[0] = '\0';
63	}
64
65	addr = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s"), str);
66	CFArrayAppendValue(newAddrs, addr);
67	CFRelease(addr);
68
69	CFDictionarySetValue(dict, key, newAddrs);
70	CFRelease(newAddrs);
71	return;
72}
73
74
75static void
76appendFlags(CFMutableDictionaryRef dict, int flags6)
77{
78	CFArrayRef		flags;
79	CFMutableArrayRef	newFlags;
80	CFNumberRef		v6Flags;
81
82	flags = CFDictionaryGetValue(dict, kSCPropNetIPv6Flags);
83	if (flags) {
84		newFlags = CFArrayCreateMutableCopy(NULL, 0, flags);
85	} else {
86		newFlags = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
87	}
88
89	v6Flags = CFNumberCreate(NULL, kCFNumberIntType, &flags6);
90	CFArrayAppendValue(newFlags, v6Flags);
91	CFRelease(v6Flags);
92
93	CFDictionarySetValue(dict, kSCPropNetIPv6Flags, newFlags);
94	CFRelease(newFlags);
95	return;
96}
97
98
99static void
100appendPrefixLen(CFMutableDictionaryRef dict, struct sockaddr_in6 *sin6)
101{
102	register u_int8_t	*name		= &sin6->sin6_addr.s6_addr[0];
103	CFNumberRef		prefixLen;
104	CFArrayRef		prefixLens;
105	CFMutableArrayRef	newPrefixLens;
106
107	register int		byte;
108	register int		bit;
109	int			plen		= 0;
110
111	for (byte = 0; byte < sizeof(struct in6_addr); byte++, plen += 8) {
112		if (name[byte] != 0xff) {
113			break;
114		}
115	}
116
117	if (byte == sizeof(struct in6_addr)) {
118		goto append;
119	}
120
121	for (bit = 7; bit != 0; bit--, plen++) {
122		if (!(name[byte] & (1 << bit))) {
123			break;
124		}
125	}
126
127	for (; bit != 0; bit--) {
128		if (name[byte] & (1 << bit)) {
129			plen = 0;
130			goto append;
131		}
132	}
133
134	byte++;
135	for (; byte < sizeof(struct in6_addr); byte++) {
136		if (name[byte]) {
137			plen = 0;
138			goto append;
139		}
140	}
141
142    append :
143
144	prefixLens = CFDictionaryGetValue(dict, kSCPropNetIPv6PrefixLength);
145	if (prefixLens) {
146		newPrefixLens = CFArrayCreateMutableCopy(NULL, 0, prefixLens);
147	} else {
148		newPrefixLens = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
149	}
150
151	prefixLen = CFNumberCreate(NULL, kCFNumberIntType, &plen);
152	CFArrayAppendValue(newPrefixLens, prefixLen);
153	CFRelease(prefixLen);
154
155	CFDictionarySetValue(dict, kSCPropNetIPv6PrefixLength, newPrefixLens);
156	CFRelease(newPrefixLens);
157	return;
158}
159
160
161#ifdef	NOTYET
162static void
163appendScopeID(CFMutableDictionaryRef dict, struct sockaddr_in6 *sin6)
164{
165	CFNumberRef		scope;
166	CFArrayRef		scopes;
167	CFMutableArrayRef	newScopes;
168
169	scopes = CFDictionaryGetValue(dict, kSCPropNetIPv6ScopeID);
170	if (scopes) {
171		newScopes = CFArrayCreateMutableCopy(NULL, 0, scopes);
172	} else {
173		newScopes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
174	}
175
176	scope = CFNumberCreate(NULL, kCFNumberSInt32Type, &sin6->sin6_scope_id);
177	CFArrayAppendValue(newScopes, scope);
178	CFRelease(scope);
179
180	CFDictionarySetValue(dict, kSCPropNetIPv6ScopeID, newScopes);
181	CFRelease(newScopes);
182	return;
183}
184#endif	/* NOTYET */
185
186
187static CFMutableDictionaryRef
188copyIF(CFStringRef key, CFMutableDictionaryRef oldIFs, CFMutableDictionaryRef newIFs)
189{
190	CFDictionaryRef		dict		= NULL;
191	CFMutableDictionaryRef	newDict		= NULL;
192
193	if (CFDictionaryGetValueIfPresent(newIFs, key, (const void **)&dict)) {
194		newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
195	} else {
196		dict = cache_SCDynamicStoreCopyValue(store, key);
197		if (dict) {
198			CFDictionarySetValue(oldIFs, key, dict);
199			if (isA_CFDictionary(dict)) {
200				newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
201				CFDictionaryRemoveValue(newDict, kSCPropNetIPv6Addresses);
202				CFDictionaryRemoveValue(newDict, kSCPropNetIPv6DestAddresses);
203				CFDictionaryRemoveValue(newDict, kSCPropNetIPv6Flags);
204				CFDictionaryRemoveValue(newDict, kSCPropNetIPv6PrefixLength);
205#ifdef	NOTYET
206				CFDictionaryRemoveValue(newDict, kSCPropNetIPv6ScopeID);
207#endif	/* NOTYET */
208			}
209			CFRelease(dict);
210		}
211	}
212
213	if (!newDict) {
214		newDict = CFDictionaryCreateMutable(NULL,
215						    0,
216						    &kCFTypeDictionaryKeyCallBacks,
217						    &kCFTypeDictionaryValueCallBacks);
218	}
219
220	return newDict;
221}
222
223
224static void
225updateStore(const void *key, const void *value, void *context)
226{
227	CFDictionaryRef dict;
228	CFDictionaryRef newDict = (CFDictionaryRef)value;
229	CFDictionaryRef	oldIFs	= (CFDictionaryRef)context;
230
231	dict = CFDictionaryGetValue(oldIFs, key);
232
233	if (!dict || !CFEqual(dict, newDict)) {
234		if (CFDictionaryGetCount(newDict) > 0) {
235			cache_SCDynamicStoreSetValue(store, key, newDict);
236		} else if (dict) {
237			cache_SCDynamicStoreRemoveValue(store, key);
238		}
239		network_changed = TRUE;
240	}
241
242	return;
243}
244
245
246__private_extern__
247void
248interface_update_ipv6(struct ifaddrs *ifap, const char *if_name)
249{
250	struct ifaddrs		*ifa;
251	struct ifaddrs		*ifap_temp	= NULL;
252	CFStringRef		interface;
253	boolean_t		interfaceFound	= FALSE;
254	CFStringRef		key		= NULL;
255	CFMutableDictionaryRef	oldIFs;
256	CFMutableDictionaryRef	newDict		= NULL;
257	CFMutableDictionaryRef	newIFs;
258	int			sock		= -1;
259
260	oldIFs = CFDictionaryCreateMutable(NULL,
261					   0,
262					   &kCFTypeDictionaryKeyCallBacks,
263					   &kCFTypeDictionaryValueCallBacks);
264	newIFs = CFDictionaryCreateMutable(NULL,
265					   0,
266					   &kCFTypeDictionaryKeyCallBacks,
267					   &kCFTypeDictionaryValueCallBacks);
268
269	if (!ifap) {
270		if (getifaddrs(&ifap_temp) == -1) {
271			SCLog(TRUE, LOG_ERR, CFSTR("getifaddrs() failed: %s"), strerror(errno));
272			goto error;
273		}
274		ifap = ifap_temp;
275	}
276
277	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
278		struct in6_ifreq	ifr6;
279#define	flags6	ifr6.ifr_ifru.ifru_flags6
280		struct sockaddr_in6	*sin6;
281
282		if (ifa->ifa_addr->sa_family != AF_INET6) {
283			continue;			/* sorry, not interested */
284		}
285
286		/* check if this is the requested interface */
287		if (if_name) {
288			if (strncmp(if_name, ifa->ifa_name, IFNAMSIZ) == 0) {
289				interfaceFound = TRUE;	/* yes, this is the one I want */
290			} else {
291				continue;		/* sorry, not interested */
292			}
293		}
294
295		if (sock == -1) {
296			sock = dgram_socket(AF_INET6);
297			if (sock == -1) {
298				SCLog(TRUE, LOG_NOTICE, CFSTR("interface_update_ipv6: socket open failed, %s"), strerror(errno));
299				goto error;
300			}
301		}
302
303		/* get the current cache information */
304		interface = CFStringCreateWithCString(NULL, ifa->ifa_name, kCFStringEncodingMacRoman);
305		key       = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
306									  kSCDynamicStoreDomainState,
307									  interface,
308									  kSCEntNetIPv6);
309		CFRelease(interface);
310
311		newDict = copyIF(key, oldIFs, newIFs);
312
313		/* ALIGN: ifa->ifa_addr aligned (getifaddrs), cast ok. */
314		sin6 = (struct sockaddr_in6 *)(void *)ifa->ifa_addr;
315
316		/* XXX: embedded link local addr check */
317		if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&sin6->sin6_addr)) {
318			u_int16_t	index;
319
320			index = sin6->sin6_addr.s6_addr16[1];
321			if (index != 0) {
322				sin6->sin6_addr.s6_addr16[1] = 0;
323				if (sin6->sin6_scope_id == 0) {
324					sin6->sin6_scope_id = ntohs(index);
325				}
326			}
327		}
328
329		bzero((char *)&ifr6, sizeof(ifr6));
330		strncpy(ifr6.ifr_name, ifa->ifa_name, sizeof(ifr6.ifr_name));
331		ifr6.ifr_addr = *sin6;
332		if (ioctl(sock, SIOCGIFAFLAG_IN6, &ifr6) == -1) {
333			/* if flags not available for this address */
334			SCLog(TRUE,
335			      (errno != EADDRNOTAVAIL) ? LOG_NOTICE : LOG_DEBUG,
336			      CFSTR("interface_update_ipv6: ioctl failed, %s"),
337			      strerror(errno));
338		}
339
340		appendAddress  (newDict, kSCPropNetIPv6Addresses, sin6);
341#ifdef	NOTYET
342		appendScopeID  (newDict, sin6);
343#endif	/* NOTYET */
344		/* ALIGN: ifa should be aligned (from getifaddrs), cast ok.
345		 * appendPrefixLen expect byte alignment */
346		appendPrefixLen(newDict, (struct sockaddr_in6 *)(void *)ifa->ifa_netmask);
347		appendFlags    (newDict, flags6);
348
349
350		if (ifa->ifa_flags & IFF_POINTOPOINT
351		    && ifa->ifa_dstaddr != NULL) {
352			struct sockaddr_in6	*dst6;
353
354			/* ALIGN: ifa should be aligned (from getifaddrs), cast ok. */
355			dst6 = (struct sockaddr_in6 *)(void *)ifa->ifa_dstaddr;
356
357			/* XXX: embedded link local addr check */
358			if (IN6_IS_ADDR_LINKLOCAL(&dst6->sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&dst6->sin6_addr)) {
359				u_int16_t	index;
360
361				index = dst6->sin6_addr.s6_addr16[1];
362				if (index != 0) {
363					dst6->sin6_addr.s6_addr16[1] = 0;
364					if (dst6->sin6_scope_id == 0) {
365						dst6->sin6_scope_id = ntohs(index);
366					}
367				}
368			}
369
370			appendAddress(newDict, kSCPropNetIPv6DestAddresses, dst6);
371		}
372
373		CFDictionarySetValue(newIFs, key, newDict);
374		CFRelease(newDict);
375		CFRelease(key);
376	}
377
378	/* if the last address[es] were removed from the target interface */
379	if (if_name && !interfaceFound) {
380		interface = CFStringCreateWithCString(NULL, if_name, kCFStringEncodingMacRoman);
381		key       = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
382									  kSCDynamicStoreDomainState,
383									  interface,
384									  kSCEntNetIPv6);
385		CFRelease(interface);
386
387		newDict = copyIF(key, oldIFs, newIFs);
388
389		CFDictionarySetValue(newIFs, key, newDict);
390		CFRelease(newDict);
391		CFRelease(key);
392	}
393
394	CFDictionaryApplyFunction(newIFs, updateStore, oldIFs);
395
396    error :
397
398	if (ifap_temp)	freeifaddrs(ifap_temp);
399	if (sock != -1)	close(sock);
400	CFRelease(oldIFs);
401	CFRelease(newIFs);
402
403	return;
404}
405