xref: /minix3/minix/net/lwip/ifconf.c (revision 03ac74ede908465cc64c671bbd209e761dc765dc)
1 /* LWIP service - ifconf.c - interface configuration */
2 
3 #include "lwip.h"
4 #include "ifaddr.h"
5 #include "lldata.h"
6 
7 #include <net/if_media.h>
8 #include <minix/if.h>
9 
10 #define LOOPBACK_IFNAME		"lo0"	/* name of the loopback interface */
11 
12 /*
13  * Initialize the first loopback device, which is present by default.
14  */
15 void
16 ifconf_init(void)
17 {
18 	const struct sockaddr_in addr = {
19 	    .sin_family = AF_INET,
20 	    .sin_addr = { htonl(INADDR_LOOPBACK) }
21 	};
22 	struct sockaddr_in6 ll_addr6 = {
23 	    .sin6_family = AF_INET6,
24 	};
25 	const struct sockaddr_in6 lo_addr6 = {
26 	    .sin6_family = AF_INET6,
27 	    .sin6_addr = IN6ADDR_LOOPBACK_INIT
28 	};
29 	const struct in6_addrlifetime lifetime = {
30 	    .ia6t_vltime = ND6_INFINITE_LIFETIME,
31 	    .ia6t_pltime = ND6_INFINITE_LIFETIME
32 	};
33 	struct sockaddr_in6 mask6;
34 	struct ifdev *ifdev;
35 	socklen_t addr_len;
36 	int r;
37 
38 	if ((r = ifdev_create(LOOPBACK_IFNAME)) != OK)
39 		panic("unable to create loopback interface: %d", r);
40 
41 	if ((ifdev = ifdev_find_by_name(LOOPBACK_IFNAME)) == NULL)
42 		panic("unable to find loopback interface");
43 
44 	if ((r = ifaddr_v4_add(ifdev, &addr, NULL, NULL, NULL, 0)) != OK)
45 		panic("unable to set IPv4 address on loopback interface: %d",
46 		    r);
47 
48 	addr_len = sizeof(mask6);
49 	addr_put_netmask((struct sockaddr *)&mask6, &addr_len, IPADDR_TYPE_V6,
50 	    64 /*prefix*/);
51 
52 	ll_addr6.sin6_addr.s6_addr[0] = 0xfe;
53 	ll_addr6.sin6_addr.s6_addr[1] = 0x80;
54 	ll_addr6.sin6_addr.s6_addr[15] = ifdev_get_index(ifdev);
55 
56 	if ((r = ifaddr_v6_add(ifdev, &ll_addr6, &mask6, NULL, 0,
57 	    &lifetime)) != OK)
58 		panic("unable to set IPv6 address on loopback interface: %d",
59 		    r);
60 
61 	addr_len = sizeof(mask6);
62 	addr_put_netmask((struct sockaddr *)&mask6, &addr_len, IPADDR_TYPE_V6,
63 	    128 /*prefix*/);
64 
65 	if ((r = ifaddr_v6_add(ifdev, &lo_addr6, &mask6, NULL, 0,
66 	    &lifetime)) != OK)
67 		panic("unable to set IPv6 address on loopback interface: %d",
68 		    r);
69 
70 	if ((r = ifdev_set_ifflags(ifdev, IFF_UP)) != OK)
71 		panic("unable to bring up loopback interface");
72 }
73 
74 /*
75  * Process an address family independent IOCTL request with an "ifreq"
76  * structure.
77  */
78 static int
79 ifconf_ioctl_ifreq(unsigned long request, const struct sockdriver_data * data)
80 {
81 	struct ifdev *ifdev;
82 	struct ifreq ifr;
83 	int r;
84 
85 	if ((r = sockdriver_copyin(data, 0, &ifr, sizeof(ifr))) != OK)
86 		return r;
87 
88 	if (request != SIOCIFCREATE) {
89 		ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
90 
91 		if ((ifdev = ifdev_find_by_name(ifr.ifr_name)) == NULL)
92 			return ENXIO;
93 	} else
94 		ifdev = NULL;
95 
96 	switch (request) {
97 	case SIOCGIFFLAGS:
98 		ifr.ifr_flags = ifdev_get_ifflags(ifdev);
99 
100 		return sockdriver_copyout(data, 0, &ifr, sizeof(ifr));
101 
102 	case SIOCSIFFLAGS:
103 		/*
104 		 * Unfortunately, ifr_flags is a signed integer and the sign
105 		 * bit is in fact used as a flag, so without explicit casting
106 		 * we end up setting all upper bits of the (full) integer.  If
107 		 * NetBSD ever extends the field, this assert should trigger..
108 		 */
109 		assert(sizeof(ifr.ifr_flags) == sizeof(short));
110 
111 		return ifdev_set_ifflags(ifdev, (unsigned short)ifr.ifr_flags);
112 
113 	case SIOCGIFMETRIC:
114 		ifr.ifr_metric = ifdev_get_metric(ifdev);
115 
116 		return sockdriver_copyout(data, 0, &ifr, sizeof(ifr));
117 
118 	case SIOCSIFMETRIC:
119 		/* The metric is not used within the operating system. */
120 		ifdev_set_metric(ifdev, ifr.ifr_metric);
121 
122 		return OK;
123 
124 	case SIOCSIFMEDIA:
125 		return ifdev_set_ifmedia(ifdev, ifr.ifr_media);
126 
127 	case SIOCGIFMTU:
128 		ifr.ifr_mtu = ifdev_get_mtu(ifdev);
129 
130 		return sockdriver_copyout(data, 0, &ifr, sizeof(ifr));
131 
132 	case SIOCSIFMTU:
133 		return ifdev_set_mtu(ifdev, ifr.ifr_mtu);
134 
135 	case SIOCIFCREATE:
136 		if (memchr(ifr.ifr_name, '\0', sizeof(ifr.ifr_name)) == NULL)
137 			return EINVAL;
138 
139 		return ifdev_create(ifr.ifr_name);
140 
141 	case SIOCIFDESTROY:
142 		return ifdev_destroy(ifdev);
143 
144 	case SIOCGIFDLT:
145 		ifr.ifr_dlt = ifdev_get_dlt(ifdev);
146 
147 		return sockdriver_copyout(data, 0, &ifr, sizeof(ifr));
148 
149 	case SIOCGIFINDEX:
150 		ifr.ifr_index = ifdev_get_index(ifdev);
151 
152 		return sockdriver_copyout(data, 0, &ifr, sizeof(ifr));
153 
154 	default:
155 		return ENOTTY;
156 	}
157 }
158 
159 /*
160  * Process an address family independent IOCTL request with an "ifcapreq"
161  * structure.
162  */
163 static int
164 ifconf_ioctl_ifcap(unsigned long request,
165 	const struct sockdriver_data * data)
166 {
167 	struct ifdev *ifdev;
168 	struct ifcapreq ifcr;
169 	int r;
170 
171 	if ((r = sockdriver_copyin(data, 0, &ifcr, sizeof(ifcr))) != OK)
172 		return r;
173 
174 	ifcr.ifcr_name[sizeof(ifcr.ifcr_name) - 1] = '\0';
175 
176 	if ((ifdev = ifdev_find_by_name(ifcr.ifcr_name)) == NULL)
177 		return ENXIO;
178 
179 	switch (request) {
180 	case SIOCSIFCAP:
181 		return ifdev_set_ifcap(ifdev, ifcr.ifcr_capenable);
182 
183 	case SIOCGIFCAP:
184 		ifdev_get_ifcap(ifdev, &ifcr.ifcr_capabilities,
185 		    &ifcr.ifcr_capenable);
186 
187 		return sockdriver_copyout(data, 0, &ifcr, sizeof(ifcr));
188 
189 	default:
190 		return ENOTTY;
191 	}
192 }
193 
194 /*
195  * Process an address family independent IOCTL request with an "ifmediareq"
196  * structure.
197  */
198 static int
199 ifconf_ioctl_ifmedia(unsigned long request,
200 	const struct sockdriver_data * data)
201 {
202 	struct ifdev *ifdev;
203 	struct ifmediareq ifm;
204 	int r;
205 
206 	if ((r = sockdriver_copyin(data, 0, &ifm, sizeof(ifm))) != OK)
207 		return r;
208 
209 	ifm.ifm_name[sizeof(ifm.ifm_name) - 1] = '\0';
210 
211 	if ((ifdev = ifdev_find_by_name(ifm.ifm_name)) == NULL)
212 		return ENXIO;
213 
214 	switch (request) {
215 	case MINIX_SIOCGIFMEDIA:
216 		if ((r = ifdev_get_ifmedia(ifdev, &ifm.ifm_current,
217 		    &ifm.ifm_active)) != OK)
218 			return r;
219 		ifm.ifm_mask = 0;
220 
221 		switch (ifdev_get_link(ifdev)) {
222 		case LINK_STATE_UP:
223 			ifm.ifm_status = IFM_AVALID | IFM_ACTIVE;
224 			break;
225 		case LINK_STATE_DOWN:
226 			ifm.ifm_status = IFM_AVALID;
227 			break;
228 		default:
229 			ifm.ifm_status = 0;
230 			break;
231 		}
232 
233 		/*
234 		 * TODO: support for the list of supported media types.  This
235 		 * one is not easy, because we cannot simply suspend the IOCTL
236 		 * and query the driver.  For now, return only entry (which is
237 		 * the minimum for ifconfig(8) not to complain), namely the
238 		 * currently selected one.
239 		 */
240 		if (ifm.ifm_ulist != NULL) {
241 			if (ifm.ifm_count < 1)
242 				return ENOMEM;
243 
244 			/*
245 			 * Copy out the 'list', which consists of one entry.
246 			 * If we were to produce multiple entries, we would
247 			 * have to check against the MINIX_IF_MAXMEDIA limit.
248 			 */
249 			if ((r = sockdriver_copyout(data,
250 			    offsetof(struct minix_ifmediareq, mifm_list),
251 			    &ifm.ifm_current, sizeof(ifm.ifm_current))) != OK)
252 				return r;
253 		}
254 		ifm.ifm_count = 1;
255 
256 		return sockdriver_copyout(data, 0, &ifm, sizeof(ifm));
257 
258 	default:
259 		return ENOTTY;
260 	}
261 }
262 
263 /*
264  * Process an address family independent IOCTL request with an "if_clonereq"
265  * structure.
266  */
267 static int
268 ifconf_ioctl_ifclone(unsigned long request,
269 	const struct sockdriver_data * data)
270 {
271 	struct if_clonereq ifcr;
272 	const char *ptr;
273 	char name[IFNAMSIZ];
274 	size_t off;
275 	unsigned int num;
276 	int r;
277 
278 	if ((r = sockdriver_copyin(data, 0, &ifcr, sizeof(ifcr))) != OK)
279 		return r;
280 
281 	if (ifcr.ifcr_count < 0)
282 		return EINVAL;
283 
284 	off = offsetof(struct minix_if_clonereq, mifcr_buffer);
285 
286 	for (num = 0; (ptr = ifdev_enum_vtypes(num)) != NULL; num++) {
287 		/* Prevent overflow in case we ever have over 128 vtypes.. */
288 		if (num == MINIX_IF_MAXCLONERS)
289 			break;
290 
291 		if (ifcr.ifcr_buffer == NULL ||
292 		    num >= (unsigned int)ifcr.ifcr_count)
293 			continue;
294 
295 		memset(name, 0, sizeof(name));
296 		strlcpy(name, ptr, sizeof(name));
297 
298 		if ((r = sockdriver_copyout(data, off, name,
299 		    sizeof(name))) != OK)
300 			return r;
301 
302 		off += sizeof(name);
303 	}
304 
305 	ifcr.ifcr_total = num;
306 
307 	return sockdriver_copyout(data, 0, &ifcr, sizeof(ifcr));
308 }
309 
310 /*
311  * Process an address family independent IOCTL request with an "if_addrprefreq"
312  * structure.
313  */
314 static int
315 ifconf_ioctl_ifaddrpref(unsigned long request,
316 	const struct sockdriver_data * data)
317 {
318 	struct ifdev *ifdev;
319 	struct if_addrprefreq ifap;
320 	int r;
321 
322 	if ((r = sockdriver_copyin(data, 0, &ifap, sizeof(ifap))) != OK)
323 		return r;
324 
325 	ifap.ifap_name[sizeof(ifap.ifap_name) - 1] = '\0';
326 
327 	if ((ifdev = ifdev_find_by_name(ifap.ifap_name)) == NULL)
328 		return ENXIO;
329 
330 	/*
331 	 * For now, we simply support only a preference of 0.  We do not try to
332 	 * look up the given address, nor do we return the looked up address.
333 	 */
334 	switch (request) {
335 	case SIOCSIFADDRPREF:
336 		if (ifap.ifap_preference != 0)
337 			return EINVAL;
338 
339 		return OK;
340 
341 	case SIOCGIFADDRPREF:
342 		ifap.ifap_preference = 0;
343 
344 		return sockdriver_copyout(data, 0, &ifap, sizeof(ifap));
345 
346 	default:
347 		return ENOTTY;
348 	}
349 }
350 
351 /*
352  * Process an IOCTL request for AF_INET with an "ifreq" structure.
353  */
354 static int
355 ifconf_ioctl_v4_ifreq(unsigned long request,
356 	const struct sockdriver_data * data)
357 {
358 	struct sockaddr_in addr, mask, bcast, dest, *sin = NULL /*gcc*/;
359 	struct ifdev *ifdev;
360 	struct ifreq ifr;
361 	ifaddr_v4_num_t num;
362 	int r, flags;
363 
364 	if ((r = sockdriver_copyin(data, 0, &ifr, sizeof(ifr))) != OK)
365 		return r;
366 
367 	ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
368 
369 	if ((ifdev = ifdev_find_by_name(ifr.ifr_name)) == NULL)
370 		return ENXIO;
371 
372 	switch (request) {
373 	case SIOCGIFADDR:
374 	case SIOCGIFNETMASK:
375 	case SIOCGIFBRDADDR:
376 	case SIOCGIFDSTADDR:
377 		/* Retrieve all addresses, then copy out the desired one. */
378 		switch (request) {
379 		case SIOCGIFADDR:	sin = &addr; break;
380 		case SIOCGIFNETMASK:	sin = &mask; break;
381 		case SIOCGIFBRDADDR:	sin = &bcast; break;
382 		case SIOCGIFDSTADDR:	sin = &dest; break;
383 		}
384 
385 		sin->sin_len = 0;
386 
387 		if ((r = ifaddr_v4_get(ifdev, (ifaddr_v4_num_t)0, &addr, &mask,
388 		    &bcast, &dest)) != OK)
389 			return r;
390 
391 		if (sin->sin_len == 0) /* not filled in */
392 			return EADDRNOTAVAIL;
393 
394 		memcpy(&ifr.ifr_addr, sin, sizeof(*sin));
395 
396 		return sockdriver_copyout(data, 0, &ifr, sizeof(ifr));
397 
398 	case SIOCGIFAFLAG_IN:
399 		if ((r = ifaddr_v4_find(ifdev,
400 		    (struct sockaddr_in *)&ifr.ifr_addr, &num)) != OK)
401 			return r;
402 
403 		ifr.ifr_addrflags = ifaddr_v4_get_flags(ifdev, num);
404 
405 		return sockdriver_copyout(data, 0, &ifr, sizeof(ifr));
406 
407 	case SIOCSIFADDR:
408 		/*
409 		 * This one is slightly different from the rest, in that we
410 		 * either set or update the primary address: if we set it, we
411 		 * must let _add() generate a matching netmask automatically,
412 		 * while if we update it, _add() would fail unless we first
413 		 * delete the old entry.
414 		 */
415 		sin = (struct sockaddr_in *)&ifr.ifr_addr;
416 
417 		if ((r = ifaddr_v4_get(ifdev, (ifaddr_v4_num_t)0, &addr, &mask,
418 		    &bcast, &dest)) == OK) {
419 			flags = ifaddr_v4_get_flags(ifdev, (ifaddr_v4_num_t)0);
420 
421 			ifaddr_v4_del(ifdev, (ifaddr_v4_num_t)0);
422 
423 			/*
424 			 * If setting the new address fails, reinstating the
425 			 * old address should always work.  This is really ugly
426 			 * as it generates routing socket noise, but this call
427 			 * is deprecated anyway.
428 			 */
429 			if ((r = ifaddr_v4_add(ifdev, sin, &mask, &bcast,
430 			    &dest, 0 /*flags*/)) != OK)
431 				(void)ifaddr_v4_add(ifdev, &addr, &mask,
432 				    &bcast, &dest, flags);
433 
434 			return r;
435 		} else
436 			return ifaddr_v4_add(ifdev, sin, NULL /*mask*/,
437 			    NULL /*bcast*/, NULL /*dest*/, 0 /*flags*/);
438 
439 	case SIOCSIFNETMASK:
440 	case SIOCSIFBRDADDR:
441 	case SIOCSIFDSTADDR:
442 		/* These calls only update the existing primary address. */
443 		if ((r = ifaddr_v4_get(ifdev, (ifaddr_v4_num_t)0, &addr, &mask,
444 		    &bcast, &dest)) != OK)
445 			return r;
446 
447 		sin = (struct sockaddr_in *)&ifr.ifr_addr;
448 
449 		switch (request) {
450 		case SIOCSIFNETMASK: memcpy(&mask, sin, sizeof(mask)); break;
451 		case SIOCSIFBRDADDR: memcpy(&bcast, sin, sizeof(bcast)); break;
452 		case SIOCSIFDSTADDR: memcpy(&dest, sin, sizeof(dest)); break;
453 		}
454 
455 		return ifaddr_v4_add(ifdev, &addr, &mask, &bcast, &dest,
456 		    ifaddr_v4_get_flags(ifdev, (ifaddr_v4_num_t)0));
457 
458 	case SIOCDIFADDR:
459 		if ((r = ifaddr_v4_find(ifdev,
460 		    (struct sockaddr_in *)&ifr.ifr_addr, &num)) != OK)
461 			return r;
462 
463 		ifaddr_v4_del(ifdev, num);
464 
465 		return OK;
466 
467 	default:
468 		return ENOTTY;
469 	}
470 }
471 
472 /*
473  * Process an IOCTL request for AF_INET with an "ifaliasreq" structure.
474  */
475 static int
476 ifconf_ioctl_v4_ifalias(unsigned long request,
477 	const struct sockdriver_data * data)
478 {
479 	struct ifdev *ifdev;
480 	struct ifaliasreq ifra;
481 	struct sockaddr_in dest;
482 	ifaddr_v4_num_t num;
483 	int r;
484 
485 	if ((r = sockdriver_copyin(data, 0, &ifra, sizeof(ifra))) != OK)
486 		return r;
487 
488 	ifra.ifra_name[sizeof(ifra.ifra_name) - 1] = '\0';
489 
490 	if ((ifdev = ifdev_find_by_name(ifra.ifra_name)) == NULL)
491 		return ENXIO;
492 
493 	switch (request) {
494 	case SIOCAIFADDR:
495 		return ifaddr_v4_add(ifdev,
496 		    (struct sockaddr_in *)&ifra.ifra_addr,
497 		    (struct sockaddr_in *)&ifra.ifra_mask,
498 		    (struct sockaddr_in *)&ifra.ifra_broadaddr,
499 		    (struct sockaddr_in *)&ifra.ifra_dstaddr, 0 /*flags*/);
500 
501 	case SIOCGIFALIAS:
502 		if ((r = ifaddr_v4_find(ifdev,
503 		    (struct sockaddr_in *)&ifra.ifra_addr, &num)) != OK)
504 			return r;
505 
506 		/*
507 		 * The broadcast and destination address are stored in the same
508 		 * ifaliasreq field.  We cannot pass a pointer to the same
509 		 * field to ifaddr_v4_get().  So, use a temporary variable.
510 		 */
511 		(void)ifaddr_v4_get(ifdev, num,
512 		    (struct sockaddr_in *)&ifra.ifra_addr,
513 		    (struct sockaddr_in *)&ifra.ifra_mask,
514 		    (struct sockaddr_in *)&ifra.ifra_broadaddr, &dest);
515 
516 		if (ifra.ifra_broadaddr.sa_len == 0)
517 			memcpy(&ifra.ifra_dstaddr, &dest, sizeof(dest));
518 
519 		return sockdriver_copyout(data, 0, &ifra, sizeof(ifra));
520 
521 	default:
522 		return ENOTTY;
523 	}
524 }
525 
526 /*
527  * Process an IOCTL request for AF_INET.
528  */
529 static int
530 ifconf_ioctl_v4(unsigned long request, const struct sockdriver_data * data,
531 	endpoint_t user_endpt)
532 {
533 
534 	switch (request) {
535 	case SIOCSIFADDR:
536 	case SIOCSIFDSTADDR:
537 	case SIOCSIFBRDADDR:
538 	case SIOCSIFNETMASK:
539 	case SIOCDIFADDR:
540 		if (!util_is_root(user_endpt))
541 			return EPERM;
542 
543 		/* FALLTHROUGH */
544 	case SIOCGIFADDR:
545 	case SIOCGIFDSTADDR:
546 	case SIOCGIFBRDADDR:
547 	case SIOCGIFNETMASK:
548 	case SIOCGIFAFLAG_IN:
549 		return ifconf_ioctl_v4_ifreq(request, data);
550 
551 	case SIOCAIFADDR:
552 		if (!util_is_root(user_endpt))
553 			return EPERM;
554 
555 		/* FALLTHROUGH */
556 	case SIOCGIFALIAS:
557 		return ifconf_ioctl_v4_ifalias(request, data);
558 
559 	default:
560 		return ENOTTY;
561 	}
562 }
563 
564 #ifdef INET6
565 /*
566  * Process an IOCTL request for AF_INET6 with an "in6_ifreq" structure.
567  */
568 static int
569 ifconf_ioctl_v6_ifreq(unsigned long request,
570 	const struct sockdriver_data * data)
571 {
572 	struct ifdev *ifdev;
573 	struct in6_ifreq ifr;
574 	ifaddr_v6_num_t num;
575 	int r;
576 
577 	if ((r = sockdriver_copyin(data, 0, &ifr, sizeof(ifr))) != OK)
578 		return r;
579 
580 	ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
581 
582 	if ((ifdev = ifdev_find_by_name(ifr.ifr_name)) == NULL)
583 		return ENXIO;
584 
585 	if ((r = ifaddr_v6_find(ifdev, &ifr.ifr_addr, &num)) != OK)
586 		return r;
587 
588 	switch (request) {
589 	case SIOCGIFADDR_IN6:
590 		/* This IOCTL basically checks if the given address exists. */
591 		ifaddr_v6_get(ifdev, num, &ifr.ifr_addr, NULL, NULL);
592 
593 		return sockdriver_copyout(data, 0, &ifr, sizeof(ifr));
594 
595 	case SIOCDIFADDR_IN6:
596 		ifaddr_v6_del(ifdev, num);
597 
598 		return OK;
599 
600 	case SIOCGIFNETMASK_IN6:
601 		ifaddr_v6_get(ifdev, num, NULL, &ifr.ifr_addr, NULL);
602 
603 		return sockdriver_copyout(data, 0, &ifr, sizeof(ifr));
604 
605 	case SIOCGIFAFLAG_IN6:
606 		ifr.ifr_ifru.ifru_flags6 = ifaddr_v6_get_flags(ifdev, num);
607 
608 		return sockdriver_copyout(data, 0, &ifr, sizeof(ifr));
609 
610 	case SIOCGIFALIFETIME_IN6:
611 		ifaddr_v6_get_lifetime(ifdev, num,
612 		    &ifr.ifr_ifru.ifru_lifetime);
613 
614 		return sockdriver_copyout(data, 0, &ifr, sizeof(ifr));
615 
616 	default:
617 		return ENOTTY;
618 	}
619 }
620 
621 /*
622  * Process an IOCTL request for AF_INET6 with an "in6_aliasreq" structure.
623  */
624 static int
625 ifconf_ioctl_v6_ifalias(unsigned long request,
626 	const struct sockdriver_data * data)
627 {
628 	struct ifdev *ifdev;
629 	struct in6_aliasreq ifra;
630 	int r;
631 
632 	if ((r = sockdriver_copyin(data, 0, &ifra, sizeof(ifra))) != OK)
633 		return r;
634 
635 	ifra.ifra_name[sizeof(ifra.ifra_name) - 1] = '\0';
636 
637 	if ((ifdev = ifdev_find_by_name(ifra.ifra_name)) == NULL)
638 		return ENXIO;
639 
640 	switch (request) {
641 	case SIOCAIFADDR_IN6:
642 		return ifaddr_v6_add(ifdev, &ifra.ifra_addr,
643 		    &ifra.ifra_prefixmask, &ifra.ifra_dstaddr,
644 		    ifra.ifra_flags, &ifra.ifra_lifetime);
645 
646 	default:
647 		return ENOTTY;
648 	}
649 }
650 
651 /*
652  * Process an IOCTL request for AF_INET6 with an "in6_ndireq" structure.
653  */
654 static int
655 ifconf_ioctl_v6_ndireq(unsigned long request,
656 	const struct sockdriver_data * data)
657 {
658 	struct ifdev *ifdev;
659 	struct in6_ndireq ndi;
660 	int r;
661 
662 	if ((r = sockdriver_copyin(data, 0, &ndi, sizeof(ndi))) != OK)
663 		return r;
664 
665 	ndi.ifname[sizeof(ndi.ifname) - 1] = '\0';
666 
667 	if ((ifdev = ifdev_find_by_name(ndi.ifname)) == NULL)
668 		return ENXIO;
669 
670 	switch (request) {
671 	case SIOCGIFINFO_IN6:
672 		memset(&ndi.ndi, 0, sizeof(ndi.ndi));
673 
674 		ndi.ndi.linkmtu = ifdev_get_mtu(ifdev);
675 		ndi.ndi.flags = ifdev_get_nd6flags(ifdev);
676 		ndi.ndi.initialized = 1;
677 		/* TODO: all the other fields.. */
678 
679 		return sockdriver_copyout(data, 0, &ndi, sizeof(ndi));
680 
681 	case SIOCSIFINFO_IN6:
682 		/* TODO: all the other fields.. */
683 
684 		/* FALLTHROUGH */
685 	case SIOCSIFINFO_FLAGS:
686 		return ifdev_set_nd6flags(ifdev, ndi.ndi.flags);
687 
688 	default:
689 		return ENOTTY;
690 	}
691 }
692 
693 /*
694  * Process an IOCTL request for AF_INET6 with an "in6_nbrinfo" structure.
695  */
696 static int
697 ifconf_ioctl_v6_nbrinfo(unsigned long request,
698 	const struct sockdriver_data * data)
699 {
700 	struct ifdev *ifdev;
701 	struct sockaddr_in6 addr;
702 	struct in6_nbrinfo nbri;
703 	lldata_ndp_num_t num;
704 	int r;
705 
706 	if ((r = sockdriver_copyin(data, 0, &nbri, sizeof(nbri))) != OK)
707 		return r;
708 
709 	nbri.ifname[sizeof(nbri.ifname) - 1] = '\0';
710 
711 	if ((ifdev = ifdev_find_by_name(nbri.ifname)) == NULL)
712 		return ENXIO;
713 
714 	switch (request) {
715 	case SIOCGNBRINFO_IN6:
716 		/*
717 		 * Convert the given in6_addr to a full sockaddr_in6, mainly
718 		 * for internal consistency.  It would have been nice if the
719 		 * KAME management API had had any sort of consistency itself.
720 		 */
721 		memset(&addr, 0, sizeof(addr));
722 		addr.sin6_family = AF_INET6;
723 		memcpy(&addr.sin6_addr.s6_addr, &nbri.addr,
724 		    sizeof(addr.sin6_addr.s6_addr));
725 
726 		if ((r = lldata_ndp_find(ifdev, &addr, &num)) != OK)
727 			return r;
728 
729 		lldata_ndp_get_info(num, &nbri.asked, &nbri.isrouter,
730 		    &nbri.state, &nbri.expire);
731 
732 		return sockdriver_copyout(data, 0, &nbri, sizeof(nbri));
733 
734 	default:
735 		return ENOTTY;
736 	}
737 }
738 
739 /*
740  * Process an IOCTL request for AF_INET6.
741  */
742 static int
743 ifconf_ioctl_v6(unsigned long request, const struct sockdriver_data * data,
744 	endpoint_t user_endpt)
745 {
746 
747 	switch (request) {
748 	case SIOCDIFADDR_IN6:
749 		if (!util_is_root(user_endpt))
750 			return EPERM;
751 
752 		/* FALLTHROUGH */
753 	case SIOCGIFADDR_IN6:
754 	case SIOCGIFNETMASK_IN6:
755 	case SIOCGIFAFLAG_IN6:
756 	case SIOCGIFALIFETIME_IN6:
757 		return ifconf_ioctl_v6_ifreq(request, data);
758 
759 	case SIOCAIFADDR_IN6:
760 		if (!util_is_root(user_endpt))
761 			return EPERM;
762 
763 		return ifconf_ioctl_v6_ifalias(request, data);
764 
765 	case SIOCSIFINFO_IN6:
766 	case SIOCSIFINFO_FLAGS:
767 		if (!util_is_root(user_endpt))
768 			return EPERM;
769 
770 		/* FALLTHROUGH */
771 	case SIOCGIFINFO_IN6:
772 		return ifconf_ioctl_v6_ndireq(request, data);
773 
774 	case SIOCGNBRINFO_IN6:
775 		return ifconf_ioctl_v6_nbrinfo(request, data);
776 
777 	default:
778 		return ENOTTY;
779 	}
780 }
781 #endif /* INET6 */
782 
783 /*
784  * Process an IOCTL request for AF_LINK with an "if_laddrreq" structure.
785  */
786 static int
787 ifconf_ioctl_dl_lifaddr(unsigned long request,
788 	const struct sockdriver_data * data)
789 {
790 	struct ifdev *ifdev;
791 	struct if_laddrreq iflr;
792 	ifaddr_dl_num_t num;
793 	int r;
794 
795 	if ((r = sockdriver_copyin(data, 0, &iflr, sizeof(iflr))) != OK)
796 		return r;
797 
798 	iflr.iflr_name[sizeof(iflr.iflr_name) - 1] = '\0';
799 
800 	if ((ifdev = ifdev_find_by_name(iflr.iflr_name)) == NULL)
801 		return ENXIO;
802 
803 	switch (request) {
804 	case SIOCGLIFADDR:
805 		if (iflr.flags & IFLR_PREFIX) {
806 			/* We ignore the prefix length, like NetBSD does. */
807 			if ((r = ifaddr_dl_find(ifdev,
808 			    (struct sockaddr_dlx *)&iflr.addr,
809 			    sizeof(iflr.addr), &num)) != OK)
810 				return r;
811 		} else
812 			num = (ifaddr_dl_num_t)0; /* this always works */
813 
814 		ifaddr_dl_get(ifdev, num, (struct sockaddr_dlx *)&iflr.addr);
815 		iflr.flags = ifaddr_dl_get_flags(ifdev, num);
816 		memset(&iflr.dstaddr, 0, sizeof(iflr.dstaddr));
817 
818 		return sockdriver_copyout(data, 0, &iflr, sizeof(iflr));
819 
820 	case SIOCALIFADDR:
821 		return ifaddr_dl_add(ifdev, (struct sockaddr_dlx *)&iflr.addr,
822 		    sizeof(iflr.addr), iflr.flags);
823 
824 	case SIOCDLIFADDR:
825 		if ((r = ifaddr_dl_find(ifdev,
826 		    (struct sockaddr_dlx *)&iflr.addr, sizeof(iflr.addr),
827 		    &num)) != OK)
828 			return r;
829 
830 		return ifaddr_dl_del(ifdev, num);
831 
832 	default:
833 		return ENOTTY;
834 	}
835 }
836 
837 /*
838  * Process an IOCTL request for AF_LINK.
839  */
840 static int
841 ifconf_ioctl_dl(unsigned long request, const struct sockdriver_data * data,
842 	endpoint_t user_endpt)
843 {
844 
845 	switch (request) {
846 	case SIOCALIFADDR:
847 	case SIOCDLIFADDR:
848 		if (!util_is_root(user_endpt))
849 			return EPERM;
850 
851 		/* FALLTHROUGH */
852 	case SIOCGLIFADDR:
853 		return ifconf_ioctl_dl_lifaddr(request, data);
854 
855 	default:
856 		return ENOTTY;
857 	}
858 }
859 
860 /*
861  * Process an IOCTL request.  This routine is shared between TCP, UDP, RAW, and
862  * link sockets.  The given socket may be used to obtain the target domain:
863  * AF_INET, AF_INET6, or AF_LINK.
864  */
865 int
866 ifconf_ioctl(struct sock * sock, unsigned long request,
867 	const struct sockdriver_data * data, endpoint_t user_endpt)
868 {
869 	int domain;
870 
871 	domain = sockevent_get_domain(sock);
872 
873 	switch (request) {
874 	case SIOCSIFFLAGS:
875 	case SIOCSIFMETRIC:
876 	case SIOCSIFMEDIA:
877 	case SIOCSIFMTU:
878 	case SIOCIFCREATE:
879 	case SIOCIFDESTROY:
880 		if (!util_is_root(user_endpt))
881 			return EPERM;
882 
883 		/* FALLTHROUGH */
884 	case SIOCGIFFLAGS:
885 	case SIOCGIFMETRIC:
886 	case SIOCGIFMTU:
887 	case SIOCGIFDLT:
888 	case SIOCGIFINDEX:
889 		return ifconf_ioctl_ifreq(request, data);
890 
891 	case SIOCSIFCAP:
892 		if (!util_is_root(user_endpt))
893 			return EPERM;
894 
895 		/* FALLTHROUGH */
896 	case SIOCGIFCAP:
897 		return ifconf_ioctl_ifcap(request, data);
898 
899 	case MINIX_SIOCGIFMEDIA:
900 		return ifconf_ioctl_ifmedia(request, data);
901 
902 	case MINIX_SIOCIFGCLONERS:
903 		return ifconf_ioctl_ifclone(request, data);
904 
905 	case SIOCSIFADDRPREF:
906 		if (!util_is_root(user_endpt))
907 			return EPERM;
908 
909 		/* FALLTHROUGH */
910 	case SIOCGIFADDRPREF:
911 		return ifconf_ioctl_ifaddrpref(request, data);
912 
913 	default:
914 		switch (domain) {
915 		case AF_INET:
916 			return ifconf_ioctl_v4(request, data, user_endpt);
917 
918 #ifdef INET6
919 		case AF_INET6:
920 			return ifconf_ioctl_v6(request, data, user_endpt);
921 #endif /* INET6 */
922 
923 		case AF_LINK:
924 			return ifconf_ioctl_dl(request, data, user_endpt);
925 
926 		default:
927 			return ENOTTY;
928 		}
929 	}
930 }
931