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 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <arpa/inet.h>
28 #include <assert.h>
29 #include <fcntl.h>
30 #include <libdlpi.h>
31 #include <libnwam.h>
32 #include <net/if.h>
33 #include <pthread.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/fcntl.h>
38 #include <unistd.h>
39 
40 #include "events.h"
41 #include "ncp.h"
42 #include "ncu.h"
43 #include "objects.h"
44 #include "util.h"
45 
46 /*
47  * dlpi_events.c - this file contains routines to retrieve
48  * DL_NOTE_LINK_[UP|DOWN] events from the system and packages them for high
49  * level processing.  Holding a dlpi_handle to a link prevents the
50  * associated driver unloading that can happen when IP is not plumbed,
51  * so it is vital to ensure that the handle is open for the lifetime
52  * of the WiFi connection.
53  */
54 
55 /*
56  * This is a callback function executed when dlpi_recv() gets a DL_NOTE_LINK_UP.
57  * It packages up the event for consumption by the link state machine.
58  */
59 /* ARGSUSED0 */
60 static void
61 nwamd_dlpi_notify(dlpi_handle_t dhp, dlpi_notifyinfo_t *info, void *arg)
62 {
63 	nwamd_event_t ev;
64 	char *name = arg;
65 
66 	if (info->dni_note & DL_NOTE_LINK_UP)
67 		ev = nwamd_event_init_link_state(name, B_TRUE);
68 	else
69 		ev = nwamd_event_init_link_state(name, B_FALSE);
70 	if (ev != NULL)
71 		nwamd_event_enqueue(ev);
72 }
73 
74 /*
75  * We are only intested in DL_NOTE_LINK_UP events which we've registered for
76  * in nwamd_dlpi_add_link().  But we have to keep calling dlpi_recv() to
77  * force the notification callback to be executed.
78  */
79 static void *
80 nwamd_dlpi_thread(void *arg)
81 {
82 	int rc;
83 	dlpi_handle_t *dh = arg;
84 
85 	do {
86 		rc = dlpi_recv(*dh, NULL, NULL, NULL, NULL, -1, NULL);
87 	} while (rc == DLPI_SUCCESS);
88 	nlog(LOG_ERR, "dlpi_recv failed: %s", dlpi_strerror(rc));
89 	return (NULL);
90 }
91 
92 /*
93  * This is called when we want to start receiving notifications from state
94  * changes on a link.
95  */
96 void
97 nwamd_dlpi_add_link(nwamd_object_t obj)
98 {
99 	nwamd_ncu_t *ncu = obj->nwamd_object_data;
100 	nwamd_link_t *link;
101 	dlpi_notifyid_t id;
102 	int rc;
103 
104 	nlog(LOG_DEBUG, "nwamd_dlpi_add_link: ncu %p (%s) type %d",
105 	    ncu, obj->nwamd_object_name, ncu != NULL ? ncu->ncu_type : -1);
106 
107 	assert(ncu != NULL && ncu->ncu_type == NWAM_NCU_TYPE_LINK);
108 
109 	link = &ncu->ncu_node.u_link;
110 
111 	/* Already running? */
112 	if (link->nwamd_link_dlpi_thread != 0) {
113 		nlog(LOG_DEBUG, "nwamd_dlpi_add_link(%s) already running",
114 		    obj->nwamd_object_name);
115 		return;
116 	}
117 
118 	rc = dlpi_open(ncu->ncu_name, &link->nwamd_link_dhp, 0);
119 	if (rc != DLPI_SUCCESS) {
120 		nlog(LOG_ERR, "nwamd_dlpi_add_link: dlpi_open(%s) = %s",
121 		    ncu->ncu_name, dlpi_strerror(rc));
122 		return;
123 	}
124 
125 	nwamd_set_unset_link_properties(ncu, B_TRUE);
126 
127 	rc = dlpi_enabnotify(link->nwamd_link_dhp,
128 	    DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN, nwamd_dlpi_notify,
129 	    ncu->ncu_name, &id);
130 	if (rc != DLPI_SUCCESS) {
131 		nlog(LOG_ERR,
132 		    "nwamd_dlpi_add_link: dlpi_enabnotify(%s) = %s",
133 		    obj->nwamd_object_name, dlpi_strerror(rc));
134 		dlpi_close(link->nwamd_link_dhp);
135 		return;
136 	}
137 
138 	rc = pthread_create(&link->nwamd_link_dlpi_thread, NULL,
139 	    nwamd_dlpi_thread, &link->nwamd_link_dhp);
140 	if (rc != 0) {
141 		nlog(LOG_ERR, "nwamd_dlpi_add_link: couldn't create "
142 		    "dlpi thread for %s: %s", obj->nwamd_object_name,
143 		    strerror(rc));
144 		dlpi_close(link->nwamd_link_dhp);
145 	}
146 }
147 
148 /*
149  * This function is called when we are no longer interested in receiving
150  * notification from state changes on a link.
151  */
152 void
153 nwamd_dlpi_delete_link(nwamd_object_t obj)
154 {
155 	nwamd_ncu_t *ncu = obj->nwamd_object_data;
156 
157 	nlog(LOG_DEBUG, "nwamd_dlpi_delete_link: ncu %p (%s) type %d",
158 	    ncu, obj->nwamd_object_name, ncu != NULL ? ncu->ncu_type : -1);
159 
160 	if (ncu->ncu_node.u_link.nwamd_link_dlpi_thread != 0) {
161 		(void) pthread_cancel(
162 		    ncu->ncu_node.u_link.nwamd_link_dlpi_thread);
163 		(void) pthread_join(ncu->ncu_node.u_link.nwamd_link_dlpi_thread,
164 		    NULL);
165 		ncu->ncu_node.u_link.nwamd_link_dlpi_thread = 0;
166 		/* Unset properties before closing */
167 		nwamd_set_unset_link_properties(ncu, B_FALSE);
168 	}
169 
170 	dlpi_close(ncu->ncu_node.u_link.nwamd_link_dhp);
171 	ncu->ncu_node.u_link.nwamd_link_dhp = NULL;
172 }
173