1/*
2 * Common ioctl functions.
3 * Copyright (C) 1997, 98 Kunihiro Ishiguro
4 *
5 * This file is part of GNU Zebra.
6 *
7 * GNU Zebra is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2, or (at your option) any
10 * later version.
11 *
12 * GNU Zebra is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with GNU Zebra; see the file COPYING.  If not, write to the Free
19 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 * 02111-1307, USA.
21 */
22
23#include <zebra.h>
24
25#include "linklist.h"
26#include "if.h"
27#include "prefix.h"
28#include "ioctl.h"
29#include "log.h"
30#include "privs.h"
31
32#include "zebra/rib.h"
33#include "zebra/rt.h"
34#include "zebra/interface.h"
35
36#ifdef HAVE_BSD_LINK_DETECT
37#include <net/if_media.h>
38#endif /* HAVE_BSD_LINK_DETECT*/
39
40extern struct zebra_privs_t zserv_privs;
41
42/* clear and set interface name string */
43void
44ifreq_set_name (struct ifreq *ifreq, struct interface *ifp)
45{
46  strncpy (ifreq->ifr_name, ifp->name, IFNAMSIZ);
47}
48
49/* call ioctl system call */
50int
51if_ioctl (u_long request, caddr_t buffer)
52{
53  int sock;
54  int ret;
55  int err;
56
57  if (zserv_privs.change(ZPRIVS_RAISE))
58    zlog (NULL, LOG_ERR, "Can't raise privileges");
59  sock = socket (AF_INET, SOCK_DGRAM, 0);
60  if (sock < 0)
61    {
62      int save_errno = errno;
63      if (zserv_privs.change(ZPRIVS_LOWER))
64        zlog (NULL, LOG_ERR, "Can't lower privileges");
65      zlog_err("Cannot create UDP socket: %s", safe_strerror(save_errno));
66      exit (1);
67    }
68  if ((ret = ioctl (sock, request, buffer)) < 0)
69    err = errno;
70  if (zserv_privs.change(ZPRIVS_LOWER))
71    zlog (NULL, LOG_ERR, "Can't lower privileges");
72  close (sock);
73
74  if (ret < 0)
75    {
76      errno = err;
77      return ret;
78    }
79  return 0;
80}
81
82#ifdef HAVE_IPV6
83static int
84if_ioctl_ipv6 (u_long request, caddr_t buffer)
85{
86  int sock;
87  int ret;
88  int err;
89
90  if (zserv_privs.change(ZPRIVS_RAISE))
91    zlog (NULL, LOG_ERR, "Can't raise privileges");
92  sock = socket (AF_INET6, SOCK_DGRAM, 0);
93  if (sock < 0)
94    {
95      int save_errno = errno;
96      if (zserv_privs.change(ZPRIVS_LOWER))
97        zlog (NULL, LOG_ERR, "Can't lower privileges");
98      zlog_err("Cannot create IPv6 datagram socket: %s",
99	       safe_strerror(save_errno));
100      exit (1);
101    }
102
103  if ((ret = ioctl (sock, request, buffer)) < 0)
104    err = errno;
105  if (zserv_privs.change(ZPRIVS_LOWER))
106    zlog (NULL, LOG_ERR, "Can't lower privileges");
107  close (sock);
108
109  if (ret < 0)
110    {
111      errno = err;
112      return ret;
113    }
114  return 0;
115}
116#endif /* HAVE_IPV6 */
117
118/*
119 * get interface metric
120 *   -- if value is not avaliable set -1
121 */
122void
123if_get_metric (struct interface *ifp)
124{
125#ifdef SIOCGIFMETRIC
126  struct ifreq ifreq;
127
128  ifreq_set_name (&ifreq, ifp);
129
130  if (if_ioctl (SIOCGIFMETRIC, (caddr_t) &ifreq) < 0)
131    return;
132  ifp->metric = ifreq.ifr_metric;
133  if (ifp->metric == 0)
134    ifp->metric = 1;
135#else /* SIOCGIFMETRIC */
136  ifp->metric = -1;
137#endif /* SIOCGIFMETRIC */
138}
139
140/* get interface MTU */
141void
142if_get_mtu (struct interface *ifp)
143{
144  struct ifreq ifreq;
145
146  ifreq_set_name (&ifreq, ifp);
147
148#if defined(SIOCGIFMTU)
149  if (if_ioctl (SIOCGIFMTU, (caddr_t) & ifreq) < 0)
150    {
151      zlog_info ("Can't lookup mtu by ioctl(SIOCGIFMTU)");
152      ifp->mtu6 = ifp->mtu = -1;
153      return;
154    }
155
156#ifdef SUNOS_5
157  ifp->mtu6 = ifp->mtu = ifreq.ifr_metric;
158#else
159  ifp->mtu6 = ifp->mtu = ifreq.ifr_mtu;
160#endif /* SUNOS_5 */
161
162  /* propogate */
163  zebra_interface_up_update(ifp);
164
165#else
166  zlog (NULL, LOG_INFO, "Can't lookup mtu on this system");
167  ifp->mtu6 = ifp->mtu = -1;
168#endif
169}
170
171#ifdef HAVE_NETLINK
172/* Interface address setting via netlink interface. */
173int
174if_set_prefix (struct interface *ifp, struct connected *ifc)
175{
176  return kernel_address_add_ipv4 (ifp, ifc);
177}
178
179/* Interface address is removed using netlink interface. */
180int
181if_unset_prefix (struct interface *ifp, struct connected *ifc)
182{
183  return kernel_address_delete_ipv4 (ifp, ifc);
184}
185#else /* ! HAVE_NETLINK */
186#ifdef HAVE_STRUCT_IFALIASREQ
187/* Set up interface's IP address, netmask (and broadcas? ).  *BSD may
188   has ifaliasreq structure.  */
189int
190if_set_prefix (struct interface *ifp, struct connected *ifc)
191{
192  int ret;
193  struct ifaliasreq addreq;
194  struct sockaddr_in addr;
195  struct sockaddr_in mask;
196  struct prefix_ipv4 *p;
197
198  p = (struct prefix_ipv4 *) ifc->address;
199  rib_lookup_and_pushup (p);
200
201  memset (&addreq, 0, sizeof addreq);
202  strncpy ((char *)&addreq.ifra_name, ifp->name, sizeof addreq.ifra_name);
203
204  memset (&addr, 0, sizeof (struct sockaddr_in));
205  addr.sin_addr = p->prefix;
206  addr.sin_family = p->family;
207#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
208  addr.sin_len = sizeof (struct sockaddr_in);
209#endif
210  memcpy (&addreq.ifra_addr, &addr, sizeof (struct sockaddr_in));
211
212  memset (&mask, 0, sizeof (struct sockaddr_in));
213  masklen2ip (p->prefixlen, &mask.sin_addr);
214  mask.sin_family = p->family;
215#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
216  mask.sin_len = sizeof (struct sockaddr_in);
217#endif
218  memcpy (&addreq.ifra_mask, &mask, sizeof (struct sockaddr_in));
219
220  ret = if_ioctl (SIOCAIFADDR, (caddr_t) &addreq);
221  if (ret < 0)
222    return ret;
223  return 0;
224}
225
226/* Set up interface's IP address, netmask (and broadcas? ).  *BSD may
227   has ifaliasreq structure.  */
228int
229if_unset_prefix (struct interface *ifp, struct connected *ifc)
230{
231  int ret;
232  struct ifaliasreq addreq;
233  struct sockaddr_in addr;
234  struct sockaddr_in mask;
235  struct prefix_ipv4 *p;
236
237  p = (struct prefix_ipv4 *)ifc->address;
238
239  memset (&addreq, 0, sizeof addreq);
240  strncpy ((char *)&addreq.ifra_name, ifp->name, sizeof addreq.ifra_name);
241
242  memset (&addr, 0, sizeof (struct sockaddr_in));
243  addr.sin_addr = p->prefix;
244  addr.sin_family = p->family;
245#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
246  addr.sin_len = sizeof (struct sockaddr_in);
247#endif
248  memcpy (&addreq.ifra_addr, &addr, sizeof (struct sockaddr_in));
249
250  memset (&mask, 0, sizeof (struct sockaddr_in));
251  masklen2ip (p->prefixlen, &mask.sin_addr);
252  mask.sin_family = p->family;
253#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
254  mask.sin_len = sizeof (struct sockaddr_in);
255#endif
256  memcpy (&addreq.ifra_mask, &mask, sizeof (struct sockaddr_in));
257
258  ret = if_ioctl (SIOCDIFADDR, (caddr_t) &addreq);
259  if (ret < 0)
260    return ret;
261  return 0;
262}
263#else
264/* Set up interface's address, netmask (and broadcas? ).  Linux or
265   Solaris uses ifname:number semantics to set IP address aliases. */
266int
267if_set_prefix (struct interface *ifp, struct connected *ifc)
268{
269  int ret;
270  struct ifreq ifreq;
271  struct sockaddr_in addr;
272  struct sockaddr_in broad;
273  struct sockaddr_in mask;
274  struct prefix_ipv4 ifaddr;
275  struct prefix_ipv4 *p;
276
277  p = (struct prefix_ipv4 *) ifc->address;
278
279  ifaddr = *p;
280
281  ifreq_set_name (&ifreq, ifp);
282
283  addr.sin_addr = p->prefix;
284  addr.sin_family = p->family;
285  memcpy (&ifreq.ifr_addr, &addr, sizeof (struct sockaddr_in));
286  ret = if_ioctl (SIOCSIFADDR, (caddr_t) &ifreq);
287  if (ret < 0)
288    return ret;
289
290  /* We need mask for make broadcast addr. */
291  masklen2ip (p->prefixlen, &mask.sin_addr);
292
293  if (if_is_broadcast (ifp))
294    {
295      apply_mask_ipv4 (&ifaddr);
296      addr.sin_addr = ifaddr.prefix;
297
298      broad.sin_addr.s_addr = (addr.sin_addr.s_addr | ~mask.sin_addr.s_addr);
299      broad.sin_family = p->family;
300
301      memcpy (&ifreq.ifr_broadaddr, &broad, sizeof (struct sockaddr_in));
302      ret = if_ioctl (SIOCSIFBRDADDR, (caddr_t) &ifreq);
303      if (ret < 0)
304	return ret;
305    }
306
307  mask.sin_family = p->family;
308#ifdef SUNOS_5
309  memcpy (&mask, &ifreq.ifr_addr, sizeof (mask));
310#else
311  memcpy (&ifreq.ifr_netmask, &mask, sizeof (struct sockaddr_in));
312#endif /* SUNOS5 */
313  ret = if_ioctl (SIOCSIFNETMASK, (caddr_t) &ifreq);
314  if (ret < 0)
315    return ret;
316
317  return 0;
318}
319
320/* Set up interface's address, netmask (and broadcas? ).  Linux or
321   Solaris uses ifname:number semantics to set IP address aliases. */
322int
323if_unset_prefix (struct interface *ifp, struct connected *ifc)
324{
325  int ret;
326  struct ifreq ifreq;
327  struct sockaddr_in addr;
328  struct prefix_ipv4 *p;
329
330  p = (struct prefix_ipv4 *) ifc->address;
331
332  ifreq_set_name (&ifreq, ifp);
333
334  memset (&addr, 0, sizeof (struct sockaddr_in));
335  addr.sin_family = p->family;
336  memcpy (&ifreq.ifr_addr, &addr, sizeof (struct sockaddr_in));
337  ret = if_ioctl (SIOCSIFADDR, (caddr_t) &ifreq);
338  if (ret < 0)
339    return ret;
340
341  return 0;
342}
343#endif /* HAVE_STRUCT_IFALIASREQ */
344#endif /* HAVE_NETLINK */
345
346/* get interface flags */
347void
348if_get_flags (struct interface *ifp)
349{
350  int ret;
351  struct ifreq ifreq;
352#ifdef HAVE_BSD_LINK_DETECT
353  struct ifmediareq ifmr;
354#endif /* HAVE_BSD_LINK_DETECT */
355
356  ifreq_set_name (&ifreq, ifp);
357
358  ret = if_ioctl (SIOCGIFFLAGS, (caddr_t) &ifreq);
359  if (ret < 0)
360    {
361      zlog_err("if_ioctl(SIOCGIFFLAGS) failed: %s", safe_strerror(errno));
362      return;
363    }
364#ifdef HAVE_BSD_LINK_DETECT /* Detect BSD link-state at start-up */
365
366  /* Per-default, IFF_RUNNING is held high, unless link-detect says
367   * otherwise - we abuse IFF_RUNNING inside zebra as a link-state flag,
368   * following practice on Linux and Solaris kernels
369   */
370  SET_FLAG(ifreq.ifr_flags, IFF_RUNNING);
371
372  if (CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_LINKDETECTION))
373    {
374      (void) memset(&ifmr, 0, sizeof(ifmr));
375      strncpy (ifmr.ifm_name, ifp->name, IFNAMSIZ);
376
377      /* Seems not all interfaces implement this ioctl */
378      if (if_ioctl(SIOCGIFMEDIA, (caddr_t) &ifmr) < 0)
379        zlog_err("if_ioctl(SIOCGIFMEDIA) failed: %s", safe_strerror(errno));
380      else if (ifmr.ifm_status & IFM_AVALID) /* Link state is valid */
381        {
382          if (ifmr.ifm_status & IFM_ACTIVE)
383            SET_FLAG(ifreq.ifr_flags, IFF_RUNNING);
384          else
385            UNSET_FLAG(ifreq.ifr_flags, IFF_RUNNING);
386        }
387  }
388#endif /* HAVE_BSD_LINK_DETECT */
389
390  if_flags_update (ifp, (ifreq.ifr_flags & 0x0000ffff));
391}
392
393/* Set interface flags */
394int
395if_set_flags (struct interface *ifp, uint64_t flags)
396{
397  int ret;
398  struct ifreq ifreq;
399
400  memset (&ifreq, 0, sizeof(struct ifreq));
401  ifreq_set_name (&ifreq, ifp);
402
403  ifreq.ifr_flags = ifp->flags;
404  ifreq.ifr_flags |= flags;
405
406  ret = if_ioctl (SIOCSIFFLAGS, (caddr_t) &ifreq);
407
408  if (ret < 0)
409    {
410      zlog_info ("can't set interface flags");
411      return ret;
412    }
413  return 0;
414}
415
416/* Unset interface's flag. */
417int
418if_unset_flags (struct interface *ifp, uint64_t flags)
419{
420  int ret;
421  struct ifreq ifreq;
422
423  memset (&ifreq, 0, sizeof(struct ifreq));
424  ifreq_set_name (&ifreq, ifp);
425
426  ifreq.ifr_flags = ifp->flags;
427  ifreq.ifr_flags &= ~flags;
428
429  ret = if_ioctl (SIOCSIFFLAGS, (caddr_t) &ifreq);
430
431  if (ret < 0)
432    {
433      zlog_info ("can't unset interface flags");
434      return ret;
435    }
436  return 0;
437}
438
439#ifdef HAVE_IPV6
440
441#ifdef LINUX_IPV6
442#ifndef _LINUX_IN6_H
443/* linux/include/net/ipv6.h */
444struct in6_ifreq
445{
446  struct in6_addr ifr6_addr;
447  u_int32_t ifr6_prefixlen;
448  int ifr6_ifindex;
449};
450#endif /* _LINUX_IN6_H */
451
452/* Interface's address add/delete functions. */
453int
454if_prefix_add_ipv6 (struct interface *ifp, struct connected *ifc)
455{
456  int ret;
457  struct prefix_ipv6 *p;
458  struct in6_ifreq ifreq;
459
460  p = (struct prefix_ipv6 *) ifc->address;
461
462  memset (&ifreq, 0, sizeof (struct in6_ifreq));
463
464  memcpy (&ifreq.ifr6_addr, &p->prefix, sizeof (struct in6_addr));
465  ifreq.ifr6_ifindex = ifp->ifindex;
466  ifreq.ifr6_prefixlen = p->prefixlen;
467
468  ret = if_ioctl_ipv6 (SIOCSIFADDR, (caddr_t) &ifreq);
469
470  return ret;
471}
472
473int
474if_prefix_delete_ipv6 (struct interface *ifp, struct connected *ifc)
475{
476  int ret;
477  struct prefix_ipv6 *p;
478  struct in6_ifreq ifreq;
479
480  p = (struct prefix_ipv6 *) ifc->address;
481
482  memset (&ifreq, 0, sizeof (struct in6_ifreq));
483
484  memcpy (&ifreq.ifr6_addr, &p->prefix, sizeof (struct in6_addr));
485  ifreq.ifr6_ifindex = ifp->ifindex;
486  ifreq.ifr6_prefixlen = p->prefixlen;
487
488  ret = if_ioctl_ipv6 (SIOCDIFADDR, (caddr_t) &ifreq);
489
490  return ret;
491}
492#else /* LINUX_IPV6 */
493#ifdef HAVE_STRUCT_IN6_ALIASREQ
494#ifndef ND6_INFINITE_LIFETIME
495#define ND6_INFINITE_LIFETIME 0xffffffffL
496#endif /* ND6_INFINITE_LIFETIME */
497int
498if_prefix_add_ipv6 (struct interface *ifp, struct connected *ifc)
499{
500  int ret;
501  struct in6_aliasreq addreq;
502  struct sockaddr_in6 addr;
503  struct sockaddr_in6 mask;
504  struct prefix_ipv6 *p;
505
506  p = (struct prefix_ipv6 * ) ifc->address;
507
508  memset (&addreq, 0, sizeof addreq);
509  strncpy ((char *)&addreq.ifra_name, ifp->name, sizeof addreq.ifra_name);
510
511  memset (&addr, 0, sizeof (struct sockaddr_in6));
512  addr.sin6_addr = p->prefix;
513  addr.sin6_family = p->family;
514#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
515  addr.sin6_len = sizeof (struct sockaddr_in6);
516#endif
517  memcpy (&addreq.ifra_addr, &addr, sizeof (struct sockaddr_in6));
518
519  memset (&mask, 0, sizeof (struct sockaddr_in6));
520  masklen2ip6 (p->prefixlen, &mask.sin6_addr);
521  mask.sin6_family = p->family;
522#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
523  mask.sin6_len = sizeof (struct sockaddr_in6);
524#endif
525  memcpy (&addreq.ifra_prefixmask, &mask, sizeof (struct sockaddr_in6));
526
527  addreq.ifra_lifetime.ia6t_vltime = 0xffffffff;
528  addreq.ifra_lifetime.ia6t_pltime = 0xffffffff;
529
530#ifdef HAVE_STRUCT_IF6_ALIASREQ_IFRA_LIFETIME
531  addreq.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
532  addreq.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
533#endif
534
535  ret = if_ioctl_ipv6 (SIOCAIFADDR_IN6, (caddr_t) &addreq);
536  if (ret < 0)
537    return ret;
538  return 0;
539}
540
541int
542if_prefix_delete_ipv6 (struct interface *ifp, struct connected *ifc)
543{
544  int ret;
545  struct in6_aliasreq addreq;
546  struct sockaddr_in6 addr;
547  struct sockaddr_in6 mask;
548  struct prefix_ipv6 *p;
549
550  p = (struct prefix_ipv6 *) ifc->address;
551
552  memset (&addreq, 0, sizeof addreq);
553  strncpy ((char *)&addreq.ifra_name, ifp->name, sizeof addreq.ifra_name);
554
555  memset (&addr, 0, sizeof (struct sockaddr_in6));
556  addr.sin6_addr = p->prefix;
557  addr.sin6_family = p->family;
558#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
559  addr.sin6_len = sizeof (struct sockaddr_in6);
560#endif
561  memcpy (&addreq.ifra_addr, &addr, sizeof (struct sockaddr_in6));
562
563  memset (&mask, 0, sizeof (struct sockaddr_in6));
564  masklen2ip6 (p->prefixlen, &mask.sin6_addr);
565  mask.sin6_family = p->family;
566#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
567  mask.sin6_len = sizeof (struct sockaddr_in6);
568#endif
569  memcpy (&addreq.ifra_prefixmask, &mask, sizeof (struct sockaddr_in6));
570
571#ifdef HAVE_STRUCT_IF6_ALIASREQ_IFRA_LIFETIME
572  addreq.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
573  addreq.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
574#endif
575
576  ret = if_ioctl_ipv6 (SIOCDIFADDR_IN6, (caddr_t) &addreq);
577  if (ret < 0)
578    return ret;
579  return 0;
580}
581#else
582int
583if_prefix_add_ipv6 (struct interface *ifp, struct connected *ifc)
584{
585  return 0;
586}
587
588int
589if_prefix_delete_ipv6 (struct interface *ifp, struct connected *ifc)
590{
591  return 0;
592}
593#endif /* HAVE_STRUCT_IN6_ALIASREQ */
594
595#endif /* LINUX_IPV6 */
596
597#endif /* HAVE_IPV6 */
598