diff --git a/TODO b/TODO index 105f7e1f..2c0a0227 100644 --- a/TODO +++ b/TODO @@ -2,8 +2,6 @@ Core ~~~~ - IPv6 support -- io.c: refuse old-style multicasts for unnumbered interfaces? - - counters (according to SNMP MIB?) - better memory allocators - real attribute cache @@ -21,12 +19,8 @@ Core - iface: SIOCGIFINDEX exists on glibc systems, but it doesn't work on 2.0.x kernels! -* glibc problems with struct mreqn - - socket: Use IP_RECVERR for BGP TCP sockets? -- OSPF: refuse running on non-multicast devices - - config: executable config files - config: when parsing prefix, check zero bits - config: reconfiguration @@ -82,6 +76,7 @@ RIP OSPF ~~~~ + - refuse running on non-multicast devices - importing of device routes for networks where we don't run OSPF - check incoming packets using neighbor cache - RFC2328 appendix E: Use a better algorithm diff --git a/sysdep/cf/README b/sysdep/cf/README index b1ed8683..a3442752 100644 --- a/sysdep/cf/README +++ b/sysdep/cf/README @@ -2,7 +2,6 @@ Available configuration variables: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CONFIG_AUTO_ROUTES Device routes are added automagically by the kernel -CONFIG_ALL_MULTICAST All devices support multicasting (i.e., ignore IFF_MULTICAST) CONFIG_SELF_CONSCIOUS We're able to recognize whether route was installed by us CONFIG_MULTIPLE_TABLES The kernel supports multiple routing tables CONFIG_ALL_TABLES_AT_ONCE Kernel scanner wants to process all tables at once @@ -10,3 +9,10 @@ CONFIG_ALL_TABLES_AT_ONCE Kernel scanner wants to process all tables at once CONFIG_UNIX_IFACE Use Unix interface scanner CONFIG_UNIX_SET Use Unix route setting CONFIG_LINUX_SCAN Use Linux /proc/net/route scanner + +CONFIG_ALL_MULTICAST krt-iface: All devices support multicasting (i.e., ignore IFF_MULTICAST) +CONFIG_UNNUM_MULTICAST krt-iface: We support multicasts on unnumbered PtP devices + +CONFIG_LINUX_MC_MREQN Linux: Use struct mreqn for multicasting +CONFIG_LINUX_MC_MREQ Linux: Use struct mreq +CONFIG_LINUX_MC_MREQ_BIND Linux: Use struct mreq and SO_BINDTODEVICE diff --git a/sysdep/cf/linux-20.h b/sysdep/cf/linux-20.h index 5fc440c9..5c6a2377 100644 --- a/sysdep/cf/linux-20.h +++ b/sysdep/cf/linux-20.h @@ -7,7 +7,6 @@ */ #undef CONFIG_AUTO_ROUTES -#define CONFIG_ALL_MULTICAST #undef CONFIG_SELF_CONSCIOUS #undef CONFIG_MULTIPLE_TABLES @@ -15,6 +14,10 @@ #define CONFIG_UNIX_SET #define CONFIG_LINUX_SCAN +#define CONFIG_LINUX_MC_MREQ_BIND +#define CONFIG_ALL_MULTICAST +#define CONFIG_UNNUM_MULTICAST + /* Link: sysdep/linux Link: sysdep/unix diff --git a/sysdep/cf/linux-21.h b/sysdep/cf/linux-21.h index 34e81830..17882c81 100644 --- a/sysdep/cf/linux-21.h +++ b/sysdep/cf/linux-21.h @@ -7,7 +7,6 @@ */ #define CONFIG_AUTO_ROUTES -#define CONFIG_ALL_MULTICAST #undef CONFIG_SELF_CONSCIOUS #undef CONFIG_MULTIPLE_TABLES @@ -15,6 +14,10 @@ #define CONFIG_UNIX_SET #define CONFIG_LINUX_SCAN +#define CONFIG_LINUX_MC_MREQN +#define CONFIG_ALL_MULTICAST +#define CONFIG_UNNUM_MULTICAST + /* Link: sysdep/linux Link: sysdep/unix diff --git a/sysdep/cf/linux-22.h b/sysdep/cf/linux-22.h index cd5090f6..52a4681a 100644 --- a/sysdep/cf/linux-22.h +++ b/sysdep/cf/linux-22.h @@ -7,11 +7,12 @@ */ #define CONFIG_AUTO_ROUTES -#define CONFIG_ALL_MULTICAST #define CONFIG_SELF_CONSCIOUS #define CONFIG_MULTIPLE_TABLES #define CONFIG_ALL_TABLES_AT_ONCE +#define CONFIG_LINUX_MC_MREQN + /* Link: sysdep/linux/netlink Link: sysdep/linux diff --git a/sysdep/linux/Modules b/sysdep/linux/Modules index 4a99916a..1b867d81 100644 --- a/sysdep/linux/Modules +++ b/sysdep/linux/Modules @@ -2,3 +2,4 @@ krt-scan.c krt-scan.h #endif +sysio.h diff --git a/sysdep/linux/sysio.h b/sysdep/linux/sysio.h new file mode 100644 index 00000000..bef8a612 --- /dev/null +++ b/sysdep/linux/sysio.h @@ -0,0 +1,118 @@ +/* + * BIRD Internet Routing Daemon -- Linux Multicasting and Network Includes + * + * (c) 1998--1999 Martin Mares + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#ifdef IPV6 +#include /* FIXMEv6: glibc variant? */ + +#else + +/* + * Multicasting in Linux systems is a real mess. Not only different kernels + * have different interfaces, but also different libc's export it in different + * ways. Horrible. + */ + +static inline char *sysio_mcast_setup(sock *s) +{ + int zero = 0; + + if (ipa_nonzero(s->daddr)) + { + if ( +#ifdef IP_DEFAULT_MULTICAST_TTL + s->ttl != IP_DEFAULT_MULTICAST_TTL && +#endif + setsockopt(s->fd, SOL_IP, IP_MULTICAST_TTL, &s->ttl, sizeof(s->ttl)) < 0) + return "IP_MULTICAST_TTL"; + if ( +#ifdef IP_DEFAULT_MULTICAST_LOOP + IP_DEFAULT_MULTICAST_LOOP && +#endif + setsockopt(s->fd, SOL_IP, IP_MULTICAST_LOOP, &zero, sizeof(zero)) < 0) + return "IP_MULTICAST_LOOP"; + } + return NULL; +} + +#ifdef CONFIG_LINUX_MC_MREQN +/* + * 2.1 and newer kernels use struct mreqn which passes ifindex, so no + * problems with unnumbered devices. + */ + +#ifndef HAVE_STRUCT_IP_MREQN +/* Several versions of glibc don't define this structure, so we have to do it ourselves */ +struct ip_mreqn +{ + struct in_addr imr_multiaddr; /* IP multicast address of group */ + struct in_addr imr_address; /* local IP address of interface */ + int imr_ifindex; /* Interface index */ +}; +#endif + +static inline char *sysio_mcast_join(sock *s) +{ + struct ip_mreqn mreq; + char *err; + + if (err = sysio_mcast_setup(s)) + return err; + mreq.imr_ifindex = s->iface->index; + set_inaddr(&mreq.imr_address, s->iface->addr->ip); + set_inaddr(&mreq.imr_multiaddr, s->daddr); + /* This defines where should we send _outgoing_ multicasts */ + if (setsockopt(s->fd, SOL_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq)) < 0) + return "IP_MULTICAST_IF"; + /* And this one sets interface for _receiving_ multicasts from */ + if (ipa_nonzero(s->saddr) && setsockopt(s->fd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) + return "IP_ADD_MEMBERSHIP"; + return NULL; +} +#endif + +#if defined(CONFIG_LINUX_MC_MREQ) || defined(CONFIG_LINUX_MC_MREQ_BIND) +/* + * Older kernels support only struct mreq which matches interfaces by their + * addresses and thus fails on unnumbered devices. On newer 2.0 kernels + * we can use SO_BINDTODEVICE to circumvent this problem. + */ + +#include + +static inline char *sysio_mcast_join(sock *s) +{ + struct in_addr mreq; + struct ip_mreq mreq_add; + char *err; + + if (err = sysio_mcast_setup(s)) + return err; + set_inaddr(&mreq, s->iface->addr->ip); +#ifdef CONFIG_LINUX_MC_MREQ_BIND + { + struct ifreq ifr; + strcpy(ifr.ifr_name, s->iface->name); + if (setsockopt(s->fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0) + return "SO_BINDTODEVICE"; + mreq_add.imr_interface.s_addr = INADDR_ANY; + } +#else + mreq_add.imr_interface = mreq; +#endif + set_inaddr(&mreq_add.imr_multiaddr, s->daddr); + /* This defines where should we send _outgoing_ multicasts */ + if (setsockopt(s->fd, SOL_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq)) < 0) + return "IP_MULTICAST_IF"; + /* And this one sets interface for _receiving_ multicasts from */ + if (ipa_nonzero(s->saddr) && setsockopt(s->fd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq_add, sizeof(mreq_add)) < 0) + return "IP_ADD_MEMBERSHIP"; + return NULL; +} +#endif + +#endif diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 7e71d20d..af630f5e 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -17,10 +17,6 @@ #include #include -#ifndef HAVE_STRUCT_IP_MREQN -#include -#endif - #include "nest/bird.h" #include "lib/lists.h" #include "lib/resource.h" @@ -30,10 +26,6 @@ #include "lib/string.h" #include "nest/iface.h" -#ifdef IPV6 -#include /* FIXMEv6: glibc variant? */ -#endif - #include "lib/unix.h" /* @@ -383,7 +375,7 @@ sk_new(pool *p) } #define ERR(x) do { err = x; goto bad; } while(0) -#define WARN(x) log(L_WARN "sk_setup: " x) +#define WARN(x) log(L_WARN "sk_setup: %s: %m", x) #ifdef IPV6 @@ -444,6 +436,8 @@ get_sockaddr(sockaddr *sa, ip_addr *a, unsigned *port) #endif +#include "lib/sysio.h" + static char * sk_setup(sock *s) { @@ -530,11 +524,9 @@ sk_open(sock *s) { int fd, e; sockaddr sa; - int zero = 0; int one = 1; int type = s->type; int has_src = ipa_nonzero(s->saddr) || s->sport; - int has_dest = ipa_nonzero(s->daddr); char *err; switch (type) @@ -581,9 +573,10 @@ sk_open(sock *s) #ifdef IPV6 /* Fortunately, IPv6 socket interface is recent enough and therefore standardized */ ASSERT(s->iface && s->iface->addr); - if (has_dest) + if (ipa_nonzero(s->daddr)) { int t = s->iface->index; + int zero = 0; if (setsockopt(fd, SOL_IPV6, IPV6_MULTICAST_HOPS, &s->ttl, sizeof(s->ttl)) < 0) ERR("IPV6_MULTICAST_HOPS"); if (setsockopt(fd, SOL_IPV6, IPV6_MULTICAST_LOOP, &zero, sizeof(zero)) < 0) @@ -601,56 +594,9 @@ sk_open(sock *s) } #else /* With IPv4 there are zillions of different socket interface variants. Ugh. */ -#ifdef HAVE_STRUCT_IP_MREQN - struct ip_mreqn mreq; -#define mreq_add mreq ASSERT(s->iface && s->iface->addr); - mreq.imr_ifindex = s->iface->index; - set_inaddr(&mreq.imr_address, s->iface->addr->ip); -#else - struct in_addr mreq; - struct ip_mreq mreq_add; - ASSERT(s->iface && s->iface->addr); - set_inaddr(&mreq, s->iface->addr->ip); -#ifdef SO_BINDTODEVICE - { - struct ifreq ifr; - strcpy(ifr.ifr_name, s->iface->name); - if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0) - ERR("SO_BINDTODEVICE"); -#if 0 /* FIXME */ - mreq_add.imr_interface.s_addr = INADDR_ANY; -#else - mreq_add.imr_interface = mreq; -#endif - } -#else -#error Multicasts not supported on PtP devices /* FIXME: Solve it somehow? */ - mreq_add.imr_interface = mreq; -#endif -#endif - set_inaddr(&mreq_add.imr_multiaddr, s->daddr); - if (has_dest) - { - if ( -#ifdef IP_DEFAULT_MULTICAST_TTL - s->ttl != IP_DEFAULT_MULTICAST_TTL && -#endif - setsockopt(fd, SOL_IP, IP_MULTICAST_TTL, &s->ttl, sizeof(s->ttl)) < 0) - ERR("IP_MULTICAST_TTL"); - if ( -#ifdef IP_DEFAULT_MULTICAST_LOOP - IP_DEFAULT_MULTICAST_LOOP && -#endif - setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP, &zero, sizeof(zero)) < 0) - ERR("IP_MULTICAST_LOOP"); - /* This defines where should we send _outgoing_ multicasts */ - if (setsockopt(fd, SOL_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq)) < 0) - ERR("IP_MULTICAST_IF"); - } - /* And this one sets interface for _receiving_ multicasts from */ - if (has_src && setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq_add, sizeof(mreq_add)) < 0) - ERR("IP_ADD_MEMBERSHIP"); + if (err = sysio_mcast_join(s)) + goto bad; #endif break; } diff --git a/sysdep/unix/krt-iface.c b/sysdep/unix/krt-iface.c index a41b5ae9..3a50e8b2 100644 --- a/sysdep/unix/krt-iface.c +++ b/sysdep/unix/krt-iface.c @@ -102,9 +102,14 @@ scan_ifs(struct ifreq *r, int cnt) } if (fl & IFF_LOOPBACK) i.flags |= IF_LOOPBACK | IF_IGNORE; + if (1 #ifndef CONFIG_ALL_MULTICAST - if (fl & IFF_MULTICAST) + && (fl & IFF_MULTICAST) #endif +#ifndef CONFIG_UNNUM_MULTICAST + && !(i.flags & IF_UNNUMBERED) +#endif + ) i.flags |= IF_MULTICAST; a.prefix = ipa_and(a.ip, ipa_mkmask(a.pxlen));