xref: /haiku/src/bin/network/ifconfig/ifconfig.cpp (revision 9ecf9d1c1d4888d341a6eac72112c72d1ae3a4cb)
1 /*
2  * Copyright 2006, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Axel Dörfler, axeld@pinc-software.de
7  *		Oliver Tappe, zooey@hirschkaefer.de
8  */
9 
10 
11 #include <SupportDefs.h>
12 
13 #include <arpa/inet.h>
14 #include <net/if.h>
15 #include <net/if_dl.h>
16 #include <net/if_types.h>
17 #include <netinet/in.h>
18 #include <sys/socket.h>
19 #include <sys/sockio.h>
20 
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 
27 
28 extern const char* __progname;
29 const char* kProgramName = __progname;
30 
31 
32 struct address_family {
33 	int			family;
34 	const char*	name;
35 	const char*	identifiers[4];
36 	bool		(*parse_address)(const char* string, sockaddr* _address);
37 	void		(*print_address)(sockaddr* address);
38 };
39 
40 // AF_INET family
41 static bool inet_parse_address(const char* string, sockaddr* address);
42 static void inet_print_address(sockaddr* address);
43 
44 static const address_family kFamilies[] = {
45 	{
46 		AF_INET,
47 		"inet",
48 		{"AF_INET", "inet", "ipv4", NULL},
49 		inet_parse_address,
50 		inet_print_address
51 	},
52 	{ -1, NULL, {NULL}, NULL, NULL }
53 };
54 
55 
56 static bool
57 inet_parse_address(const char* string, sockaddr* _address)
58 {
59 	in_addr inetAddress;
60 
61 	if (inet_aton(string, &inetAddress) != 1)
62 		return false;
63 
64 	sockaddr_in& address = *(sockaddr_in *)_address;
65 	address.sin_family = AF_INET;
66 	address.sin_len = sizeof(struct sockaddr_in);
67 	address.sin_port = 0;
68 	address.sin_addr = inetAddress;
69 	memset(&address.sin_zero[0], 0, sizeof(address.sin_zero));
70 
71 	return true;
72 }
73 
74 
75 static void
76 inet_print_address(sockaddr* _address)
77 {
78 	sockaddr_in& address = *(sockaddr_in *)_address;
79 
80 	if (address.sin_family != AF_INET)
81 		return;
82 
83 	printf("%s", inet_ntoa(address.sin_addr));
84 }
85 
86 
87 //	#pragma mark -
88 
89 
90 void
91 usage(int status)
92 {
93 	printf("usage: %s [<interface> [<address family>] [<address> [<mask>]] [<option/flags>...]]\n"
94 		"\t%s --delete interface [...]\n\n"
95 		"Where <option> can be the following:\n"
96 		"  netmask <addr>   - networking subnet mask\n"
97 		"  broadcast <addr> - set broadcast address\n"
98 		"  peer <addr>      - ppp-peer address\n"
99 		"  mtu <bytes>      - maximal transfer unit\n"
100 		"  metric <number>  - metric number to use (defaults to 0)\n"
101 		"And <flags> can be: up, down, [-]promisc, [-]allmulti, [-]bcast, loopback\n\n"
102 		"Example:\n"
103 		"\t%s loop 127.0.0.1 255.0.0.0 up\n",
104 		kProgramName, kProgramName, kProgramName);
105 
106 	exit(status);
107 }
108 
109 
110 bool
111 prepare_request(struct ifreq& request, const char* name)
112 {
113 	if (strlen(name) > IF_NAMESIZE) {
114 		fprintf(stderr, "%s: interface name \"%s\" is too long.\n", kProgramName, name);
115 		return false;
116 	}
117 
118 	strcpy(request.ifr_name, name);
119 	return true;
120 }
121 
122 
123 bool
124 get_address_family(const char* argument, int32& familyIndex)
125 {
126 	for (int32 i = 0; kFamilies[i].family >= 0; i++) {
127 		for (int32 j = 0; kFamilies[i].identifiers[j]; j++) {
128 			if (!strcmp(argument, kFamilies[i].identifiers[j])) {
129 				// found a match
130 				familyIndex = i;
131 				return true;
132 			}
133 		}
134 	}
135 
136 	// defaults to AF_INET
137 	familyIndex = 0;
138 	return false;
139 }
140 
141 
142 bool
143 parse_address(int32 familyIndex, const char* argument, struct sockaddr& address)
144 {
145 	if (argument == NULL)
146 		return false;
147 
148 	return kFamilies[familyIndex].parse_address(argument, &address);
149 }
150 
151 
152 //	#pragma mark -
153 
154 
155 void
156 list_interface(int socket, const char* name)
157 {
158 	ifreq request;
159 	if (!prepare_request(request, name))
160 		return;
161 
162 	if (ioctl(socket, SIOCGIFINDEX, &request, sizeof(request)) < 0) {
163 		fprintf(stderr, "%s: Interface \"%s\" does not exist.\n", kProgramName, name);
164 		return;
165 	}
166 
167 	printf("%s", name);
168 	size_t length = strlen(name);
169 	if (length < 8)
170 		putchar('\t');
171 	else
172 		printf("\n\t");
173 
174 	// get link level interface for this interface
175 
176 	int linkSocket = ::socket(AF_LINK, SOCK_DGRAM, 0);
177 	if (linkSocket < 0) {
178 		printf("No link level: %s\n", strerror(errno));
179 	} else {
180 		const char *type = "unknown";
181 		char address[256];
182 		strcpy(address, "none");
183 
184 		if (ioctl(socket, SIOCGIFPARAM, &request, sizeof(struct ifreq)) == 0) {
185 			prepare_request(request, request.ifr_parameter.device);
186 			if (ioctl(linkSocket, SIOCGIFADDR, &request, sizeof(struct ifreq)) == 0) {
187 				sockaddr_dl &link = *(sockaddr_dl *)&request.ifr_addr;
188 
189 				switch (link.sdl_type) {
190 					case IFT_ETHER:
191 					{
192 						type = "Ethernet";
193 
194 						if (link.sdl_alen > 0) {
195 							uint8 *mac = (uint8 *)LLADDR(&link);
196 							sprintf(address, "%02x:%02x:%02x:%02x:%02x:%02x",
197 								mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
198 						} else
199 							strcpy(address, "not available");
200 						break;
201 					}
202 					case IFT_LOOP:
203 						type = "Local Loopback";
204 						break;
205 					case IFT_MODEM:
206 						type = "Modem";
207 						break;
208 				}
209 			}
210 		}
211 
212 		printf("Hardware Type: %s, Address: %s\n", type, address);
213 		close(linkSocket);
214 	}
215 
216 	uint32 flags = 0;
217 	if (ioctl(socket, SIOCGIFFLAGS, &request, sizeof(struct ifreq)) == 0)
218 		flags = request.ifr_flags;
219 
220 	for (int32 i = 0; kFamilies[i].family >= 0; i++) {
221 		int familySocket = ::socket(kFamilies[i].family, SOCK_DGRAM, 0);
222 		if (familySocket < 0)
223 			continue;
224 
225 		if (ioctl(familySocket, SIOCGIFADDR, &request, sizeof(struct ifreq)) == 0) {
226 			printf("\t%s addr: ", kFamilies[i].name);
227 			kFamilies[i].print_address(&request.ifr_addr);
228 
229 			if ((flags & IFF_BROADCAST) != 0
230 				&& ioctl(familySocket, SIOCGIFBRDADDR, &request, sizeof(struct ifreq)) == 0
231 				&& request.ifr_broadaddr.sa_family == kFamilies[i].family) {
232 				printf(", Bcast: ");
233 				kFamilies[i].print_address(&request.ifr_broadaddr);
234 			}
235 			if (ioctl(familySocket, SIOCGIFNETMASK, &request, sizeof(struct ifreq)) == 0
236 				&& request.ifr_mask.sa_family == kFamilies[i].family) {
237 				printf(", Mask: ");
238 				kFamilies[i].print_address(&request.ifr_mask);
239 			}
240 			putchar('\n');
241 		}
242 
243 		close(familySocket);
244 	}
245 
246 	// Print MTU, metric, flags
247 
248 	printf("\tMTU: ");
249 	if (ioctl(socket, SIOCGIFMTU, &request, sizeof(struct ifreq)) == 0)
250 		printf("%d", request.ifr_mtu);
251 	else
252 		printf("-");
253 
254 	printf(", Metric: ");
255 	if (ioctl(socket, SIOCGIFMETRIC, &request, sizeof(struct ifreq)) == 0)
256 		printf("%d", request.ifr_metric);
257 	else
258 		printf("-");
259 
260 	if (flags != 0) {
261 		const struct {
262 			int			value;
263 			const char	*name;
264 		} kFlags[] = {
265 			{IFF_UP, "up"},
266 			{IFF_NOARP, "noarp"},
267 			{IFF_BROADCAST, "broadcast"},
268 			{IFF_LOOPBACK, "loopback"},
269 			{IFF_PROMISC, "promiscuous"},
270 			{IFF_ALLMULTI, "allmulti"},
271 			{IFF_AUTOUP, "autoup"},
272 		};
273 		bool first = true;
274 
275 		for (uint32 i = 0; i < sizeof(kFlags) / sizeof(kFlags[0]); i++) {
276 			if ((flags & kFlags[i].value) != 0) {
277 				if (first) {
278 					printf(",");
279 					first = false;
280 				}
281 				putchar(' ');
282 				printf(kFlags[i].name);
283 			}
284 		}
285 	}
286 
287 	putchar('\n');
288 
289 	// Print statistics
290 
291 	if (ioctl(socket, SIOCGIFSTATS, &request, sizeof(struct ifreq)) == 0) {
292 		printf("\tReceive: %ld packets, %ld errors, %Ld bytes, %ld mcasts, %ld dropped\n",
293 			request.ifr_stats.receive.packets, request.ifr_stats.receive.errors,
294 			request.ifr_stats.receive.bytes, request.ifr_stats.receive.multicast_packets,
295 			request.ifr_stats.receive.dropped);
296 		printf("\tTransmit: %ld packets, %ld errors, %Ld bytes, %ld mcasts, %ld dropped\n",
297 			request.ifr_stats.send.packets, request.ifr_stats.send.errors,
298 			request.ifr_stats.send.bytes, request.ifr_stats.send.multicast_packets,
299 			request.ifr_stats.send.dropped);
300 		printf("\tCollisions: %ld\n", request.ifr_stats.collisions);
301 	}
302 
303 	putchar('\n');
304 }
305 
306 
307 void
308 list_interfaces(int socket, const char* name)
309 {
310 	if (name != NULL) {
311 		list_interface(socket, name);
312 		return;
313 	}
314 
315 	// get a list of all interfaces
316 
317 	ifconf config;
318 	config.ifc_len = sizeof(config.ifc_value);
319 	if (ioctl(socket, SIOCGIFCOUNT, &config, sizeof(struct ifconf)) < 0)
320 		return;
321 
322 	uint32 count = (uint32)config.ifc_value;
323 	if (count == 0) {
324 		fprintf(stderr, "%s: There are no interfaces yet!\n", kProgramName);
325 		return;
326 	}
327 
328 	void *buffer = malloc(count * sizeof(struct ifreq));
329 	if (buffer == NULL) {
330 		fprintf(stderr, "%s: Out of memory.\n", kProgramName);
331 		return;
332 	}
333 
334 	config.ifc_len = count * sizeof(struct ifreq);
335 	config.ifc_buf = buffer;
336 	if (ioctl(socket, SIOCGIFCONF, &config, sizeof(struct ifconf)) < 0)
337 		return;
338 
339 	ifreq *interface = (ifreq *)buffer;
340 
341 	for (uint32 i = 0; i < count; i++) {
342 		list_interface(socket, interface->ifr_name);
343 
344 		interface = (ifreq *)((addr_t)interface + IF_NAMESIZE + interface->ifr_addr.sa_len);
345 	}
346 
347 	free(buffer);
348 }
349 
350 
351 void
352 delete_interface(int socket, const char* name)
353 {
354 	ifreq request;
355 	if (!prepare_request(request, name))
356 		return;
357 
358 	if (ioctl(socket, SIOCDIFADDR, &request, sizeof(request)) < 0) {
359 		fprintf(stderr, "%s: Could not delete interface %s: %s\n",
360 			kProgramName, name, strerror(errno));
361 	}
362 }
363 
364 
365 void
366 configure_interface(int socket, const char* name, char* const* args,
367 	int32 argCount)
368 {
369 	ifreq request;
370 	if (!prepare_request(request, name))
371 		return;
372 
373 	uint32 index = 0;
374 	if (ioctl(socket, SIOCGIFINDEX, &request, sizeof(request)) >= 0)
375 		index = request.ifr_index;
376 
377 	bool hasAddress = false, hasMask = false, hasPeer = false, hasBroadcast = false;
378 	struct sockaddr address, mask, peer, broadcast;
379 	int mtu = -1, metric = -1, addFlags = 0, removeFlags = 0, currentFlags = 0;
380 
381 	// try to parse address family
382 
383 	int32 familyIndex;
384 	int32 i = 0;
385 	if (get_address_family(args[i], familyIndex))
386 		i++;
387 
388 	if (kFamilies[familyIndex].family != AF_INET) {
389 		close(socket);
390 
391 		// replace socket with one of the correct address family
392 		socket = ::socket(kFamilies[familyIndex].family, SOCK_DGRAM, 0);
393 		if (socket < 0) {
394 			fprintf(stderr, "%s: Address family \"%s\" is not available.\n",
395 				kProgramName, kFamilies[familyIndex].name);
396 			exit(1);
397 		}
398 	}
399 
400 	if (index == 0) {
401 		// the interface does not exist yet, we have to add it first
402 		request.ifr_parameter.base_name[0] = '\0';
403 		request.ifr_parameter.device[0] = '\0';
404 		request.ifr_parameter.sub_type = 0;
405 			// the default device is okay for us
406 
407 		if (ioctl(socket, SIOCAIFADDR, &request, sizeof(request)) < 0) {
408 			fprintf(stderr, "%s: Could not add interface: %s\n", kProgramName,
409 				strerror(errno));
410 			exit(1);
411 		}
412 	}
413 
414 	// try to parse address
415 
416 	if (parse_address(familyIndex, args[i], address)) {
417 		hasAddress = true;
418 		i++;
419 
420 		if (parse_address(familyIndex, args[i], mask)) {
421 			hasMask = true;
422 			i++;
423 		}
424 	}
425 
426 	// parse parameters and flags
427 
428 	while (i < argCount) {
429 		if (!strcmp(args[i], "peer")) {
430 			if (!parse_address(familyIndex, args[i + 1], peer)) {
431 				fprintf(stderr, "%s: Option 'peer' needs valid address parameter\n",
432 					kProgramName);
433 				exit(1);
434 			}
435 			hasPeer = true;
436 			i++;
437 		} else if (!strcmp(args[i], "nm") || !strcmp(args[i], "netmask")) {
438 			if (hasMask) {
439 				fprintf(stderr, "%s: Netmask is specified twice\n",
440 					kProgramName);
441 				exit(1);
442 			}
443 			if (!parse_address(familyIndex, args[i + 1], mask)) {
444 				fprintf(stderr, "%s: Option 'netmask' needs valid address parameter\n",
445 					kProgramName);
446 				exit(1);
447 			}
448 			hasMask = true;
449 			i++;
450 		} else if (!strcmp(args[i], "bc") || !strcmp(args[i], "broadcast")) {
451 			if (hasBroadcast) {
452 				fprintf(stderr, "%s: broadcast address is specified twice\n",
453 					kProgramName);
454 				exit(1);
455 			}
456 			if (!parse_address(familyIndex, args[i + 1], broadcast)) {
457 				fprintf(stderr, "%s: Option 'broadcast' needs valid address parameter\n",
458 					kProgramName);
459 				exit(1);
460 			}
461 			hasBroadcast = true;
462 			addFlags |= IFF_BROADCAST;
463 			i++;
464 		} else if (!strcmp(args[i], "mtu")) {
465 			mtu = args[i + 1] ? strtol(args[i + 1], NULL, 0) : 0;
466 			if (mtu <= 500) {
467 				fprintf(stderr, "%s: Option 'mtu' expected valid max transfer unit size\n",
468 					kProgramName);
469 				exit(1);
470 			}
471 			i++;
472 		} else if (!strcmp(args[i], "metric")) {
473 			if (i + 1 >= argCount) {
474 				fprintf(stderr, "%s: Option 'metric' exptected parameter\n",
475 					kProgramName);
476 				exit(1);
477 			}
478 			metric = strtol(args[i + 1], NULL, 0);
479 			i++;
480 		} else if (!strcmp(args[i], "up") || !strcmp(args[i], "-down")) {
481 			addFlags |= IFF_UP;
482 		} else if (!strcmp(args[i], "down") || !strcmp(args[i], "-up")) {
483 			removeFlags |= IFF_UP;
484 		} else if (!strcmp(args[i], "bcast")) {
485 			addFlags |= IFF_BROADCAST;
486 		} else if (!strcmp(args[i], "-bcast")) {
487 			removeFlags |= IFF_BROADCAST;
488 		} else if (!strcmp(args[i], "promisc")) {
489 			addFlags |= IFF_PROMISC;
490 		} else if (!strcmp(args[i], "-promisc")) {
491 			removeFlags |= IFF_PROMISC;
492 		} else if (!strcmp(args[i], "allmulti")) {
493 			addFlags |= IFF_ALLMULTI;
494 		} else if (!strcmp(args[i], "-allmulti")) {
495 			removeFlags |= IFF_ALLMULTI;
496 		} else if (!strcmp(args[i], "loopback")) {
497 			addFlags |= IFF_LOOPBACK;
498 		} else
499 			usage(1);
500 
501 		i++;
502 	}
503 
504 	if ((addFlags & removeFlags) != 0) {
505 		fprintf(stderr, "%s: Contradicting flags specified\n", kProgramName);
506 		exit(1);
507 	}
508 
509 	// set address/mask/broadcast/peer
510 
511 	if (hasAddress) {
512 		memcpy(&request.ifr_addr, &address, address.sa_len);
513 
514 		if (ioctl(socket, SIOCSIFADDR, &request, sizeof(struct ifreq)) < 0) {
515 			fprintf(stderr, "%s: Setting address failed: %s\n", kProgramName, strerror(errno));
516 			exit(1);
517 		}
518 	}
519 
520 	if (ioctl(socket, SIOCGIFFLAGS, &request, sizeof(struct ifreq)) < 0) {
521 		fprintf(stderr, "%s: Getting flags failed: %s\n", kProgramName, strerror(errno));
522 		exit(1);
523 	}
524 	currentFlags = request.ifr_flags;
525 
526 	if (!hasMask && hasAddress && kFamilies[familyIndex].family == AF_INET
527 		&& ioctl(socket, SIOCGIFNETMASK, &request, sizeof(struct ifreq)) == 0
528 		&& request.ifr_mask.sa_family == AF_UNSPEC) {
529 			// generate standard netmask if it doesn't have one yet
530 		sockaddr_in *netmask = (sockaddr_in *)&mask;
531 		netmask->sin_len = sizeof(sockaddr_in);
532 		netmask->sin_family = AF_INET;
533 
534 		// choose default netmask depending on the class of the address
535 		in_addr_t net = ((sockaddr_in *)&address)->sin_addr.s_addr;
536 		if (IN_CLASSA(net)
537 			|| (ntohl(net) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) {
538 			// class A, or loopback
539 			netmask->sin_addr.s_addr = IN_CLASSA_NET;
540 		} if (IN_CLASSB(net)) {
541 			// class B
542 			netmask->sin_addr.s_addr = IN_CLASSB_NET;
543 		} else {
544 			// class C and rest
545 			netmask->sin_addr.s_addr = IN_CLASSC_NET;
546 		}
547 
548 		hasMask = true;
549 	}
550 	if (hasMask) {
551 		memcpy(&request.ifr_mask, &mask, mask.sa_len);
552 
553 		if (ioctl(socket, SIOCSIFNETMASK, &request, sizeof(struct ifreq)) < 0) {
554 			fprintf(stderr, "%s: Setting subnet mask failed: %s\n", kProgramName, strerror(errno));
555 			exit(1);
556 		}
557 	}
558 
559 	if (!hasBroadcast && hasAddress && (currentFlags & IFF_BROADCAST)
560 		&& kFamilies[familyIndex].family == AF_INET
561 		&& ioctl(socket, SIOCGIFBRDADDR, &request, sizeof(struct ifreq)) == 0
562 		&& request.ifr_mask.sa_family == AF_UNSPEC) {
563 			// generate standard broadcast address if it doesn't have one yet
564 		sockaddr_in *broadcastAddr = (sockaddr_in *)&broadcast;
565 		uint32 maskValue = ((sockaddr_in *)&mask)->sin_addr.s_addr;
566 		uint32 broadcastValue = ((sockaddr_in *)&address)->sin_addr.s_addr;
567 		broadcastValue = (broadcastValue & maskValue) | ~maskValue;
568 		broadcastAddr->sin_len = sizeof(sockaddr_in);
569 		broadcastAddr->sin_family = AF_INET;
570 		broadcastAddr->sin_addr.s_addr = broadcastValue;
571 		hasBroadcast = true;
572 	}
573 	if (hasBroadcast) {
574 		memcpy(&request.ifr_broadaddr, &broadcast, broadcast.sa_len);
575 
576 		if (ioctl(socket, SIOCSIFBRDADDR, &request, sizeof(struct ifreq)) < 0) {
577 			fprintf(stderr, "%s: Setting broadcast address failed: %s\n", kProgramName, strerror(errno));
578 			exit(1);
579 		}
580 	}
581 
582 	if (hasPeer) {
583 		memcpy(&request.ifr_dstaddr, &peer, peer.sa_len);
584 
585 		if (ioctl(socket, SIOCSIFDSTADDR, &request, sizeof(struct ifreq)) < 0) {
586 			fprintf(stderr, "%s: Setting peer address failed: %s\n", kProgramName, strerror(errno));
587 			exit(1);
588 		}
589 	}
590 
591 	// set flags
592 
593 	if (addFlags || removeFlags) {
594 		request.ifr_flags = (currentFlags & ~removeFlags) | addFlags;
595 		if (ioctl(socket, SIOCSIFFLAGS, &request, sizeof(struct ifreq)) < 0)
596 			fprintf(stderr, "%s: Setting flags failed: %s\n", kProgramName, strerror(errno));
597 	}
598 
599 	// set options
600 
601 	if (mtu != -1) {
602 		request.ifr_mtu = mtu;
603 		if (ioctl(socket, SIOCSIFMTU, &request, sizeof(struct ifreq)) < 0)
604 			fprintf(stderr, "%s: Setting MTU failed: %s\n", kProgramName, strerror(errno));
605 	}
606 
607 	if (metric != -1) {
608 		request.ifr_metric = metric;
609 		if (ioctl(socket, SIOCSIFMETRIC, &request, sizeof(struct ifreq)) < 0)
610 			fprintf(stderr, "%s: Setting metric failed: %s\n", kProgramName, strerror(errno));
611 	}
612 }
613 
614 
615 //	#pragma mark -
616 
617 
618 int
619 main(int argc, char** argv)
620 {
621 	if (argc > 1 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")))
622 		usage(0);
623 
624 	bool deleteInterfaces = false;
625 
626 	if (argc > 1
627 		&& (!strcmp(argv[1], "--delete")
628 			|| !strcmp(argv[1], "--del")
629 			|| !strcmp(argv[1], "-d"))) {
630 		// delete interfaces
631 		if (argc < 3)
632 			usage(1);
633 
634 		deleteInterfaces = true;
635 	}
636 
637 	// we need a socket to talk to the networking stack
638 	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
639 	if (socket < 0) {
640 		fprintf(stderr, "%s: The networking stack doesn't seem to be available.\n",
641 			kProgramName);
642 		return 1;
643 	}
644 
645 	if (deleteInterfaces) {
646 		for (int i = 2; i < argc; i++) {
647 			delete_interface(socket, argv[i]);
648 		}
649 		return 0;
650 	} else if (argc > 1 && !strcmp(argv[1], "-a")) {
651 		// accept -a option for those that are used to it from other platforms
652 		if (argc > 2)
653 			usage(1);
654 
655 		list_interfaces(socket, NULL);
656 		return 0;
657 	}
658 
659 	const char* name = argv[1];
660 	if (argc > 2) {
661 		// add or configure an interface
662 
663 		configure_interface(socket, name, argv + 2, argc - 2);
664 		return 0;
665 	}
666 
667 	// list interfaces
668 
669 	list_interfaces(socket, name);
670 	return 0;
671 }
672 
673