diff options
Diffstat (limited to 'sysdep/unix/krt-iface.c')
-rw-r--r-- | sysdep/unix/krt-iface.c | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/sysdep/unix/krt-iface.c b/sysdep/unix/krt-iface.c new file mode 100644 index 00000000..3b665a44 --- /dev/null +++ b/sysdep/unix/krt-iface.c @@ -0,0 +1,194 @@ +/* + * BIRD -- Unix Interface Scanning and Syncing + * + * (c) 1998--1999 Martin Mares <mj@ucw.cz> + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include <string.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <net/if.h> +#include <sys/ioctl.h> +#include <errno.h> + +#define LOCAL_DEBUG + +#include "nest/bird.h" +#include "nest/iface.h" +#include "nest/route.h" +#include "nest/protocol.h" +#include "lib/timer.h" +#include "lib/krt.h" + +#include "unix.h" + +int if_scan_sock = -1; + +static void +scan_ifs(struct ifreq *r, int cnt) +{ + struct iface i; + char *err; + unsigned fl; + ip_addr netmask; + int l; + + if_start_update(); + for (cnt /= sizeof(struct ifreq); cnt; cnt--, r++) + { + bzero(&i, sizeof(i)); + DBG("%s\n", r->ifr_ifrn.ifrn_name); + strncpy(i.name, r->ifr_ifrn.ifrn_name, sizeof(i.name) - 1); + get_sockaddr((struct sockaddr_in *) &r->ifr_addr, &i.ip, NULL); + if (ipa_nonzero(i.ip)) + { + l = ipa_classify(i.ip); + if (l < 0 || !(l & IADDR_HOST)) + { + log(L_ERR "%s: Invalid interface address", i.name); + i.ip = IPA_NONE; + } + else if ((l & IADDR_SCOPE_MASK) == SCOPE_HOST) + i.flags |= IF_LOOPBACK | IF_IGNORE; + } + + if (ioctl(if_scan_sock, SIOCGIFFLAGS, r) < 0) + { + err = "SIOCGIFFLAGS"; + faulty: + log(L_ERR "%s(%s): %m", err, i.name); + bad: + i.flags = (i.flags & ~IF_LINK_UP) | IF_ADMIN_DOWN; + continue; + } + fl = r->ifr_flags; + if (fl & IFF_UP) + i.flags |= IF_LINK_UP; + + if (ioctl(if_scan_sock, SIOCGIFNETMASK, r) < 0) + { err = "SIOCGIFNETMASK"; goto faulty; } + get_sockaddr((struct sockaddr_in *) &r->ifr_addr, &netmask, NULL); + l = ipa_mklen(netmask); + if (l < 0 || l == 31) + { + log(L_ERR "%s: Invalid netmask", i.name); + goto bad; + } + i.pxlen = l; + + if (fl & IFF_POINTOPOINT) + { + i.flags |= IF_UNNUMBERED; + i.pxlen = BITS_PER_IP_ADDRESS; + if (ioctl(if_scan_sock, SIOCGIFDSTADDR, r) < 0) + { err = "SIOCGIFDSTADDR"; goto faulty; } + get_sockaddr((struct sockaddr_in *) &r->ifr_addr, &i.opposite, NULL); + } + if (fl & IFF_LOOPBACK) + i.flags |= IF_LOOPBACK | IF_IGNORE; +#ifndef CONFIG_ALL_MULTICAST + if (fl & IFF_MULTICAST) +#endif + i.flags |= IF_MULTICAST; + + i.prefix = ipa_and(i.ip, ipa_mkmask(i.pxlen)); + if (i.pxlen < 32) + { + i.brd = ipa_or(i.prefix, ipa_not(ipa_mkmask(i.pxlen))); + if (ipa_equal(i.ip, i.prefix) || ipa_equal(i.ip, i.brd)) + { + log(L_ERR "%s: Using network or broadcast address for interface", i.name); + goto bad; + } + if (fl & IFF_BROADCAST) + i.flags |= IF_BROADCAST; + if (i.pxlen < 30) + i.flags |= IF_MULTIACCESS; + else + i.opposite = ipa_opposite(i.ip); + } + else + i.brd = i.opposite; + + if (ioctl(if_scan_sock, SIOCGIFMTU, r) < 0) + { err = "SIOCGIFMTU"; goto faulty; } + i.mtu = r->ifr_mtu; + +#ifdef SIOCGIFINDEX + if (ioctl(if_scan_sock, SIOCGIFINDEX, r) < 0) + DBG("SIOCGIFINDEX failed: %m\n"); + else + i.index = r->ifr_ifindex; +#else + /* FIXME: What else? Guess ifindex (we need it at least for OSPF on unnumbered links)? */ +#endif + + if_update(&i); + } + if_end_update(); +} + +void +krt_if_scan(struct krt_proto *p) +{ + struct ifconf ic; + static int last_ifbuf_size = 4*sizeof(struct ifreq); + int res; + + for(;;) + { + if (last_ifbuf_size) + { + struct ifreq *r = alloca(last_ifbuf_size); + ic.ifc_ifcu.ifcu_req = r; + ic.ifc_len = last_ifbuf_size; + res = ioctl(if_scan_sock, SIOCGIFCONF, &ic); + if (res < 0 && errno != EFAULT) + die("SIOCCGIFCONF: %m"); + if (res < last_ifbuf_size) + { + scan_ifs(r, ic.ifc_len); + break; + } + } +#ifdef CLEAN_WAY_WORKING_ONLY_ON_LINUX_2_1 /* FIXME */ + ic.ifc_req = NULL; + ic.ifc_len = 999999999; + if (ioctl(if_scan_sock, SIOCGIFCONF, &ic) < 0) + die("SIOCIFCONF: %m"); + ic.ifc_len += sizeof(struct ifreq); + if (last_ifbuf_size < ic.ifc_len) + { + last_ifbuf_size = ic.ifc_len; + DBG("Increased ifconf buffer size to %d\n", last_ifbuf_size); + } +#else + last_ifbuf_size *= 2; + DBG("Increased ifconf buffer size to %d\n", last_ifbuf_size); +#endif + } +} + +void +krt_if_preconfig(struct krt_config *c) +{ +} + +void +krt_if_start(struct krt_proto *p) +{ + if (if_scan_sock < 0) + { + if_scan_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + DBG("Using socket %d for interface and route scanning\n", if_scan_sock); + if (if_scan_sock < 0) + die("Cannot create scanning socket: %m"); + } +} + +void +krt_if_shutdown(struct krt_proto *p) +{ +} |