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  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Query and configure LAN interfaces over IPMI.  This is done through the
28  * complicated get/set LAN Configuration Parameters command.  This queries or
29  * sets the parameters one per command in series.  We hide this implementation
30  * detail and instead export a single structure to consumers.
31  */
32 
33 #include <stddef.h>
34 #include <strings.h>
35 
36 #include <libipmi.h>
37 
38 #include "ipmi_impl.h"
39 
40 typedef struct ipmi_cmd_lan_get_config {
41 	DECL_BITFIELD3(
42 	    ilgc_number		:4,
43 	    __reserved		:3,
44 	    ilgc_revonly	:1);
45 	uint8_t		ilgc_param;
46 	uint8_t		ilgc_set;
47 	uint8_t		ilgc_block;
48 } ipmi_cmd_lan_get_config_t;
49 
50 typedef struct ipmi_cmd_lan_set_config {
51 	DECL_BITFIELD2(
52 	    ilsc_number		:4,
53 	    __reserved		:4);
54 	uint8_t		ilsc_param;
55 	uint8_t		ilsc_data[18];
56 } ipmi_cmd_lan_set_config_t;
57 
58 #define	IPMI_LAN_SET_LEN(dlen)	\
59 	(offsetof(ipmi_cmd_lan_set_config_t, ilsc_data) + (dlen))
60 
61 #define	IPMI_LAN_PARAM_SET_IN_PROGRESS		0
62 #define	IPMI_LAN_PARAM_IP_ADDR			3
63 #define	IPMI_LAN_PARAM_IP_SOURCE		4
64 #define	IPMI_LAN_PARAM_MAC_ADDR			5
65 #define	IPMI_LAN_PARAM_SUBNET_MASK		6
66 #define	IPMI_LAN_PARAM_GATEWAY_ADDR		12
67 
68 #define	IPMI_LAN_SET_COMPLETE			0x0
69 #define	IPMI_LAN_SET_INPROGRESS			0x1
70 #define	IPMI_LAN_SET_COMMIT			0x2
71 
72 typedef struct ipmi_lan_entry {
73 	int	ile_param;
74 	int	ile_mask;
75 	int	ile_set;
76 	int	ile_block;
77 	size_t	ile_offset;
78 	size_t	ile_len;
79 } ipmi_lan_entry_t;
80 
81 static ipmi_lan_entry_t ipmi_lan_table[] = {
82 	{ IPMI_LAN_PARAM_IP_ADDR, IPMI_LAN_SET_IPADDR, 0, 0,
83 	    offsetof(ipmi_lan_config_t, ilc_ipaddr), sizeof (uint32_t) },
84 	{ IPMI_LAN_PARAM_IP_SOURCE, IPMI_LAN_SET_IPADDR_SOURCE, 0, 0,
85 	    offsetof(ipmi_lan_config_t, ilc_ipaddr_source), sizeof (uint8_t) },
86 	{ IPMI_LAN_PARAM_MAC_ADDR, IPMI_LAN_SET_MACADDR, 0, 0,
87 	    offsetof(ipmi_lan_config_t, ilc_macaddr), 6 * sizeof (uint8_t) },
88 	{ IPMI_LAN_PARAM_SUBNET_MASK, IPMI_LAN_SET_SUBNET, 0, 0,
89 	    offsetof(ipmi_lan_config_t, ilc_subnet), sizeof (uint32_t) },
90 	{ IPMI_LAN_PARAM_GATEWAY_ADDR, IPMI_LAN_SET_GATEWAY_ADDR, 0, 0,
91 	    offsetof(ipmi_lan_config_t, ilc_gateway_addr), sizeof (uint32_t) }
92 };
93 
94 #define	IPMI_LAN_NENTRIES	\
95 	(sizeof (ipmi_lan_table) / sizeof (ipmi_lan_table[0]))
96 
97 static int
ipmi_lan_get_param(ipmi_handle_t * ihp,int channel,int param,int set,int block,void * data,size_t len)98 ipmi_lan_get_param(ipmi_handle_t *ihp, int channel, int param, int set,
99     int block, void *data, size_t len)
100 {
101 	ipmi_cmd_t cmd, *rsp;
102 	ipmi_cmd_lan_get_config_t lcmd = { 0 };
103 
104 	lcmd.ilgc_number = channel;
105 	lcmd.ilgc_param = param;
106 	lcmd.ilgc_set = set;
107 	lcmd.ilgc_block = block;
108 
109 	cmd.ic_netfn = IPMI_NETFN_TRANSPORT;
110 	cmd.ic_lun = 0;
111 	cmd.ic_cmd = IPMI_CMD_GET_LAN_CONFIG;
112 	cmd.ic_data = &lcmd;
113 	cmd.ic_dlen = sizeof (lcmd);
114 
115 	if ((rsp = ipmi_send(ihp, &cmd)) == NULL) {
116 		switch (ihp->ih_completion) {
117 		case 0x80:
118 			(void) ipmi_set_error(ihp, EIPMI_BADPARAM, NULL);
119 			break;
120 		}
121 		return (-1);
122 	}
123 
124 	if (rsp->ic_dlen < len + 1)
125 		return (ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH, NULL));
126 
127 	bcopy((uint8_t *)rsp->ic_data + 1, data, len);
128 
129 	return (0);
130 }
131 
132 int
ipmi_lan_get_config(ipmi_handle_t * ihp,int channel,ipmi_lan_config_t * cfgp)133 ipmi_lan_get_config(ipmi_handle_t *ihp, int channel, ipmi_lan_config_t *cfgp)
134 {
135 	uint8_t set;
136 	int i;
137 	ipmi_lan_entry_t *lep;
138 
139 	if (ipmi_lan_get_param(ihp, channel, IPMI_LAN_PARAM_SET_IN_PROGRESS, 0,
140 	    0, &set, sizeof (set)) != 0)
141 		return (-1);
142 
143 	if (set & IPMI_LAN_SET_INPROGRESS)
144 		cfgp->ilc_set_in_progress = B_TRUE;
145 	else
146 		cfgp->ilc_set_in_progress = B_FALSE;
147 
148 	for (i = 0; i < IPMI_LAN_NENTRIES; i++) {
149 		lep = &ipmi_lan_table[i];
150 		if (ipmi_lan_get_param(ihp, channel, lep->ile_param,
151 		    lep->ile_set, lep->ile_block,
152 		    (char *)cfgp + lep->ile_offset, lep->ile_len) != 0)
153 			return (-1);
154 	}
155 
156 	return (0);
157 }
158 
159 static int
ipmi_lan_set_param(ipmi_handle_t * ihp,int channel,int param,void * data,size_t len)160 ipmi_lan_set_param(ipmi_handle_t *ihp, int channel, int param, void *data,
161     size_t len)
162 {
163 	ipmi_cmd_t cmd;
164 	ipmi_cmd_lan_set_config_t lcmd = { 0 };
165 
166 	lcmd.ilsc_number = channel;
167 	lcmd.ilsc_param = param;
168 	bcopy(data, lcmd.ilsc_data, len);
169 
170 	cmd.ic_netfn = IPMI_NETFN_TRANSPORT;
171 	cmd.ic_lun = 0;
172 	cmd.ic_cmd = IPMI_CMD_SET_LAN_CONFIG;
173 	cmd.ic_data = &lcmd;
174 	cmd.ic_dlen = IPMI_LAN_SET_LEN(len);
175 
176 	if (ipmi_send(ihp, &cmd) == NULL) {
177 		switch (ihp->ih_completion) {
178 		case 0x80:
179 			(void) ipmi_set_error(ihp, EIPMI_BADPARAM, NULL);
180 			break;
181 
182 		case 0x81:
183 			(void) ipmi_set_error(ihp, EIPMI_BUSY, NULL);
184 			break;
185 
186 		case 0x82:
187 			(void) ipmi_set_error(ihp, EIPMI_READONLY, NULL);
188 			break;
189 
190 		case 0x83:
191 			(void) ipmi_set_error(ihp, EIPMI_WRITEONLY, NULL);
192 			break;
193 		}
194 		return (-1);
195 	}
196 
197 	return (0);
198 }
199 
200 int
ipmi_lan_set_config(ipmi_handle_t * ihp,int channel,ipmi_lan_config_t * cfgp,int mask)201 ipmi_lan_set_config(ipmi_handle_t *ihp, int channel, ipmi_lan_config_t *cfgp,
202     int mask)
203 {
204 	uint8_t set;
205 	int i;
206 	ipmi_lan_entry_t *lep;
207 
208 	/*
209 	 * Cancel any pending transaction, then open a new transaction.
210 	 */
211 	set = IPMI_LAN_SET_COMPLETE;
212 	if (ipmi_lan_set_param(ihp, channel, IPMI_LAN_PARAM_SET_IN_PROGRESS,
213 	    &set, sizeof (set)) != 0)
214 		return (-1);
215 	set = IPMI_LAN_SET_INPROGRESS;
216 	if (ipmi_lan_set_param(ihp, channel, IPMI_LAN_PARAM_SET_IN_PROGRESS,
217 	    &set, sizeof (set)) != 0)
218 		return (-1);
219 
220 	/*
221 	 * Iterate over all parameters and set them.
222 	 */
223 	for (i = 0; i < IPMI_LAN_NENTRIES; i++) {
224 		lep = &ipmi_lan_table[i];
225 		if (!(lep->ile_mask & mask))
226 			continue;
227 
228 		if (ipmi_lan_set_param(ihp, channel, lep->ile_param,
229 		    (char *)cfgp + lep->ile_offset, lep->ile_len) != 0) {
230 			/*
231 			 * On some systems, setting the mode to DHCP may cause
232 			 * the command to timeout, presumably because it is
233 			 * waiting for the setting to take effect.  If we see
234 			 * completion code 0xc3 (command timeout) while setting
235 			 * the DHCP value, just ignore it.
236 			 */
237 			if (mask != IPMI_LAN_SET_IPADDR_SOURCE ||
238 			    cfgp->ilc_ipaddr_source != IPMI_LAN_SRC_DHCP ||
239 			    ihp->ih_completion != 0xC3)
240 				return (-1);
241 		}
242 	}
243 
244 	/*
245 	 * Commit the transaction.
246 	 */
247 	set = IPMI_LAN_SET_COMPLETE;
248 	if (ipmi_lan_set_param(ihp, channel, IPMI_LAN_PARAM_SET_IN_PROGRESS,
249 	    &set, sizeof (set)) != 0)
250 		return (-1);
251 
252 	return (0);
253 }
254