aboutsummaryrefslogtreecommitdiffstats
path: root/scripts/basic/docproc.c
blob: 35bdc68b6e66604fc31f184fff4ad1c0e6c5398e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
/*
 *	docproc is a simple preprocessor for the template files
 *      used as placeholders for the kernel internal documentation.
 *	docproc is used for documentation-frontend and
 *      dependency-generator.
 *	The two usages have in common that they require
 *	some knowledge of the .tmpl syntax, therefore they
 *	are kept together.
 *
 *	documentation-frontend
 *		Scans the template file and call kernel-doc for
 *		all occurrences of ![EIF]file
 *		Beforehand each referenced file is scanned for
 *		any symbols that are exported via these macros:
 *			EXPORT_SYMBOL(), EXPORT_SYMBOL_GPL(), &
 *			EXPORT_SYMBOL_GPL_FUTURE()
 *		This is used to create proper -function and
 *		-nofunction arguments in calls to kernel-doc.
 *		Usage: docproc doc file.tmpl
 *
 *	dependency-generator:
 *		Scans the template file and list all files
 *		referenced in a format recognized by make.
 *		Usage:	docproc depend file.tmpl
 *		Writes dependency information to stdout
 *		in the following format:
 *		file.tmpl src.c	src2.c
 *		The filenames are obtained from the following constructs:
 *		!Efilename
 *		!Ifilename
 *		!Dfilename
 *		!Ffilename
 *		!Pfilename
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/wait.h>

/* exitstatus is used to keep track of any failing calls to kernel-doc,
 * but execution continues. */
int exitstatus = 0;

typedef void DFL(char *);
DFL *defaultline;

typedef void FILEONLY(char * file);
FILEONLY *internalfunctions;
FILEONLY *externalfunctions;
FILEONLY *symbolsonly;

typedef void FILELINE(char * file, char * line);
FILELINE * singlefunctions;
FILELINE * entity_system;
FILELINE * docsection;

#define MAXLINESZ     2048
#define MAXFILES      250
#define KERNELDOCPATH "scripts/"
#define KERNELDOC     "kernel-doc"
#define DOCBOOK       "-docbook"
#define FUNCTION      "-function"
#define NOFUNCTION    "-nofunction"
#define NODOCSECTIONS "-no-doc-sections"

char *srctree;

void usage (void)
{
	fprintf(stderr, "Usage: docproc {doc|depend} file\n");
	fprintf(stderr, "Input is read from file.tmpl. Output is sent to stdout\n");
	fprintf(stderr, "doc: frontend when generating kernel documentation\n");
	fprintf(stderr, "depend: generate list of files referenced within file\n");
	fprintf(stderr, "Environment variable SRCTREE: absolute path to kernel source tree.\n");
}

/*
 * Execute kernel-doc with parameters given in svec
 */
void exec_kernel_doc(char **svec)
{
	pid_t pid;
	int ret;
	char real_filename[PATH_MAX + 1];
	/* Make sure output generated so far are flushed */
	fflush(stdout);
	switch (pid=fork()) {
		case -1:
			perror("fork");
			exit(1);
		case  0:
			memset(real_filename, 0, sizeof(real_filename));
			strncat(real_filename, srctree, PATH_MAX);
			strncat(real_filename, KERNELDOCPATH KERNELDOC,
					PATH_MAX - strlen(real_filename));
			execvp(real_filename, svec);
			fprintf(stderr, "exec ");
			perror(real_filename);
			exit(1);
		default:
			waitpid(pid, &ret ,0);
	}
	if (WIFEXITED(ret))
		exitstatus |= WEXITSTATUS(ret);
	else
		exitstatus = 0xff;
}

/* Types used to create list of all exported symbols in a number of files */
struct symbols
{
	char *name;
};

struct symfile
{
	char *filename;
	struct symbols *symbollist;
	int symbolcnt;
};

struct symfile symfilelist[MAXFILES];
int symfilecnt = 0;

void add_new_symbol(struct symfile *sym, char * symname)
{
	sym->symbollist =
          realloc(sym->symbollist, (sym->symbolcnt + 1) * sizeof(char *));
	sym->symbollist[sym->symbolcnt++].name = strdup(symname);
}

/* Add a filename to the list */
struct symfile * add_new_file(char * filename)
{
	symfilelist[symfilecnt++].filename = strdup(filename);
	return &symfilelist[symfilecnt - 1];
}

/* Check if file already are present in the list */
struct symfile * filename_exist(char * filename)
{
	int i;
	for (i=0; i < symfilecnt; i++)
		if (strcmp(symfilelist[i].filename, filename) == 0)
			return &symfilelist[i];
	return NULL;
}

/*
 * List all files referenced within the template file.
 * Files are separated by tabs.
 */
void adddep(char * file)		   { printf("\t%s", file); }
void adddep2(char * file, char * line)     { line = line; adddep(file); }
void noaction(char * line)		   { line = line; }
void noaction2(char * file, char * line)   { file = file; line = line; }

/* Echo the line without further action */
void printline(char * line)               { printf("%s", line); }

/*
 * Find all symbols in filename that are exported with EXPORT_SYMBOL &
 * EXPORT_SYMBOL_GPL (& EXPORT_SYMBOL_GPL_FUTURE implicitly).
 * All symbols located are stored in symfilelist.
 */
void find_export_symbols(char * filename)
{
	FILE * fp;
	struct symfile *sym;
	char line[MAXLINESZ];
	if (filename_exist(filename) == NULL) {
		char real_filename[PATH_MAX + 1];
		memset(real_filename, 0, sizeof(real_filename));
		strncat(real_filename, srctree, PATH_MAX);
		strncat(real_filename, filename,
				PATH_MAX - strlen(real_filename));
		sym = add_new_file(filename);
		fp = fopen(real_filename, "r");
		if (fp == NULL)
		{
			fprintf(stderr, "docproc: ");
			perror(real_filename);
			exit(1);
		}
		while (fgets(line, MAXLINESZ, fp)) {
			char *p;
			char *e;
			if (((p = strstr(line, "EXPORT_SYMBOL_GPL")) != NULL) ||
                            ((p = strstr(line, "EXPORT_SYMBOL")) != NULL)) {
				/* Skip EXPORT_SYMBOL{_GPL} */
				while (isalnum(*p) || *p == '_')
					p++;
				/* Remove parentheses & additional whitespace */
				while (isspace(*p))
					p++;
				if (*p != '(')
					continue; /* Syntax error? */
				else
					p++;
				while (isspace(*p))
					p++;
				e = p;
				while (isalnum(*e) || *e == '_')
					e++;
				*e = '\0';
				add_new_symbol(sym, p);
			}
		}
		fclose(fp);
	}
}

/*
 * Document all external or internal functions in a file.
 * Call kernel-doc with following parameters:
 * kernel-doc -docbook -nofunction function_name1 filename
 * Function names are obtained from all the src files
 * by find_export_symbols.
 * intfunc uses -nofunction
 * extfunc uses -function
 */
void docfunctions(char * filename, char * type)
{
	int i,j;
	int symcnt = 0;
	int idx = 0;
	char **vec;

	for (i=0; i <= symfilecnt; i++)
		symcnt += symfilelist[i].symbolcnt;
	vec = malloc((2 + 2 * symcnt + 3) * sizeof(char *));
	if (vec == NULL) {
		perror("docproc: ");
		exit(1);
	}
	vec[idx++] = KERNELDOC;
	vec[idx++] = DOCBOOK;
	vec[idx++] = NODOCSECTIONS;
	for (i=0; i < symfilecnt; i++) {
		struct symfile * sym = &symfilelist[i];
		for (j=0; j < sym->symbolcnt; j++) {
			vec[idx++]     = type;
			vec[idx++] = sym->symbollist[j].name;
		}
	}
	vec[idx++]     = filename;
	vec[idx] = NULL;
	printf("<!-- %s -->\n", filename);
	exec_kernel_doc(vec);
	fflush(stdout);
	free(vec);
}
void intfunc(char * filename) {	docfunctions(filename, NOFUNCTION); }
void extfunc(char * filename) { docfunctions(filename, FUNCTION);   }

/*
 * Document specific function(s) in a file.
 * Call kernel-doc with the following parameters:
 * kernel-doc -docbook -function function1 [-function function2]
 */
void singfunc(char * filename, char * line)
{
	char *vec[200]; /* Enough for specific functions */
        int i, idx = 0;
        int startofsym = 1;
	vec[idx++] = KERNELDOC;
	vec[idx++] = DOCBOOK;

        /* Split line up in individual parameters preceded by FUNCTION */
        for (i=0; line[i]; i++) {
                if (isspace(line[i])) {
                        line[i] = '\0';
                        startofsym = 1;
                        continue;
                }
                if (startofsym) {
                        startofsym = 0;
                        vec[idx++] = FUNCTION;
                        vec[idx++] = &line[i];
                }
        }
	vec[idx++] = filename;
	vec[idx] = NULL;
	exec_kernel_doc(vec);
}

/*
 * Insert specific documentation section from a file.
 * Call kernel-doc with the following parameters:
 * kernel-doc -docbook -function "doc section" filename
 */
void docsect(char *filename, char *line)
{
	char *vec[6]; /* kerneldoc -docbook -function "section" file NULL */
	char *s;

	for (s = line; *s; s++)
		if (*s == '\n')
			*s = '\0';

	vec[0] = KERNELDOC;
	vec[1] = DOCBOOK;
	vec[2] = FUNCTION;
	vec[3] = line;
	vec[4] = filename;
	vec[5] = NULL;
	exec_kernel_doc(vec);
}

/*
 * Parse file, calling action specific functions for:
 * 1) Lines containing !E
 * 2) Lines containing !I
 * 3) Lines containing !D
 * 4) Lines containing !F
 * 5) Lines containing !P
 * 6) Default lines - lines not matching the above
 */
void parse_file(FILE *infile)
{
	char line[MAXLINESZ];
	char * s;
	while (fgets(line, MAXLINESZ, infile)) {
		if (line[0] == '!') {
			s = line + 2;
			switch (line[1]) {
				case 'E':
					while (*s && !isspace(*s)) s++;
					*s = '\0';
					externalfunctions(line+2);
					break;
				case 'I':
					while (*s && !isspace(*s)) s++;
					*s = '\0';
					internalfunctions(line+2);
					break;
				case 'D':
					while (*s && !isspace(*s)) s++;
                                        *s = '\0';
                                        symbolsonly(line+2);
                                        break;
				case 'F':
					/* filename */
					while (*s && !isspace(*s)) s++;
					*s++ = '\0';
                                        /* function names */
					while (isspace(*s))
						s++;
					singlefunctions(line +2, s);
					break;
				case 'P':
					/* filename */
					while (*s && !isspace(*s)) s++;
					*s++ = '\0';
					/* DOC: section name */
					while (isspace(*s))
						s++;
					docsection(line + 2, s);
					break;
				default:
					defaultline(line);
			}
		}
		else {
			defaultline(line);
		}
	}
	fflush(stdout);
}


int main(int argc, char *argv[])
{
	FILE * infile;

	srctree = getenv("SRCTREE");
	if (!srctree)
		srctree = getcwd(NULL, 0);
	if (argc != 3) {
		usage();
		exit(1);
	}
	/* Open file, exit on error */
	infile = fopen(argv[2], "r");
        if (infile == NULL) {
                fprintf(stderr, "docproc: ");
                perror(argv[2]);
                exit(2);
        }

	if (strcmp("doc", argv[1]) == 0)
	{
		/* Need to do this in two passes.
		 * First pass is used to collect all symbols exported
		 * in the various files;
		 * Second pass generate the documentation.
		 * This is required because some functions are declared
		 * and exported in different files :-((
		 */
		/* Collect symbols */
		defaultline       = noaction;
		internalfunctions = find_export_symbols;
		externalfunctions = find_export_symbols;
		symbolsonly       = find_export_symbols;
		singlefunctions   = noaction2;
		docsection        = noaction2;
		parse_file(infile);

		/* Rewind to start from beginning of file again */
		fseek(infile, 0, SEEK_SET);
		defaultline       = printline;
		internalfunctions = intfunc;
		externalfunctions = extfunc;
		symbolsonly       = printline;
		singlefunctions   = singfunc;
		docsection        = docsect;

		parse_file(infile);
	}
	else if (strcmp("depend", argv[1]) == 0)
	{
		/* Create first part of dependency chain
		 * file.tmpl */
		printf("%s\t", argv[2]);
		defaultline       = noaction;
		internalfunctions = adddep;
		externalfunctions = adddep;
		symbolsonly       = adddep;
		singlefunctions   = adddep2;
		docsection        = adddep2;
		parse_file(infile);
		printf("\n");
	}
	else
	{
		fprintf(stderr, "Unknown option: %s\n", argv[1]);
		exit(1);
	}
	fclose(infile);
	fflush(stdout);
	return exitstatus;
}
l kwb">static void if_print(char *ifname); static int get_drv_info(char *master_ifname); static int get_if_settings(char *ifname, struct dev_ifr ifra[]); static int get_slave_flags(char *slave_ifname); static int set_master_hwaddr(char *master_ifname, struct sockaddr *hwaddr); static int set_slave_hwaddr(char *slave_ifname, struct sockaddr *hwaddr); static int set_slave_mtu(char *slave_ifname, int mtu); static int set_if_flags(char *ifname, short flags); static int set_if_up(char *ifname, short flags); static int set_if_down(char *ifname, short flags); static int clear_if_addr(char *ifname); static int set_if_addr(char *master_ifname, char *slave_ifname); static int change_active(char *master_ifname, char *slave_ifname); static int enslave(char *master_ifname, char *slave_ifname); static int release(char *master_ifname, char *slave_ifname); #define v_print(fmt, args...) \ if (opt_v) \ fprintf(stderr, fmt, ## args ) int main(int argc, char *argv[]) { char **spp, *master_ifname, *slave_ifname; int c, i, rv; int res = 0; int exclusive = 0; while ((c = getopt_long(argc, argv, "acdfhuvV", longopts, 0)) != EOF) { switch (c) { case 'a': opt_a++; exclusive++; break; case 'c': opt_c++; exclusive++; break; case 'd': opt_d++; exclusive++; break; case 'f': opt_f++; exclusive++; break; case 'h': opt_h++; exclusive++; break; case 'u': opt_u++; exclusive++; break; case 'v': opt_v++; break; case 'V': opt_V++; exclusive++; break; case '?': fprintf(stderr, usage_msg); res = 2; goto out; } } /* options check */ if (exclusive > 1) { fprintf(stderr, usage_msg); res = 2; goto out; } if (opt_v || opt_V) { printf(version); if (opt_V) { res = 0; goto out; } } if (opt_u) { printf(usage_msg); res = 0; goto out; } if (opt_h) { printf(usage_msg); printf(help_msg); res = 0; goto out; } /* Open a basic socket */ if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket"); res = 1; goto out; } if (opt_a) { if (optind == argc) { /* No remaining args */ /* show all interfaces */ if_print((char *)NULL); goto out; } else { /* Just show usage */ fprintf(stderr, usage_msg); res = 2; goto out; } } /* Copy the interface name */ spp = argv + optind; master_ifname = *spp++; if (master_ifname == NULL) { fprintf(stderr, usage_msg); res = 2; goto out; } /* exchange abi version with bonding module */ res = get_drv_info(master_ifname); if (res) { fprintf(stderr, "Master '%s': Error: handshake with driver failed. " "Aborting\n", master_ifname); goto out; } slave_ifname = *spp++; if (slave_ifname == NULL) { if (opt_d || opt_c) { fprintf(stderr, usage_msg); res = 2; goto out; } /* A single arg means show the * configuration for this interface */ if_print(master_ifname); goto out; } res = get_if_settings(master_ifname, master_ifra); if (res) { /* Probably a good reason not to go on */ fprintf(stderr, "Master '%s': Error: get settings failed: %s. " "Aborting\n", master_ifname, strerror(res)); goto out; } /* check if master is indeed a master; * if not then fail any operation */ if (!(master_flags.ifr_flags & IFF_MASTER)) { fprintf(stderr, "Illegal operation; the specified interface '%s' " "is not a master. Aborting\n", master_ifname); res = 1; goto out; } /* check if master is up; if not then fail any operation */ if (!(master_flags.ifr_flags & IFF_UP)) { fprintf(stderr, "Illegal operation; the specified master interface " "'%s' is not up.\n", master_ifname); res = 1; goto out; } /* Only for enslaving */ if (!opt_c && !opt_d) { sa_family_t master_family = master_hwaddr.ifr_hwaddr.sa_family; unsigned char *hwaddr = (unsigned char *)master_hwaddr.ifr_hwaddr.sa_data; /* The family '1' is ARPHRD_ETHER for ethernet. */ if (master_family != 1 && !opt_f) { fprintf(stderr, "Illegal operation: The specified master " "interface '%s' is not ethernet-like.\n " "This program is designed to work with " "ethernet-like network interfaces.\n " "Use the '-f' option to force the " "operation.\n", master_ifname); res = 1; goto out; } /* Check master's hw addr */ for (i = 0; i < 6; i++) { if (hwaddr[i] != 0) { hwaddr_set = 1; break; } } if (hwaddr_set) { v_print("current hardware address of master '%s' " "is %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, " "type %d\n", master_ifname, hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5], master_family); } } /* Accepts only one slave */ if (opt_c) { /* change active slave */ res = get_slave_flags(slave_ifname); if (res) { fprintf(stderr, "Slave '%s': Error: get flags failed. " "Aborting\n", slave_ifname); goto out; } res = change_active(master_ifname, slave_ifname); if (res) { fprintf(stderr, "Master '%s', Slave '%s': Error: " "Change active failed\n", master_ifname, slave_ifname); } } else { /* Accept multiple slaves */ do { if (opt_d) { /* detach a slave interface from the master */ rv = get_slave_flags(slave_ifname); if (rv) { /* Can't work with this slave. */ /* remember the error and skip it*/ fprintf(stderr, "Slave '%s': Error: get flags " "failed. Skipping\n", slave_ifname); res = rv; continue; } rv = release(master_ifname, slave_ifname); if (rv) { fprintf(stderr, "Master '%s', Slave '%s': Error: " "Release failed\n", master_ifname, slave_ifname); res = rv; } } else { /* attach a slave interface to the master */ rv = get_if_settings(slave_ifname, slave_ifra); if (rv) { /* Can't work with this slave. */ /* remember the error and skip it*/ fprintf(stderr, "Slave '%s': Error: get " "settings failed: %s. " "Skipping\n", slave_ifname, strerror(rv)); res = rv; continue; } rv = enslave(master_ifname, slave_ifname); if (rv) { fprintf(stderr, "Master '%s', Slave '%s': Error: " "Enslave failed\n", master_ifname, slave_ifname); res = rv; } } } while ((slave_ifname = *spp++) != NULL); } out: if (skfd >= 0) { close(skfd); } return res; } static short mif_flags; /* Get the inteface configuration from the kernel. */ static int if_getconfig(char *ifname) { struct ifreq ifr; int metric, mtu; /* Parameters of the master interface. */ struct sockaddr dstaddr, broadaddr, netmask; unsigned char *hwaddr; strcpy(ifr.ifr_name, ifname); if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) return -1; mif_flags = ifr.ifr_flags; printf("The result of SIOCGIFFLAGS on %s is %x.\n", ifname, ifr.ifr_flags); strcpy(ifr.ifr_name, ifname); if (ioctl(skfd, SIOCGIFADDR, &ifr) < 0) return -1; printf("The result of SIOCGIFADDR is %2.2x.%2.2x.%2.2x.%2.2x.\n", ifr.ifr_addr.sa_data[0], ifr.ifr_addr.sa_data[1], ifr.ifr_addr.sa_data[2], ifr.ifr_addr.sa_data[3]); strcpy(ifr.ifr_name, ifname); if (ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0) return -1; /* Gotta convert from 'char' to unsigned for printf(). */ hwaddr = (unsigned char *)ifr.ifr_hwaddr.sa_data; printf("The result of SIOCGIFHWADDR is type %d " "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n", ifr.ifr_hwaddr.sa_family, hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]); strcpy(ifr.ifr_name, ifname); if (ioctl(skfd, SIOCGIFMETRIC, &ifr) < 0) { metric = 0; } else metric = ifr.ifr_metric; strcpy(ifr.ifr_name, ifname); if (ioctl(skfd, SIOCGIFMTU, &ifr) < 0) mtu = 0; else mtu = ifr.ifr_mtu; strcpy(ifr.ifr_name, ifname); if (ioctl(skfd, SIOCGIFDSTADDR, &ifr) < 0) { memset(&dstaddr, 0, sizeof(struct sockaddr)); } else dstaddr = ifr.ifr_dstaddr; strcpy(ifr.ifr_name, ifname); if (ioctl(skfd, SIOCGIFBRDADDR, &ifr) < 0) { memset(&broadaddr, 0, sizeof(struct sockaddr)); } else broadaddr = ifr.ifr_broadaddr; strcpy(ifr.ifr_name, ifname); if (ioctl(skfd, SIOCGIFNETMASK, &ifr) < 0) { memset(&netmask, 0, sizeof(struct sockaddr)); } else netmask = ifr.ifr_netmask; return 0; } static void if_print(char *ifname) { char buff[1024]; struct ifconf ifc; struct ifreq *ifr; int i; if (ifname == (char *)NULL) { ifc.ifc_len = sizeof(buff); ifc.ifc_buf = buff; if (ioctl(skfd, SIOCGIFCONF, &ifc) < 0) { perror("SIOCGIFCONF failed"); return; } ifr = ifc.ifc_req; for (i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; ifr++) { if (if_getconfig(ifr->ifr_name) < 0) { fprintf(stderr, "%s: unknown interface.\n", ifr->ifr_name); continue; } if (((mif_flags & IFF_UP) == 0) && !opt_a) continue; /*ife_print(&ife);*/ } } else { if (if_getconfig(ifname) < 0) { fprintf(stderr, "%s: unknown interface.\n", ifname); } } } static int get_drv_info(char *master_ifname) { struct ifreq ifr; struct ethtool_drvinfo info; char *endptr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); ifr.ifr_data = (caddr_t)&info; info.cmd = ETHTOOL_GDRVINFO; strncpy(info.driver, "ifenslave", 32); snprintf(info.fw_version, 32, "%d", BOND_ABI_VERSION); if (ioctl(skfd, SIOCETHTOOL, &ifr) < 0) { if (errno == EOPNOTSUPP) { goto out; } saved_errno = errno; v_print("Master '%s': Error: get bonding info failed %s\n", master_ifname, strerror(saved_errno)); return 1; } abi_ver = strtoul(info.fw_version, &endptr, 0); if (*endptr) { v_print("Master '%s': Error: got invalid string as an ABI " "version from the bonding module\n", master_ifname); return 1; } out: v_print("ABI ver is %d\n", abi_ver); return 0; } static int change_active(char *master_ifname, char *slave_ifname) { struct ifreq ifr; int res = 0; if (!(slave_flags.ifr_flags & IFF_SLAVE)) { fprintf(stderr, "Illegal operation: The specified slave interface " "'%s' is not a slave\n", slave_ifname); return 1; } strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); strncpy(ifr.ifr_slave, slave_ifname, IFNAMSIZ); if ((ioctl(skfd, SIOCBONDCHANGEACTIVE, &ifr) < 0) && (ioctl(skfd, BOND_CHANGE_ACTIVE_OLD, &ifr) < 0)) { saved_errno = errno; v_print("Master '%s': Error: SIOCBONDCHANGEACTIVE failed: " "%s\n", master_ifname, strerror(saved_errno)); res = 1; } return res; } static int enslave(char *master_ifname, char *slave_ifname) { struct ifreq ifr; int res = 0; if (slave_flags.ifr_flags & IFF_SLAVE) { fprintf(stderr, "Illegal operation: The specified slave interface " "'%s' is already a slave\n", slave_ifname); return 1; } res = set_if_down(slave_ifname, slave_flags.ifr_flags); if (res) { fprintf(stderr, "Slave '%s': Error: bring interface down failed\n", slave_ifname); return res; } if (abi_ver < 2) { /* Older bonding versions would panic if the slave has no IP * address, so get the IP setting from the master. */ set_if_addr(master_ifname, slave_ifname); } else { res = clear_if_addr(slave_ifname); if (res) { fprintf(stderr, "Slave '%s': Error: clear address failed\n", slave_ifname); return res; } } if (master_mtu.ifr_mtu != slave_mtu.ifr_mtu) { res = set_slave_mtu(slave_ifname, master_mtu.ifr_mtu); if (res) { fprintf(stderr, "Slave '%s': Error: set MTU failed\n", slave_ifname); return res; } } if (hwaddr_set) { /* Master already has an hwaddr * so set it's hwaddr to the slave */ if (abi_ver < 1) { /* The driver is using an old ABI, so * the application sets the slave's * hwaddr */ res = set_slave_hwaddr(slave_ifname, &(master_hwaddr.ifr_hwaddr)); if (res) { fprintf(stderr, "Slave '%s': Error: set hw address " "failed\n", slave_ifname); goto undo_mtu; } /* For old ABI the application needs to bring the * slave back up */ res = set_if_up(slave_ifname, slave_flags.ifr_flags); if (res) { fprintf(stderr, "Slave '%s': Error: bring interface " "down failed\n", slave_ifname); goto undo_slave_mac; } } /* The driver is using a new ABI, * so the driver takes care of setting * the slave's hwaddr and bringing * it up again */ } else { /* No hwaddr for master yet, so * set the slave's hwaddr to it */ if (abi_ver < 1) { /* For old ABI, the master needs to be * down before setting its hwaddr */ res = set_if_down(master_ifname, master_flags.ifr_flags); if (res) { fprintf(stderr, "Master '%s': Error: bring interface " "down failed\n", master_ifname); goto undo_mtu; } } res = set_master_hwaddr(master_ifname, &(slave_hwaddr.ifr_hwaddr)); if (res) { fprintf(stderr, "Master '%s': Error: set hw address " "failed\n", master_ifname); goto undo_mtu; } if (abi_ver < 1) { /* For old ABI, bring the master * back up */ res = set_if_up(master_ifname, master_flags.ifr_flags); if (res) { fprintf(stderr, "Master '%s': Error: bring interface " "up failed\n", master_ifname); goto undo_master_mac; } } hwaddr_set = 1; } /* Do the real thing */ strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); strncpy(ifr.ifr_slave, slave_ifname, IFNAMSIZ); if ((ioctl(skfd, SIOCBONDENSLAVE, &ifr) < 0) && (ioctl(skfd, BOND_ENSLAVE_OLD, &ifr) < 0)) { saved_errno = errno; v_print("Master '%s': Error: SIOCBONDENSLAVE failed: %s\n", master_ifname, strerror(saved_errno)); res = 1; } if (res) { goto undo_master_mac; } return 0; /* rollback (best effort) */ undo_master_mac: set_master_hwaddr(master_ifname, &(master_hwaddr.ifr_hwaddr)); hwaddr_set = 0; goto undo_mtu; undo_slave_mac: set_slave_hwaddr(slave_ifname, &(slave_hwaddr.ifr_hwaddr)); undo_mtu: set_slave_mtu(slave_ifname, slave_mtu.ifr_mtu); return res; } static int release(char *master_ifname, char *slave_ifname) { struct ifreq ifr; int res = 0; if (!(slave_flags.ifr_flags & IFF_SLAVE)) { fprintf(stderr, "Illegal operation: The specified slave interface " "'%s' is not a slave\n", slave_ifname); return 1; } strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); strncpy(ifr.ifr_slave, slave_ifname, IFNAMSIZ); if ((ioctl(skfd, SIOCBONDRELEASE, &ifr) < 0) && (ioctl(skfd, BOND_RELEASE_OLD, &ifr) < 0)) { saved_errno = errno; v_print("Master '%s': Error: SIOCBONDRELEASE failed: %s\n", master_ifname, strerror(saved_errno)); return 1; } else if (abi_ver < 1) { /* The driver is using an old ABI, so we'll set the interface * down to avoid any conflicts due to same MAC/IP */ res = set_if_down(slave_ifname, slave_flags.ifr_flags); if (res) { fprintf(stderr, "Slave '%s': Error: bring interface " "down failed\n", slave_ifname); } } /* set to default mtu */ set_slave_mtu(slave_ifname, 1500); return res; } static int get_if_settings(char *ifname, struct dev_ifr ifra[]) { int i; int res = 0; for (i = 0; ifra[i].req_ifr; i++) { strncpy(ifra[i].req_ifr->ifr_name, ifname, IFNAMSIZ); res = ioctl(skfd, ifra[i].req_type, ifra[i].req_ifr); if (res < 0) { saved_errno = errno; v_print("Interface '%s': Error: %s failed: %s\n", ifname, ifra[i].req_name, strerror(saved_errno)); return saved_errno; } } return 0; } static int get_slave_flags(char *slave_ifname) { int res = 0; strncpy(slave_flags.ifr_name, slave_ifname, IFNAMSIZ); res = ioctl(skfd, SIOCGIFFLAGS, &slave_flags); if (res < 0) { saved_errno = errno; v_print("Slave '%s': Error: SIOCGIFFLAGS failed: %s\n", slave_ifname, strerror(saved_errno)); } else { v_print("Slave %s: flags %04X.\n", slave_ifname, slave_flags.ifr_flags); } return res; } static int set_master_hwaddr(char *master_ifname, struct sockaddr *hwaddr) { unsigned char *addr = (unsigned char *)hwaddr->sa_data; struct ifreq ifr; int res = 0; strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); memcpy(&(ifr.ifr_hwaddr), hwaddr, sizeof(struct sockaddr)); res = ioctl(skfd, SIOCSIFHWADDR, &ifr); if (res < 0) { saved_errno = errno; v_print("Master '%s': Error: SIOCSIFHWADDR failed: %s\n", master_ifname, strerror(saved_errno)); return res; } else { v_print("Master '%s': hardware address set to " "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n", master_ifname, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); } return res; } static int set_slave_hwaddr(char *slave_ifname, struct sockaddr *hwaddr) { unsigned char *addr = (unsigned char *)hwaddr->sa_data; struct ifreq ifr; int res = 0; strncpy(ifr.ifr_name, slave_ifname, IFNAMSIZ); memcpy(&(ifr.ifr_hwaddr), hwaddr, sizeof(struct sockaddr)); res = ioctl(skfd, SIOCSIFHWADDR, &ifr); if (res < 0) { saved_errno = errno; v_print("Slave '%s': Error: SIOCSIFHWADDR failed: %s\n", slave_ifname, strerror(saved_errno)); if (saved_errno == EBUSY) { v_print(" The device is busy: it must be idle " "before running this command.\n"); } else if (saved_errno == EOPNOTSUPP) { v_print(" The device does not support setting " "the MAC address.\n" " Your kernel likely does not support slave " "devices.\n"); } else if (saved_errno == EINVAL) { v_print(" The device's address type does not match " "the master's address type.\n"); } return res; } else { v_print("Slave '%s': hardware address set to " "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n", slave_ifname, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); } return res; } static int set_slave_mtu(char *slave_ifname, int mtu) { struct ifreq ifr; int res = 0; ifr.ifr_mtu = mtu; strncpy(ifr.ifr_name, slave_ifname, IFNAMSIZ); res = ioctl(skfd, SIOCSIFMTU, &ifr); if (res < 0) { saved_errno = errno; v_print("Slave '%s': Error: SIOCSIFMTU failed: %s\n", slave_ifname, strerror(saved_errno)); } else { v_print("Slave '%s': MTU set to %d.\n", slave_ifname, mtu); } return res; } static int set_if_flags(char *ifname, short flags) { struct ifreq ifr; int res = 0; ifr.ifr_flags = flags; strncpy(ifr.ifr_name, ifname, IFNAMSIZ); res = ioctl(skfd, SIOCSIFFLAGS, &ifr); if (res < 0) { saved_errno = errno; v_print("Interface '%s': Error: SIOCSIFFLAGS failed: %s\n", ifname, strerror(saved_errno)); } else { v_print("Interface '%s': flags set to %04X.\n", ifname, flags); } return res; } static int set_if_up(char *ifname, short flags) { return set_if_flags(ifname, flags | IFF_UP); } static int set_if_down(char *ifname, short flags) { return set_if_flags(ifname, flags & ~IFF_UP); } static int clear_if_addr(char *ifname) { struct ifreq ifr; int res = 0; strncpy(ifr.ifr_name, ifname, IFNAMSIZ); ifr.ifr_addr.sa_family = AF_INET; memset(ifr.ifr_addr.sa_data, 0, sizeof(ifr.ifr_addr.sa_data)); res = ioctl(skfd, SIOCSIFADDR, &ifr); if (res < 0) { saved_errno = errno; v_print("Interface '%s': Error: SIOCSIFADDR failed: %s\n", ifname, strerror(saved_errno)); } else { v_print("Interface '%s': address cleared\n", ifname); } return res; } static int set_if_addr(char *master_ifname, char *slave_ifname) { struct ifreq ifr; int res; unsigned char *ipaddr; int i; struct { char *req_name; char *desc; int g_ioctl; int s_ioctl; } ifra[] = { {"IFADDR", "addr", SIOCGIFADDR, SIOCSIFADDR}, {"DSTADDR", "destination addr", SIOCGIFDSTADDR, SIOCSIFDSTADDR}, {"BRDADDR", "broadcast addr", SIOCGIFBRDADDR, SIOCSIFBRDADDR}, {"NETMASK", "netmask", SIOCGIFNETMASK, SIOCSIFNETMASK}, {NULL, NULL, 0, 0}, }; for (i = 0; ifra[i].req_name; i++) { strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); res = ioctl(skfd, ifra[i].g_ioctl, &ifr); if (res < 0) { int saved_errno = errno; v_print("Interface '%s': Error: SIOCG%s failed: %s\n", master_ifname, ifra[i].req_name, strerror(saved_errno)); ifr.ifr_addr.sa_family = AF_INET; memset(ifr.ifr_addr.sa_data, 0, sizeof(ifr.ifr_addr.sa_data)); } strncpy(ifr.ifr_name, slave_ifname, IFNAMSIZ); res = ioctl(skfd, ifra[i].s_ioctl, &ifr);