1/*
2 * Copyright (c) 2014 Apple Inc.
3 * All rights reserved.
4 */
5#include <syslog.h>
6#include <sys/ioctl.h>
7#include <net/if.h>
8#include <netinet/in.h>
9#include <sys/socket.h>
10#include <arpa/inet.h>
11#include <mach/mach_time.h>
12#include <netinet/in_var.h>
13#include <xpc/private.h>
14#include <SystemConfiguration/SystemConfiguration.h>
15#include <SystemConfiguration/SNHelperPrivate.h>
16
17#include "ne_sm_bridge_private.h"
18
19#include "scnc_main.h"
20#include "scnc_client.h"
21#include "ipsec_manager.h"
22#include "ppp_manager.h"
23
24extern TAILQ_HEAD(, service) service_head; /* Defined in scnc_main.c */
25extern CFBundleRef gBundleRef; /* Defined in scnc_main.c */
26
27struct _ne_sm_bridge {
28	ne_sm_bridge_type_t type;
29	struct service serv;
30	void *info;
31	void (^disposable_callback)(void);
32};
33
34static struct ne_sm_bridge_callbacks *g_callbacks = NULL;
35
36static void
37ne_sm_bridge_log(int level, CFStringRef format, ...)
38{
39	va_list args;
40	va_start(args, format);
41	ne_sm_bridge_logv(level, format, args);
42	va_end(args);
43}
44
45bool
46ne_sm_bridge_logv(int level, CFStringRef format, va_list args)
47{
48	if (g_callbacks != NULL && g_callbacks->log != NULL) {
49		g_callbacks->log(level, format, args);
50		return true;
51	} else {
52		return false;
53	}
54}
55
56bool
57ne_sm_bridge_is_logging_at_level(int level)
58{
59	if (g_callbacks != NULL && g_callbacks->is_logging_at_level != NULL) {
60		return g_callbacks->is_logging_at_level(level);
61	} else {
62		return true;
63	}
64}
65
66CFDictionaryRef
67ne_sm_bridge_copy_configuration(ne_sm_bridge_t bridge)
68{
69	if (g_callbacks != NULL && g_callbacks->copy_service_configuration != NULL) {
70		return g_callbacks->copy_service_configuration(bridge->info);
71	} else {
72		return NULL;
73	}
74}
75
76void
77ne_sm_bridge_status_changed(ne_sm_bridge_t bridge)
78{
79	if (g_callbacks != NULL && g_callbacks->status_changed != NULL) {
80		g_callbacks->status_changed(bridge->info, scnc_getstatus(&bridge->serv));
81	}
82}
83
84void
85ne_sm_bridge_acknowledge_sleep(ne_sm_bridge_t bridge)
86{
87	if (g_callbacks != NULL && g_callbacks->acknowledge_sleep != NULL) {
88		g_callbacks->acknowledge_sleep(bridge->info);
89	}
90}
91
92void
93ne_sm_bridge_filter_state_dictionaries(ne_sm_bridge_t bridge, CFMutableArrayRef names, CFMutableArrayRef dictionaries)
94{
95	if (g_callbacks != NULL && g_callbacks->filter_state_dictionaries != NULL) {
96		g_callbacks->filter_state_dictionaries(bridge->info, names, dictionaries);
97	}
98}
99
100CFStringRef
101ne_sm_bridge_copy_password_from_keychain(ne_sm_bridge_t bridge, CFStringRef type)
102{
103	if (g_callbacks != NULL && g_callbacks->copy_password_from_keychain != NULL) {
104		return g_callbacks->copy_password_from_keychain(bridge->info, type);
105	}
106	return NULL;
107}
108
109void
110ne_sm_bridge_allow_dispose(ne_sm_bridge_t bridge)
111{
112	if (bridge->disposable_callback != NULL) {
113		bridge->disposable_callback();
114		Block_release(bridge->disposable_callback);
115		bridge->disposable_callback = NULL;
116	}
117}
118
119uint64_t
120ne_sm_bridge_get_connect_time(ne_sm_bridge_t bridge)
121{
122	if (g_callbacks != NULL && g_callbacks->get_connect_time != NULL) {
123		return g_callbacks->get_connect_time(bridge->info);
124	}
125	return 0;
126}
127
128bool
129ne_sm_bridge_request_install(ne_sm_bridge_t bridge, bool exclusive)
130{
131	if (g_callbacks != NULL && g_callbacks->request_install != NULL) {
132		g_callbacks->request_install(bridge->info, exclusive);
133		return true;
134	}
135	return false;
136}
137
138bool
139ne_sm_bridge_request_uninstall(ne_sm_bridge_t bridge)
140{
141	if (g_callbacks != NULL && g_callbacks->request_uninstall != NULL) {
142		g_callbacks->request_uninstall(bridge->info);
143		return true;
144	}
145	return false;
146}
147
148bool
149ne_sm_bridge_start_profile_janitor(ne_sm_bridge_t bridge, CFStringRef profileIdentifier)
150{
151	char profileIdentifierStr[256];
152	if (profileIdentifier == NULL || !CFStringGetCString(profileIdentifier, profileIdentifierStr, sizeof(profileIdentifierStr), kCFStringEncodingUTF8)) {
153		return false;
154	}
155
156	if (g_callbacks != NULL && g_callbacks->start_profile_janitor != NULL) {
157		g_callbacks->start_profile_janitor(bridge->info, profileIdentifierStr);
158		return true;
159	}
160	return false;
161}
162
163void
164ne_sm_bridge_clear_saved_password(ne_sm_bridge_t bridge, CFStringRef type)
165{
166#if NE_SM_BRIDGE_VERSION > 2
167	if (g_callbacks != NULL && g_callbacks->clear_saved_password != NULL) {
168		g_callbacks->clear_saved_password(bridge->info, type);
169	}
170#else
171#pragma unused(bridge, type)
172#endif
173}
174
175static bool
176init_controller(void)
177{
178	static dispatch_once_t controller_once = 0;
179	static bool success = false;
180
181	dispatch_once(&controller_once,
182		^{
183			mach_timebase_info_data_t timebaseInfo;
184			const CFStringRef scdOptionsKeys[] = { kSCDynamicStoreUseSessionKeys };
185			const CFBooleanRef scdOptionsValues[] = { kCFBooleanTrue };
186			CFDictionaryRef scdOptions = NULL;
187
188			scnc_init_resources(gBundleRef);
189
190			/* Initialize time scale */
191			if (mach_timebase_info(&timebaseInfo) != KERN_SUCCESS) {
192				ne_sm_bridge_log(LOG_ERR, CFSTR("init_controller: mach_timebase_info failed"));
193				goto fail;
194			}
195			gTimeScaleSeconds = ((double) timebaseInfo.numer / (double) timebaseInfo.denom) / 1000000000;
196
197			scdOptions = CFDictionaryCreate(kCFAllocatorDefault,
198			                                (const void **)scdOptionsKeys,
199			                                (const void **)scdOptionsValues,
200			                                sizeof(scdOptionsValues) / sizeof(scdOptionsValues[0]),
201			                                &kCFTypeDictionaryKeyCallBacks,
202			                                &kCFTypeDictionaryValueCallBacks);
203
204			gDynamicStore = SCDynamicStoreCreateWithOptions(kCFAllocatorDefault, CFSTR("NE - SCNC bridge"), scdOptions, NULL, NULL);
205			if (gDynamicStore == NULL) {
206				ne_sm_bridge_log(LOG_ERR, CFSTR("init_controller: SCDynamicStoreCreateWithOptions failed: %s"), SCErrorString(SCError()));
207				goto fail;
208			}
209
210			TAILQ_INIT(&service_head);
211			client_init_all();
212			ipsec_init_things();
213
214			success = true;
215fail:
216			if (scdOptions != NULL) {
217				CFRelease(scdOptions);
218			}
219		});
220
221	return success;
222}
223
224static void
225bridge_destroy(ne_sm_bridge_t bridge)
226{
227	if (bridge->type == NESMBridgeTypeIPSec) {
228		ipsec_dispose_service(&bridge->serv);
229	} else if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) {
230		ppp_dispose_service(&bridge->serv);
231	}
232
233	CFRelease(bridge->serv.serviceID);
234	CFRelease(bridge->serv.typeRef);
235	if (bridge->serv.subtypeRef != NULL) {
236		CFRelease(bridge->serv.subtypeRef);
237	}
238	TAILQ_REMOVE(&service_head, &bridge->serv, next);
239
240	if (bridge->disposable_callback != NULL) {
241		Block_release(bridge->disposable_callback);
242	}
243
244	free(bridge->serv.sid);
245	free(bridge);
246}
247
248static ne_sm_bridge_t
249bridge_create(ne_sm_bridge_type_t type, CFStringRef serviceID, void *info)
250{
251	ne_sm_bridge_t new_bridge;
252	CFIndex sid_len;
253	int error = 0;
254
255	if (!init_controller()) {
256		return NULL;
257	}
258
259	new_bridge = (ne_sm_bridge_t)malloc(sizeof(*new_bridge));
260	memset(new_bridge, 0, sizeof(*new_bridge));
261
262	new_bridge->type = type;
263	new_bridge->info = info;
264
265	new_bridge->serv.serviceID = CFRetain(serviceID);
266	sid_len = CFStringGetLength(serviceID) + 1;
267	new_bridge->serv.sid = malloc(sid_len);
268	CFStringGetCString(serviceID, (char*)new_bridge->serv.sid, sid_len, kCFStringEncodingUTF8);
269
270	new_bridge->serv.ne_sm_bridge = new_bridge;
271
272	if (type == NESMBridgeTypeIPSec) {
273		new_bridge->serv.typeRef = CFRetain(kSCValNetInterfaceTypeIPSec);
274		new_bridge->serv.type = TYPE_IPSEC;
275		ipsec_new_service(&new_bridge->serv);
276		error = ipsec_setup_service(&new_bridge->serv);
277		if (error) {
278			ne_sm_bridge_log(LOG_ERR, CFSTR("bridge_create: ipsec_setup_service failed: %d"), error);
279		}
280	} else if (type == NESMBridgeTypeL2TP || type == NESMBridgeTypePPTP) {
281		new_bridge->serv.typeRef = CFRetain(kSCValNetInterfaceTypePPP);
282		new_bridge->serv.type = TYPE_PPP;
283		if (type == NESMBridgeTypeL2TP) {
284			new_bridge->serv.subtypeRef = CFRetain(kSCValNetInterfaceSubTypeL2TP);
285		} else {
286			new_bridge->serv.subtypeRef = CFRetain(kSCValNetInterfaceSubTypePPTP);
287		}
288		new_bridge->serv.subtype = ppp_subtype(new_bridge->serv.subtypeRef);
289		ppp_new_service(&new_bridge->serv);
290		error = ppp_setup_service(&new_bridge->serv);
291		if (error) {
292			ne_sm_bridge_log(LOG_ERR, CFSTR("bridge_create: ppp_setup_service failed: %d"), error);
293		}
294	}
295
296	new_bridge->serv.unit = findfreeunit(new_bridge->serv.type, new_bridge->serv.subtype);
297	if (new_bridge->serv.unit == 0xFFFF) {
298		ne_sm_bridge_log(LOG_ERR, CFSTR("bridge_create: findfreeunit failed"));
299		error = ENOMEM;
300	}
301
302	TAILQ_INSERT_TAIL(&service_head, &new_bridge->serv, next);
303
304	if (error) {
305		bridge_destroy(new_bridge);
306		return NULL;
307	}
308
309	new_bridge->serv.initialized = TRUE;
310
311	return new_bridge;
312}
313
314static void
315bridge_handle_network_change_event(ne_sm_bridge_t bridge, const char *ifname, nwi_ifstate_flags flags)
316{
317	if (bridge->type == NESMBridgeTypeIPSec && scnc_getstatus(&bridge->serv) != kSCNetworkConnectionDisconnected) {
318		struct {
319			struct kern_event_msg msg;
320			uint8_t buffer[sizeof(struct kev_in_data)];
321		} event;
322		struct kev_in_data *inetdata = (struct kev_in_data *)event.msg.event_data;
323		int unitIdx;
324
325		memset(&event, 0, sizeof(event));
326
327		if (flags & NWI_IFSTATE_FLAGS_HAS_IPV4) {
328			int ifr_socket;
329			struct ifreq ifr;
330			int ioctl_result;
331
332			memset(&ifr, 0, sizeof(ifr));
333			ifr.ifr_addr.sa_family = AF_INET;
334			strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)-1);
335
336			ifr_socket = socket(AF_INET, SOCK_DGRAM, 0);
337			ioctl_result = ioctl(ifr_socket, SIOCGIFADDR, &ifr);
338			close(ifr_socket);
339
340			if (ioctl_result < 0) {
341				ne_sm_bridge_log(LOG_ERR, CFSTR("ioctl(SIOCGIFADDR) failed: %s"), strerror(errno));
342				return;
343			}
344
345			memcpy(&inetdata->ia_addr, &((struct sockaddr_in *)(void *)&ifr.ifr_addr)->sin_addr, sizeof(inetdata->ia_addr));
346		}
347
348		for (unitIdx = 0; ifname[unitIdx] != '\0' && !isdigit(ifname[unitIdx]); unitIdx++);
349
350		strncpy(inetdata->link_data.if_name, ifname, unitIdx);
351
352		if (isdigit(ifname[unitIdx])) {
353			inetdata->link_data.if_unit = (uint32_t)strtol((ifname + unitIdx), NULL, 10);
354		}
355
356		if (!strcmp(inetdata->link_data.if_name, "ppp")) {
357			inetdata->link_data.if_family = APPLE_IF_FAM_PPP;
358		}
359
360		if (flags & NWI_IFSTATE_FLAGS_HAS_IPV4) {
361			event.msg.event_code = KEV_INET_NEW_ADDR;
362		} else {
363			event.msg.event_code = KEV_INET_ADDR_DELETED;
364		}
365
366		if (ne_sm_bridge_is_logging_at_level(LOG_DEBUG)) {
367			if (event.msg.event_code == KEV_INET_NEW_ADDR) {
368				char addr_str[INET_ADDRSTRLEN];
369				memset(addr_str, 0, sizeof(addr_str));
370				inet_ntop(AF_INET, &inetdata->ia_addr, addr_str, sizeof(addr_str));
371
372				ne_sm_bridge_log(LOG_DEBUG, CFSTR("Network change event: added address %s to interface %s%d (family %d)"), addr_str, inetdata->link_data.if_name, inetdata->link_data.if_unit, inetdata->link_data.if_family);
373			} else if (event.msg.event_code == KEV_INET_ADDR_DELETED) {
374				ne_sm_bridge_log(LOG_DEBUG, CFSTR("Network change event: deleted address from interface %s%d (family %d)"), inetdata->link_data.if_name, inetdata->link_data.if_unit, inetdata->link_data.if_family);
375			}
376		}
377
378		ipsec_network_event(&bridge->serv, &event.msg);
379	}
380}
381
382static bool
383bridge_handle_sleep(ne_sm_bridge_t bridge)
384{
385	bool result = false;
386
387	if (bridge->type == NESMBridgeTypeIPSec) {
388		result = ipsec_will_sleep(&bridge->serv, 0);
389	} else if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) {
390		result = (ppp_will_sleep(&bridge->serv, 0) > 0);
391	}
392
393	ne_sm_bridge_log(LOG_DEBUG, CFSTR("handle sleep for bridge type %d returning %d"), bridge->type, result);
394
395	return result;
396}
397
398static bool
399bridge_can_sleep(ne_sm_bridge_t bridge)
400{
401	bool result = true;
402
403	if (bridge->type == NESMBridgeTypeIPSec) {
404		result = ipsec_can_sleep(&bridge->serv);
405	} else if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) {
406		result = ppp_can_sleep(&bridge->serv);
407	}
408
409	ne_sm_bridge_log(LOG_DEBUG, CFSTR("can sleep for bridge type %d returning %d"), bridge->type, result);
410
411	return result;
412}
413
414static void
415bridge_handle_sleep_time(ne_sm_bridge_t bridge, double sleep_time)
416{
417	ne_sm_bridge_log(LOG_INFO, CFSTR("System slept for %f secs"), sleep_time);
418	if (bridge->serv.flags & FLAG_SETUP_DISCONNECTONWAKE) {
419		double wake_timeout = 0;
420#if !TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
421		wake_timeout = scnc_getsleepwaketimeout(&bridge->serv);
422#endif
423
424		ne_sm_bridge_log(LOG_INFO, CFSTR("Session is configured to disconnect on wake if slept for more than %f seconds"), wake_timeout);
425		bridge->serv.connectionslepttime += (uint32_t)sleep_time;
426
427		if (sleep_time > wake_timeout) {
428			scnc_idle_disconnect(&bridge->serv);
429		}
430	}
431}
432
433static void
434bridge_handle_wakeup(ne_sm_bridge_t bridge)
435{
436	ne_sm_bridge_log(LOG_INFO, CFSTR("Handling wake up for bridge type %d"), bridge->type);
437	if (bridge->type == NESMBridgeTypeIPSec) {
438		ipsec_wake_up(&bridge->serv);
439	} else if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) {
440		ppp_wake_up(&bridge->serv);
441	}
442}
443
444static bool
445bridge_handle_start(ne_sm_bridge_t bridge, CFDictionaryRef options, uid_t uid, gid_t gid, mach_port_t bootstrap_port, mach_port_t audit_session_port, bool on_demand)
446{
447	if (bridge->type == NESMBridgeTypeIPSec) {
448		return (ipsec_start(&bridge->serv, options, uid, gid, bootstrap_port, false, on_demand) == 0);
449	} else if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) {
450		return (ppp_start(&bridge->serv, options, uid, gid, bootstrap_port, audit_session_port, false, on_demand) == 0);
451	}
452	return false;
453}
454
455static bool
456bridge_handle_stop(ne_sm_bridge_t bridge)
457{
458	ne_sm_bridge_log(LOG_INFO, CFSTR("Handling stop for bridge type %d"), bridge->type);
459	if (bridge->type == NESMBridgeTypeIPSec) {
460		return (ipsec_stop(&bridge->serv, 0) == 0);
461	} else if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) {
462		return (ppp_stop(&bridge->serv, SIGTERM) == 0);
463	}
464	return false;
465}
466
467static void
468bridge_get_security_session_info(ne_sm_bridge_t bridge, xpc_object_t request, mach_port_t *bootstrap_port, mach_port_t *audit_session_port)
469{
470	xpc_connection_t connection = xpc_dictionary_get_remote_connection(request);
471	xpc_object_t entitlement = xpc_connection_copy_entitlement_value(connection, NESessionManagerPrivilegedEntitlement);
472
473	if ((bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) &&
474	    isa_xpc_bool(entitlement) && xpc_bool_get_value(entitlement))
475	{
476		*bootstrap_port = bridge->serv.bootstrap;
477		*audit_session_port = bridge->serv.au_session;
478	} else {
479		*bootstrap_port = MACH_PORT_NULL;
480		*audit_session_port = MACH_PORT_NULL;
481	}
482
483	if (entitlement) {
484		xpc_release(entitlement);
485	}
486}
487
488static CFDictionaryRef
489bridge_copy_configuration(ne_sm_bridge_t bridge, xpc_object_t request)
490{
491	xpc_connection_t connection = xpc_dictionary_get_remote_connection(request);
492	CFDictionaryRef serviceConfig = ne_sm_bridge_copy_configuration(bridge);
493	CFDictionaryRef userOptions = NULL;
494	CFMutableDictionaryRef info = NULL;
495	int error = 0;
496
497	if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) {
498		xpc_object_t entitlement = xpc_connection_copy_entitlement_value(connection, NESessionManagerPrivilegedEntitlement);
499
500		if (isa_xpc_bool(entitlement) && xpc_bool_get_value(entitlement))
501		{
502			error = ppp_getconnectdata(&bridge->serv, &userOptions, true);
503		} else {
504			error = ppp_getconnectdata(&bridge->serv, &userOptions, false);
505		}
506
507		if (entitlement) {
508			xpc_release(entitlement);
509		}
510	} else if (bridge->type == NESMBridgeTypeIPSec) {
511		error = ipsec_getconnectdata(&bridge->serv, &userOptions, false);
512	}
513
514	if (error == 0 && userOptions != NULL) {
515		if (info == NULL) {
516			info = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
517		}
518		CFDictionarySetValue(info, CFSTR(NESMSessionLegacyUserConfigurationKey), userOptions);
519	}
520
521	if (userOptions != NULL) {
522		CFRelease(userOptions);
523	}
524
525	if (serviceConfig != NULL) {
526		if (info == NULL) {
527			info = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
528		}
529		CFDictionarySetValue(info, CFSTR(NESMSessionLegacyServiceConfigurationKey), serviceConfig);
530		CFRelease(serviceConfig);
531	}
532
533	return info;
534}
535
536static void
537bridge_set_disposable_callback(ne_sm_bridge_t bridge, void (^callback)(void))
538{
539	if (bridge->disposable_callback != NULL) {
540		Block_release(bridge->disposable_callback);
541	}
542	bridge->disposable_callback = Block_copy(callback);
543}
544
545static CFDictionaryRef
546bridge_copy_statistics(ne_sm_bridge_t bridge)
547{
548	int error = 0;
549	CFDictionaryRef statsdict = NULL;
550
551	if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) {
552		error = ppp_copystatistics(&bridge->serv, &statsdict);
553	} else if (bridge->type == NESMBridgeTypeIPSec) {
554		error = ipsec_copystatistics(&bridge->serv, &statsdict);
555	}
556
557	if (error) {
558		ne_sm_bridge_log(LOG_NOTICE, CFSTR("Failed to copy statistics: %s"), strerror(error));
559	}
560
561	return statsdict;
562}
563
564static CFDictionaryRef
565bridge_copy_extended_status(ne_sm_bridge_t bridge)
566{
567	int error = 0;
568	CFDictionaryRef statusdict = NULL;
569
570	if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) {
571		error = ppp_copyextendedstatus(&bridge->serv, &statusdict);
572	} else if (bridge->type == NESMBridgeTypeIPSec) {
573		error = ipsec_copyextendedstatus(&bridge->serv, &statusdict);
574	}
575
576	if (error) {
577		ne_sm_bridge_log(LOG_NOTICE, CFSTR("Failed to copy extended status: %s"), strerror(error));
578	}
579
580	return statusdict;
581}
582
583static void
584bridge_install(ne_sm_bridge_t bridge)
585{
586	int error = 0;
587
588	if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) {
589		error = ppp_install(&bridge->serv);
590	} else if (bridge->type == NESMBridgeTypeIPSec) {
591		error = ipsec_install(&bridge->serv);
592	}
593
594	if (error) {
595		ne_sm_bridge_log(LOG_NOTICE, CFSTR("Failed to install: %s"), strerror(error));
596	}
597}
598
599static void
600bridge_uninstall(ne_sm_bridge_t bridge)
601{
602	int error = 0;
603
604	if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) {
605		error = ppp_uninstall(&bridge->serv);
606	} else if (bridge->type == NESMBridgeTypeIPSec) {
607		error = ipsec_uninstall(&bridge->serv);
608	}
609
610	if (error) {
611		ne_sm_bridge_log(LOG_NOTICE, CFSTR("Failed to uninstall: %s"), strerror(error));
612	}
613}
614
615static void
616bridge_handle_configuration_change(ne_sm_bridge_t bridge)
617{
618	int error;
619
620	if (bridge->type == NESMBridgeTypeIPSec) {
621		error = ipsec_setup_service(&bridge->serv);
622		if (error) {
623			ne_sm_bridge_log(LOG_ERR, CFSTR("bridge_create: ipsec_setup_service failed: %d"), error);
624		}
625	} else if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) {
626		error = ppp_setup_service(&bridge->serv);
627		if (error) {
628			ne_sm_bridge_log(LOG_ERR, CFSTR("bridge_create: ppp_setup_service failed: %d"), error);
629		}
630	}
631}
632
633static void
634bridge_handle_user_logout(ne_sm_bridge_t bridge)
635{
636	if (bridge->type == NESMBridgeTypeIPSec) {
637		ipsec_log_out(&bridge->serv);
638	} else if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) {
639		ppp_log_out(&bridge->serv);
640	}
641}
642
643static void
644bridge_handle_user_switch(ne_sm_bridge_t bridge)
645{
646	if (bridge->type == NESMBridgeTypeIPSec) {
647		ipsec_log_switch(&bridge->serv);
648	} else if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) {
649		ppp_log_switch(&bridge->serv);
650	}
651}
652
653static void
654bridge_handle_device_lock(ne_sm_bridge_t bridge)
655{
656	if (bridge->type == NESMBridgeTypeIPSec) {
657		ipsec_device_lock(&bridge->serv);
658	}
659}
660
661static void
662bridge_handle_device_unlock(ne_sm_bridge_t bridge)
663{
664	if (bridge->type == NESMBridgeTypeIPSec) {
665		ipsec_device_unlock(&bridge->serv);
666	}
667}
668
669extern ne_sm_bridge_functions_t
670ne_sm_bridge_copy_functions(struct ne_sm_bridge_callbacks *callbacks, CFBundleRef bundle)
671{
672	static dispatch_once_t copy_functions_once = 0;
673	static ne_sm_bridge_functions_t functions = NULL;
674
675	dispatch_once(&copy_functions_once,
676		^{
677			functions = (ne_sm_bridge_functions_t)malloc(sizeof(*functions));
678
679			memset(functions, 0, sizeof(*functions));
680
681			functions->create = bridge_create;
682			functions->destroy = bridge_destroy;
683			functions->handle_network_change_event = bridge_handle_network_change_event;
684			functions->handle_sleep = bridge_handle_sleep;
685			functions->can_sleep = bridge_can_sleep;
686			functions->handle_sleep_time = bridge_handle_sleep_time;
687			functions->handle_wakeup = bridge_handle_wakeup;
688			functions->handle_start = bridge_handle_start;
689			functions->handle_stop = bridge_handle_stop;
690			functions->get_security_session_info = bridge_get_security_session_info;
691			functions->set_disposable_callback = bridge_set_disposable_callback;
692			functions->copy_statistics = bridge_copy_statistics;
693			functions->copy_extended_status = bridge_copy_extended_status;
694			functions->copy_configuration = bridge_copy_configuration;
695			functions->install = bridge_install;
696			functions->uninstall = bridge_uninstall;
697			functions->handle_configuration_change = bridge_handle_configuration_change;
698			functions->handle_user_logout = bridge_handle_user_logout;
699			functions->handle_user_switch = bridge_handle_user_switch;
700			functions->handle_device_lock = bridge_handle_device_lock;
701			functions->handle_device_unlock = bridge_handle_device_unlock;
702
703			g_callbacks = (struct ne_sm_bridge_callbacks *)malloc(sizeof(*g_callbacks));
704			memcpy(g_callbacks, callbacks, sizeof(*g_callbacks));
705			gBundleRef = (CFBundleRef)CFRetain(bundle);
706		});
707
708	return functions;
709}
710
711