1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 #include <arpa/inet.h>
27 #include <assert.h>
28 #include <fcntl.h>
29 #include <libdlpi.h>
30 #include <libnwam.h>
31 #include <net/if.h>
32 #include <pthread.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/fcntl.h>
37 #include <unistd.h>
38 
39 #include "events.h"
40 #include "ncp.h"
41 #include "ncu.h"
42 #include "objects.h"
43 #include "util.h"
44 
45 /*
46  * dlpi_events.c - this file contains routines to retrieve
47  * DL_NOTE_LINK_[UP|DOWN] events from the system and packages them for high
48  * level processing.  Holding a dlpi_handle to a link prevents the
49  * associated driver unloading that can happen when IP is not plumbed,
50  * so it is vital to ensure that the handle is open for the lifetime
51  * of the WiFi connection.
52  */
53 
54 /*
55  * This is a callback function executed when dlpi_recv() gets a DL_NOTE_LINK_UP.
56  * It packages up the event for consumption by the link state machine.
57  */
58 /* ARGSUSED0 */
59 static void
nwamd_dlpi_notify(dlpi_handle_t dhp,dlpi_notifyinfo_t * info,void * arg)60 nwamd_dlpi_notify(dlpi_handle_t dhp, dlpi_notifyinfo_t *info, void *arg)
61 {
62 	nwamd_event_t ev;
63 	char *name = arg;
64 
65 	if (info->dni_note & DL_NOTE_LINK_UP)
66 		ev = nwamd_event_init_link_state(name, B_TRUE);
67 	else
68 		ev = nwamd_event_init_link_state(name, B_FALSE);
69 	if (ev != NULL)
70 		nwamd_event_enqueue(ev);
71 }
72 
73 /*
74  * We are only intested in DL_NOTE_LINK_UP events which we've registered for
75  * in nwamd_dlpi_add_link().  But we have to keep calling dlpi_recv() to
76  * force the notification callback to be executed.
77  */
78 static void *
nwamd_dlpi_thread(void * arg)79 nwamd_dlpi_thread(void *arg)
80 {
81 	int rc;
82 	dlpi_handle_t *dh = arg;
83 
84 	do {
85 		rc = dlpi_recv(*dh, NULL, NULL, NULL, NULL, -1, NULL);
86 	} while (rc == DLPI_SUCCESS);
87 	nlog(LOG_ERR, "dlpi_recv failed: %s", dlpi_strerror(rc));
88 	return (NULL);
89 }
90 
91 /*
92  * This is called when we want to start receiving notifications from state
93  * changes on a link.
94  */
95 void
nwamd_dlpi_add_link(nwamd_object_t obj)96 nwamd_dlpi_add_link(nwamd_object_t obj)
97 {
98 	nwamd_ncu_t *ncu = obj->nwamd_object_data;
99 	nwamd_link_t *link;
100 	dlpi_notifyid_t id;
101 	int rc;
102 
103 	nlog(LOG_DEBUG, "nwamd_dlpi_add_link: ncu %p (%s) type %d",
104 	    ncu, obj->nwamd_object_name, ncu != NULL ? ncu->ncu_type : -1);
105 
106 	assert(ncu != NULL && ncu->ncu_type == NWAM_NCU_TYPE_LINK);
107 
108 	link = &ncu->ncu_link;
109 
110 	/* Already running? */
111 	if (link->nwamd_link_dlpi_thread != 0) {
112 		nlog(LOG_DEBUG, "nwamd_dlpi_add_link(%s) already running",
113 		    obj->nwamd_object_name);
114 		return;
115 	}
116 
117 	rc = dlpi_open(ncu->ncu_name, &link->nwamd_link_dhp, 0);
118 	if (rc != DLPI_SUCCESS) {
119 		nlog(LOG_ERR, "nwamd_dlpi_add_link: dlpi_open(%s) = %s",
120 		    ncu->ncu_name, dlpi_strerror(rc));
121 		return;
122 	}
123 
124 	nwamd_set_unset_link_properties(ncu, B_TRUE);
125 
126 	rc = dlpi_enabnotify(link->nwamd_link_dhp,
127 	    DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN, nwamd_dlpi_notify,
128 	    ncu->ncu_name, &id);
129 	if (rc != DLPI_SUCCESS) {
130 		nlog(LOG_ERR,
131 		    "nwamd_dlpi_add_link: dlpi_enabnotify(%s) = %s",
132 		    obj->nwamd_object_name, dlpi_strerror(rc));
133 		dlpi_close(link->nwamd_link_dhp);
134 		return;
135 	}
136 
137 	rc = pthread_create(&link->nwamd_link_dlpi_thread, NULL,
138 	    nwamd_dlpi_thread, &link->nwamd_link_dhp);
139 	if (rc != 0) {
140 		nlog(LOG_ERR, "nwamd_dlpi_add_link: couldn't create "
141 		    "dlpi thread for %s: %s", obj->nwamd_object_name,
142 		    strerror(rc));
143 		dlpi_close(link->nwamd_link_dhp);
144 	}
145 }
146 
147 /*
148  * This function is called when we are no longer interested in receiving
149  * notification from state changes on a link.
150  */
151 void
nwamd_dlpi_delete_link(nwamd_object_t obj)152 nwamd_dlpi_delete_link(nwamd_object_t obj)
153 {
154 	nwamd_ncu_t *ncu = obj->nwamd_object_data;
155 
156 	nlog(LOG_DEBUG, "nwamd_dlpi_delete_link: ncu %p (%s) type %d",
157 	    ncu, obj->nwamd_object_name, ncu != NULL ? ncu->ncu_type : -1);
158 
159 	if (ncu->ncu_link.nwamd_link_dlpi_thread != 0) {
160 		(void) pthread_cancel(
161 		    ncu->ncu_link.nwamd_link_dlpi_thread);
162 		(void) pthread_join(ncu->ncu_link.nwamd_link_dlpi_thread, NULL);
163 		ncu->ncu_link.nwamd_link_dlpi_thread = 0;
164 		/* Unset properties before closing */
165 		nwamd_set_unset_link_properties(ncu, B_FALSE);
166 	}
167 
168 	dlpi_close(ncu->ncu_link.nwamd_link_dhp);
169 	ncu->ncu_link.nwamd_link_dhp = NULL;
170 }
171