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
ifconf_init(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
ifconf_ioctl_ifreq(unsigned long request,const struct sockdriver_data * data)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
ifconf_ioctl_ifcap(unsigned long request,const struct sockdriver_data * data)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
ifconf_ioctl_ifmedia(unsigned long request,const struct sockdriver_data * data)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
ifconf_ioctl_ifclone(unsigned long request,const struct sockdriver_data * data)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
ifconf_ioctl_ifaddrpref(unsigned long request,const struct sockdriver_data * data)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
ifconf_ioctl_v4_ifreq(unsigned long request,const struct sockdriver_data * data)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
ifconf_ioctl_v4_ifalias(unsigned long request,const struct sockdriver_data * data)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
ifconf_ioctl_v4(unsigned long request,const struct sockdriver_data * data,endpoint_t user_endpt)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
ifconf_ioctl_v6_ifreq(unsigned long request,const struct sockdriver_data * data)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
ifconf_ioctl_v6_ifalias(unsigned long request,const struct sockdriver_data * data)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
ifconf_ioctl_v6_ndireq(unsigned long request,const struct sockdriver_data * data)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
ifconf_ioctl_v6_nbrinfo(unsigned long request,const struct sockdriver_data * data)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
ifconf_ioctl_v6(unsigned long request,const struct sockdriver_data * data,endpoint_t user_endpt)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
ifconf_ioctl_dl_lifaddr(unsigned long request,const struct sockdriver_data * data)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
ifconf_ioctl_dl(unsigned long request,const struct sockdriver_data * data,endpoint_t user_endpt)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
ifconf_ioctl(struct sock * sock,unsigned long request,const struct sockdriver_data * data,endpoint_t user_endpt)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