1/*
2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License").  You may not use this file except in compliance with the
9 * License.  Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23/*
24 * ifbond.c
25 * - add and remove interfaces from a bond interface
26 */
27
28/*
29 * Modification History:
30 *
31 * July 14, 2004	Dieter Siegmund (dieter@apple.com)
32 * - created
33 */
34
35#include <sys/param.h>
36#include <sys/ioctl.h>
37#include <sys/socket.h>
38
39#include <stdlib.h>
40#include <unistd.h>
41
42#include <net/ethernet.h>
43#include <net/if.h>
44#include <net/if_var.h>
45#include <net/if_bond_var.h>
46
47#include <net/route.h>
48
49#include <ctype.h>
50#include <stdio.h>
51#include <string.h>
52#include <stdlib.h>
53#include <unistd.h>
54#include <err.h>
55#include <errno.h>
56
57#include "ifconfig.h"
58extern int bond_details;
59
60#define EA_FORMAT	"%02x:%02x:%02x:%02x:%02x:%02x"
61#define EA_CH(e, i)	((u_char)((u_char *)(e))[(i)])
62#define EA_LIST(ea)	EA_CH(ea,0),EA_CH(ea,1),EA_CH(ea,2),EA_CH(ea,3),EA_CH(ea,4),EA_CH(ea,5)
63
64static __inline__ const char *
65selected_state_string(u_char s)
66{
67	static const char * names[] = { "unselected", "selected", "standby" };
68
69	if (s <= IF_BOND_STATUS_SELECTED_STATE_STANDBY) {
70		return (names[s]);
71	}
72	return ("<unknown>");
73}
74
75static void
76bond_print_details(struct if_bond_status * ibs_p, int count)
77
78{
79	int				i;
80	struct if_bond_status * 	scan_p = ibs_p;
81
82	for (i = 0; i < count; i++, scan_p++) {
83		struct if_bond_partner_state *	ps;
84		ps = &scan_p->ibs_partner_state;
85		printf("\tbond interface: %s priority: 0x%04x "
86		       "state: 0x%02x partner system: 0x%04x,"
87		       EA_FORMAT " "
88		       "key: 0x%04x port: 0x%04x priority: 0x%04x "
89		       "state: 0x%02x\n",
90		       scan_p->ibs_if_name, scan_p->ibs_port_priority,
91		       scan_p->ibs_state, ps->ibps_system_priority,
92		       EA_LIST(&ps->ibps_system), ps->ibps_key,
93		       ps->ibps_port, ps->ibps_port_priority,
94		       ps->ibps_state);
95	}
96	return;
97}
98
99void
100bond_status(int s)
101{
102	int				i;
103	struct if_bond_req		ibr;
104	struct if_bond_status *		ibs_p;
105	struct if_bond_status_req *	ibsr_p;
106	char				mode_buf[16];
107	const char *			mode_str;
108
109	bzero((char *)&ibr, sizeof(ibr));
110	ibr.ibr_op = IF_BOND_OP_GET_STATUS;
111	ibsr_p = &ibr.ibr_ibru.ibru_status;
112	ibsr_p->ibsr_version = IF_BOND_STATUS_REQ_VERSION;
113	ifr.ifr_data = (caddr_t)&ibr;
114
115	/* how many of them are there? */
116	if (ioctl(s, SIOCGIFBOND, (caddr_t)&ifr) < 0) {
117		return;
118	}
119	switch (ibsr_p->ibsr_mode) {
120	case IF_BOND_MODE_LACP:
121		mode_str = "lacp";
122		break;
123	case IF_BOND_MODE_STATIC:
124		mode_str = "static";
125		break;
126	default:
127		snprintf(mode_buf, sizeof(mode_buf), "%d", ibsr_p->ibsr_mode);
128		mode_str = mode_buf;
129		break;
130	}
131	if (ibsr_p->ibsr_total == 0) {
132		if (bond_details) {
133			printf("\tbond mode: %s\n"
134			       "\tbond key: 0x%04x interfaces: <none>",
135			       mode_str, ibsr_p->ibsr_key);
136		}
137		else {
138			printf("\tbond interfaces: <none>\n");
139		}
140		return;
141	}
142	ibsr_p->ibsr_buffer
143		= (char *)malloc(sizeof(struct if_bond_status)
144				 * ibsr_p->ibsr_total);
145	ibsr_p->ibsr_count = ibsr_p->ibsr_total;
146
147	/* get the list */
148	if (ioctl(s, SIOCGIFBOND, (caddr_t)&ifr) < 0) {
149		goto done;
150	}
151	if (ibsr_p->ibsr_total > 0) {
152		if (bond_details) {
153			printf("\tbond mode: %s\n"
154			       "\tbond key: 0x%04x interfaces:",
155			       mode_str, ibsr_p->ibsr_key);
156		}
157		else {
158			printf("\tbond interfaces:");
159		}
160		ibs_p = (struct if_bond_status *)ibsr_p->ibsr_buffer;
161		for (i = 0; i < ibsr_p->ibsr_total; i++, ibs_p++) {
162			printf(" %s", ibs_p->ibs_if_name);
163			if (bond_details) {
164				u_char s = ibs_p->ibs_selected_state;
165				printf(" (%s)", selected_state_string(s));
166			}
167		}
168		printf("\n");
169		if (bond_details) {
170			bond_print_details((struct if_bond_status *)
171					   ibsr_p->ibsr_buffer,
172					   ibsr_p->ibsr_total);
173		}
174	}
175	else if (bond_details) {
176		printf("\tbond mode: %s\n"
177		       "\tbond key: 0x%04x interfaces: <none>\n",
178		       mode_str, ibsr_p->ibsr_key);
179	}
180	else {
181		printf("\tbond interfaces: <none>\n");
182	}
183
184 done:
185	free(ibsr_p->ibsr_buffer);
186	return;
187}
188
189static
190DECL_CMD_FUNC(setbonddev, val, d)
191{
192	struct if_bond_req		ibr;
193
194	bzero((char *)&ibr, sizeof(ibr));
195	if ((unsigned int)snprintf(ibr.ibr_ibru.ibru_if_name,
196				   sizeof(ibr.ibr_ibru.ibru_if_name),
197				   "%s", val) >= IFNAMSIZ) {
198		errx(1, "interface name too long");
199	}
200	ibr.ibr_op = IF_BOND_OP_ADD_INTERFACE;
201	ifr.ifr_data = (caddr_t)&ibr;
202	if (ioctl(s, SIOCSIFBOND, (caddr_t)&ifr) == -1)
203		err(1, "SIOCSIFBOND add interface");
204
205	return;
206}
207
208static
209DECL_CMD_FUNC(unsetbonddev, val, d)
210{
211	struct if_bond_req		ibr;
212
213	bzero((char *)&ibr, sizeof(ibr));
214	if ((unsigned int)snprintf(ibr.ibr_ibru.ibru_if_name,
215				   sizeof(ibr.ibr_ibru.ibru_if_name),
216				   "%s", val) >= IFNAMSIZ) {
217		errx(1, "interface name too long");
218	}
219	ibr.ibr_op = IF_BOND_OP_REMOVE_INTERFACE;
220	ifr.ifr_data = (caddr_t)&ibr;
221	if (ioctl(s, SIOCSIFBOND, (caddr_t)&ifr) == -1)
222		err(1, "SIOCSIFBOND remove interface");
223
224	return;
225}
226
227static
228DECL_CMD_FUNC(setbondmode, val, d)
229{
230	struct if_bond_req		ibr;
231	int				mode;
232
233	if (strcmp(val, "lacp") == 0) {
234		mode = IF_BOND_MODE_LACP;
235	}
236	else if (strcmp(val, "static") == 0) {
237		mode = IF_BOND_MODE_STATIC;
238	}
239	else {
240		mode = strtoul(val, NULL, 0);
241		if (errno != 0) {
242			errx(1, "invalid mode value "
243			     "(must be either \"lacp\" or \"static\")");
244		}
245	}
246
247	bzero((char *)&ibr, sizeof(ibr));
248	if ((unsigned int)snprintf(ibr.ibr_ibru.ibru_if_name,
249				   sizeof(ibr.ibr_ibru.ibru_if_name),
250				   "%s", val) >= IFNAMSIZ) {
251		errx(1, "interface name too long");
252	}
253	ibr.ibr_op = IF_BOND_OP_SET_MODE;
254	ibr.ibr_ibru.ibru_int_val = mode;
255	ifr.ifr_data = (caddr_t)&ibr;
256	if (ioctl(s, SIOCSIFBOND, (caddr_t)&ifr) == -1)
257		err(1, "SIOCSIFBOND set mode");
258
259	return;
260}
261
262static struct cmd bond_cmds[] = {
263	DEF_CLONE_CMD_ARG("bonddev",		setbonddev),
264	DEF_CLONE_CMD_ARG("-bonddev",		unsetbonddev),
265	DEF_CMD_ARG("bondmode",				setbondmode),
266};
267static struct afswtch af_bond = {
268	.af_name	= "af_bond",
269	.af_af		= AF_UNSPEC,
270	.af_other_status = bond_status,
271};
272
273static __constructor void
274bond_ctor(void)
275{
276#define	N(a)	(sizeof(a) / sizeof(a[0]))
277	int i;
278
279	for (i = 0; i < N(bond_cmds);  i++)
280		cmd_register(&bond_cmds[i]);
281	af_register(&af_bond);
282#undef N
283}
284
285