/*
* The olsr.org Optimized Link-State Routing daemon(olsrd)
* Copyright (c) 2004, Andreas Tønnesen(andreto@olsr.org)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of olsr.org, olsrd nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Visit http://www.olsr.org for more information.
*
* If you find this software useful feel free to make a donation
* to the project. For more information see the website or contact
* the copyright holders.
*
* $Id: net.c,v 1.38 2007/10/04 22:27:31 bernd67 Exp $
*/
#include "defs.h"
#include "net_os.h"
#include "parser.h" /* dnc: needed for call to packet_parser() */
#include "net.h"
#include <net/if.h>
#ifdef __NetBSD__
#include <sys/param.h>
#include <net/if_ether.h>
#endif
#ifdef __OpenBSD__
#include <netinet/if_ether.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/icmp_var.h>
#include <netinet/icmp6.h>
#include <netinet6/in6_var.h> /* For struct in6_ifreq */
#include <ifaddrs.h>
#include <sys/uio.h>
#endif
#ifdef __FreeBSD__
#include <ifaddrs.h>
#include <net/if_var.h>
#include <net/ethernet.h>
#include <netinet/in_var.h>
#ifndef FBSD_NO_80211
#include <net80211/ieee80211.h>
#include <net80211/ieee80211_ioctl.h>
#endif
#endif
#ifdef __MacOSX__
#include <ifaddrs.h>
#include <net/if_var.h>
#include <net/ethernet.h>
#include <netinet/in_var.h>
#endif
#include <net/if_dl.h>
#ifdef SPOOF
#include <libnet.h>
#endif /* SPOOF */
//#define SIOCGIFGENERIC _IOWR('i', 58, struct ifreq) /* generic IF get op */
//#define SIOCGWAVELAN SIOCGIFGENERIC
#include <sys/sysctl.h>
static int ignore_redir;
static int send_redir;
static int gateway;
static int set_sysctl_int(char *name, int new)
{
int old;
#if __MacOSX__ || __OpenBSD__
size_t len = sizeof (old);
#else
unsigned int len = sizeof (old);
#endif
#ifdef __OpenBSD__
int mib[4];
/* Set net.inet.ip.forwarding by default. */
mib[0] = CTL_NET;
mib[1] = PF_INET;
mib[2] = IPPROTO_IP;
mib[3] = IPCTL_FORWARDING;
if (!strcmp(name, "net.inet6.ip6.forwarding"))
{
mib[1] = PF_INET6;
mib[2] = IPPROTO_IPV6;
}
else if (!strcmp(name, "net.inet.icmp.rediraccept"))
{
mib[2] = IPPROTO_ICMP;
mib[3] = ICMPCTL_REDIRACCEPT;
}
else if (!strcmp(name, "net.inet6.icmp6.rediraccept"))
{
mib[2] = IPPROTO_ICMPV6;
mib[3] = ICMPV6CTL_REDIRACCEPT;
}
else if (!strcmp(name, "net.inet.ip.redirect"))
{
mib[3] = IPCTL_SENDREDIRECTS;
}
else if (!strcmp(name, "net.inet6.ip6.redirect"))
{
mib[1] = PF_INET6;
mib[2] = IPPROTO_IPV6;
mib[3] = IPCTL_SENDREDIRECTS;
}
if (sysctl(mib, 4, &old, &len, &new, sizeof (new)) < 0)
return -1;
#else
if (sysctlbyname(name, &old, &len, &new, sizeof (new)) < 0)
return -1;
#endif
return old;
}
int enable_ip_forwarding(int version)
{
char *name;
if (olsr_cnf->ip_version == AF_INET)
name = "net.inet.ip.forwarding";
else
name = "net.inet6.ip6.forwarding";
gateway = set_sysctl_int(name, 1);
if (gateway < 0)
{
fprintf(stderr, "Cannot enable IP forwarding. Please enable IP forwarding manually. Continuing in 3 seconds...\n");
sleep(3);
}
return 1;
}
int
disable_redirects_global(int version)
{
char *name;
// do not accept ICMP redirects
#ifdef __OpenBSD__
if (olsr_cnf->ip_version == AF_INET)
name = "net.inet.icmp.rediraccept";
else
name = "net.inet6.icmp6.rediraccept";
ignore_redir = set_sysctl_int(name, 0);
#elif defined __FreeBSD__ || defined __MacOSX__
if (olsr_cnf->ip_version == AF_INET)
{
name = "net.inet.icmp.drop_redirect";
ignore_redir = set_sysctl_int(name, 1);
}
else
{
name = "net.inet6.icmp6.rediraccept";
ignore_redir = set_sysctl_int(name, 0);
}
#else
if (olsr_cnf->ip_version == AF_INET)
name = "net.inet.icmp.drop_redirect";
else
name = "net.inet6.icmp6.drop_redirect";
ignore_redir = set_sysctl_int(name, 1);
#endif
if (ignore_redir < 0)
{
fprintf(stderr, "Cannot disable incoming ICMP redirect messages. Please disable them manually. Continuing in 3 seconds...\n");
sleep(3);
}
// do not send ICMP redirects
if (olsr_cnf->ip_version == AF_INET)
name = "net.inet.ip.redirect";
else
name = "net.inet6.ip6.redirect";
send_redir = set_sysctl_int(name, 0);
if (send_redir < 0)
{
fprintf(stderr, "Cannot disable outgoing ICMP redirect messages. Please disable them manually. Continuing in 3 seconds...\n");
sleep(3);
}
return 1;
}
int disable_redirects(const char *if_name, struct interface *iface, int version)
{
// this function gets called for each interface olsrd uses; however,
// FreeBSD can only globally control ICMP redirects, and not on a
// per-interface basis; hence, only disable ICMP redirects in the "global"
// function
return 1;
}
int deactivate_spoof(const char *if_name, struct interface *iface, int version)
{
return 1;
}
int restore_settings(int version)
{
char *name;
// reset IP forwarding
if (olsr_cnf->ip_version == AF_INET)
name = "net.inet.ip.forwarding";
else
name = "net.inet6.ip6.forwarding";
set_sysctl_int(name, gateway);
// reset incoming ICMP redirects
#ifdef __OpenBSD__
if (olsr_cnf->ip_version == AF_INET)
name = "net.inet.icmp.rediraccept";
else
name = "net.inet6.icmp6.rediraccept";
#elif defined __FreeBSD__ || defined __MacOSX__
if (olsr_cnf->ip_version == AF_INET)
name = "net.inet.icmp.drop_redirect";
else
name = "net.inet6.icmp6.rediraccept";
#else
if (olsr_cnf->ip_version == AF_INET)
name = "net.inet.icmp.drop_redirect";
else
name = "net.inet6.icmp6.drop_redirect";
#endif
set_sysctl_int(name, ignore_redir);
// reset outgoing ICMP redirects
if (olsr_cnf->ip_version == AF_INET)
name = "net.inet.ip.redirect";
else
name = "net.inet6.ip6.redirect";
set_sysctl_int(name, send_redir);
return 1;
}
/**
*Creates a nonblocking broadcast socket.
*@param sa sockaddr struct. Used for bind(2).
*@return the FD of the socket or -1 on error.
*/
int
gethemusocket(struct sockaddr_in *pin)
{
int sock, on = 1;
OLSR_PRINTF(1, " Connecting to switch daemon port 10150...");
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("hcsocket");
syslog(LOG_ERR, "hcsocket: %m");
return (-1);
}
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
{
perror("SO_REUSEADDR failed");
close(sock);
return (-1);
}
/* connect to PORT on HOST */
if (connect(sock,(struct sockaddr *) pin, sizeof(*pin)) < 0)
{
printf("FAILED\n");
fprintf(stderr, "Error connecting %d - %s\n", errno, strerror(errno));
printf("connection refused\n");
close(sock);
return (-1);
}
printf("OK\n");
/* Keep TCP socket blocking */
return (sock);
}
int
getsocket(struct sockaddr *sa, int bufspace, char *int_name)
{
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
int sock, on = 1;
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("socket");
syslog(LOG_ERR, "socket: %m");
return (-1);
}
if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) < 0)
{
perror("setsockopt");
syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m");
close(sock);
return (-1);
}
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
{
perror("SO_REUSEADDR failed");
close(sock);
return (-1);
}
if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0)
{
perror("SO_REUSEPORT failed");
close(sock);
return (-1);
}
if (setsockopt(sock, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)) < 0)
{
perror("IP_RECVIF failed");
close(sock);
return (-1);
}
for (on = bufspace; ; on -= 1024)
{
if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
&on, sizeof (on)) == 0)
break;
if (on <= 8*1024)
{
perror("setsockopt");
syslog(LOG_ERR, "setsockopt SO_RCVBUF: %m");
break;
}
}
if (bind(sock, (struct sockaddr *)sin, sizeof (*sin)) < 0)
{
perror("bind");
syslog(LOG_ERR, "bind: %m");
close(sock);
return (-1);
}
if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1)
syslog(LOG_ERR, "fcntl O_NONBLOCK: %m\n");
return (sock);
}
int getsocket6(struct sockaddr_in6 *sin, int bufspace, char *int_name)
{
int sock, on = 1;
if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
{
perror("socket");
syslog(LOG_ERR, "socket: %m");
return (-1);
}
for (on = bufspace; ; on -= 1024)
{
if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
&on, sizeof (on)) == 0)
break;
if (on <= 8*1024)
{
perror("setsockopt");
syslog(LOG_ERR, "setsockopt SO_RCVBUF: %m");
break;
}
}
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
{
perror("SO_REUSEADDR failed");
close(sock);
return (-1);
}
if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0)
{
perror("SO_REUSEPORT failed");
return (-1);
}
#ifdef IPV6_RECVPKTINFO
if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)) < 0)
{
perror("IPV6_RECVPKTINFO failed");
return (-1);
}
#elif defined IPV6_PKTINFO
if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on)) < 0)
{
perror("IPV6_PKTINFO failed");
return (-1);
}
#endif
if (bind(sock, (struct sockaddr *)sin, sizeof (*sin)) < 0)
{
perror("bind");
syslog(LOG_ERR, "bind: %m");
close(sock);
return (-1);
}
if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1)
syslog(LOG_ERR, "fcntl O_NONBLOCK: %m\n");
return (sock);
}
int
join_mcast(struct interface *ifs, int sock)
{
/* See netinet6/in6.h */
struct ipv6_mreq mcastreq;
COPY_IP(&mcastreq.ipv6mr_multiaddr, &ifs->int6_multaddr.sin6_addr);
mcastreq.ipv6mr_interface = ifs->if_index;
OLSR_PRINTF(3, "Interface %s joining multicast %s...", ifs->int_name, olsr_ip_to_string((union olsr_ip_addr *)&ifs->int6_multaddr.sin6_addr));
/* rfc 3493 */
#ifdef IPV6_JOIN_GROUP
/* Join reciever group */
if(setsockopt(sock,
IPPROTO_IPV6,
IPV6_JOIN_GROUP,
(char *)&mcastreq,
sizeof(struct ipv6_mreq))
< 0)
#else /* rfc 2133, obsoleted */
/* Join receiver group */
if(setsockopt(sock,
IPPROTO_IPV6,
IPV6_ADD_MEMBERSHIP,
(char *)&mcastreq,
sizeof(struct ipv6_mreq))
< 0)
#endif
{
perror("Join multicast send");
return -1;
}
if(setsockopt(sock,
IPPROTO_IPV6,
IPV6_MULTICAST_IF,
(char *)&mcastreq.ipv6mr_interface,
sizeof(mcastreq.ipv6mr_interface))
< 0)
{
perror("Set multicast if");
return -1;
}
OLSR_PRINTF(3, "OK\n");
return 0;
}
int get_ipv6_address(char *ifname, struct sockaddr_in6 *saddr6, int scope_in)
{
struct ifaddrs *ifap, *ifa;
const struct sockaddr_in6 *sin6 = NULL;
struct in6_ifreq ifr6;
int found = 0;
int s6;
u_int32_t flags6;
if (getifaddrs(&ifap) != 0)
{
OLSR_PRINTF(3, "get_ipv6_address: getifaddrs() failed.\n");
return 0;
}
for (ifa = ifap; ifa; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr->sa_family == AF_INET6 &&
strcmp(ifa->ifa_name, ifname) == 0)
{
sin6 = (const struct sockaddr_in6 *)ifa->ifa_addr;
if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
continue;
strncpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name));
if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
{
OLSR_PRINTF(3, "socket(AF_INET6,SOCK_DGRAM)");
break;
}
ifr6.ifr_addr = *sin6;
if (ioctl(s6, SIOCGIFAFLAG_IN6, &ifr6) < 0)
{
OLSR_PRINTF(3, "ioctl(SIOCGIFAFLAG_IN6)");
close(s6);
break;
}
close(s6);
flags6 = ifr6.ifr_ifru.ifru_flags6;
if ((flags6 & IN6_IFF_ANYCAST) != 0)
continue;
if (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))
{
if (scope_in)
{
memcpy(&saddr6->sin6_addr, &sin6->sin6_addr,
sizeof(struct in6_addr));
found = 1;
break;
}
}
else
{
if (scope_in == 0)
{
memcpy(&saddr6->sin6_addr, &sin6->sin6_addr,
sizeof(struct in6_addr));
found = 1;
break;
}
}
}
}
freeifaddrs(ifap);
if (found)
return 1;
return 0;
}
/**
* Wrapper for sendto(2)
*/
#ifdef SPOOF
static u_int16_t ip_id = 0;
#endif /* SPOOF */
ssize_t
olsr_sendto(int s,
const void *buf,
size_t len,
int flags,
const struct sockaddr *to,
socklen_t tolen)
{
#ifdef SPOOF
/* IPv4 for now! */
libnet_t *context;
char errbuf[LIBNET_ERRBUF_SIZE];
libnet_ptag_t udp_tag, ip_tag, ether_tag;
unsigned char enet_broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
int status;
struct sockaddr_in *to_in = (struct sockaddr_in *) to;
u_int32_t destip;
struct interface *iface;
udp_tag = ip_tag = ether_tag = 0;
destip = to_in->sin_addr.s_addr;
iface = if_ifwithsock (s);
/* initialize libnet */
context = libnet_init(LIBNET_LINK, iface->int_name, errbuf);
if (context == NULL)
{
OLSR_PRINTF (1, "libnet init: %s\n", libnet_geterror (context));
return (0);
}
/* initialize IP ID field if necessary */
if (ip_id == 0)
{
ip_id = (u_int16_t) (arc4random () & 0xffff);
}
udp_tag = libnet_build_udp (698, /* src port */
698, /* dest port */
LIBNET_UDP_H + len, /* length */
0, /* checksum */
buf, /* payload */
len, /* payload size */
context, /* context */
udp_tag); /* pblock */
if (udp_tag == -1)
{
OLSR_PRINTF (1, "libnet UDP header: %s\n", libnet_geterror (context));
return (0);
}
ip_tag = libnet_build_ipv4 (LIBNET_IPV4_H + LIBNET_UDP_H + len, /* len */
0, /* TOS */
ip_id++, /* IP id */
0, /* IP frag */
1, /* IP TTL */
IPPROTO_UDP, /* protocol */
0, /* checksum */
libnet_get_ipaddr4 (context), /* src IP */
destip, /* dest IP */
NULL, /* payload */
0, /* payload len */
context, /* context */
ip_tag); /* pblock */
if (ip_tag == -1)
{
OLSR_PRINTF (1, "libnet IP header: %s\n", libnet_geterror (context));
return (0);
}
ether_tag = libnet_build_ethernet (enet_broadcast, /* ethernet dest */
libnet_get_hwaddr (context), /* ethernet source */
ETHERTYPE_IP, /* protocol type */
NULL, /* payload */
0, /* payload size */
context, /* libnet handle */
ether_tag); /* pblock tag */
if (ether_tag == -1)
{
OLSR_PRINTF (1, "libnet ethernet header: %s\n", libnet_geterror (context));
return (0);
}
status = libnet_write (context);
if (status == -1)
{
OLSR_PRINTF (1, "libnet packet write: %s\n", libnet_geterror (context));
return (0);
}
libnet_destroy (context);
return (len);
#else
return sendto(s, buf, len, flags, to, tolen);
#endif
}
/**
* Wrapper for recvfrom(2)
*/
ssize_t
olsr_recvfrom(int s,
void *buf,
size_t len,
int flags,
struct sockaddr *from,
socklen_t *fromlen)
{
struct msghdr mhdr;
struct iovec iov;
union {
struct cmsghdr cmsg;
unsigned char chdr[4096];
} cmu;
struct cmsghdr *cm;
struct sockaddr_dl *sdl;
struct sockaddr_in *sin = (struct sockaddr_in *) from; //XXX
struct sockaddr_in6 *sin6;
struct in6_addr *iaddr6;
struct in6_pktinfo *pkti;
struct interface *ifc;
char addrstr[INET6_ADDRSTRLEN];
char iname[IFNAMSIZ];
int count;
memset(&mhdr, 0, sizeof(mhdr));
memset(&iov, 0, sizeof(iov));
mhdr.msg_name = (caddr_t) from;
mhdr.msg_namelen = *fromlen;
mhdr.msg_iov = &iov;
mhdr.msg_iovlen = 1;
mhdr.msg_control = (caddr_t) &cmu;
mhdr.msg_controllen = sizeof (cmu);
iov.iov_len = len;
iov.iov_base = buf;
count = recvmsg (s, &mhdr, MSG_DONTWAIT);
if (count <= 0)
{
return (count);
}
/* this needs to get communicated back to caller */
*fromlen = mhdr.msg_namelen;
if (olsr_cnf->ip_version == AF_INET6)
{
for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&mhdr); cm;
cm = (struct cmsghdr *)CMSG_NXTHDR(&mhdr, cm))
{
if (cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_PKTINFO)
{
pkti = (struct in6_pktinfo *) CMSG_DATA(cm);
iaddr6 = &pkti->ipi6_addr;
if_indextoname(pkti->ipi6_ifindex, iname);
}
}
}
else
{
cm = &cmu.cmsg;
sdl = (struct sockaddr_dl *) CMSG_DATA (cm);
memset (iname, 0, sizeof (iname));
memcpy (iname, sdl->sdl_data, sdl->sdl_nlen);
}
ifc = if_ifwithsock (s);
sin6 = (struct sockaddr_in6 *)from;
OLSR_PRINTF (4, "%d bytes from %s, socket associated %s really received on %s\n",
count,
(olsr_cnf->ip_version == AF_INET6) ?
inet_ntop(AF_INET6, (char *)&sin6->sin6_addr, addrstr,
INET6_ADDRSTRLEN):
inet_ntoa (sin->sin_addr),
ifc->int_name,
iname);
if (strcmp (ifc->int_name, iname) != 0)
{
return (0);
}
return (count);
}
/**
* Wrapper for select(2)
*/
int
olsr_select(int nfds,
fd_set *readfds,
fd_set *writefds,
fd_set *exceptfds,
struct timeval *timeout)
{
return select(nfds,
readfds,
writefds,
exceptfds,
timeout);
}
int
check_wireless_interface(char *ifname)
{
#if defined __FreeBSD__ && !defined FBSD_NO_80211
/* From FreeBSD ifconfig/ifieee80211.c ieee80211_status() */
struct ieee80211req ireq;
u_int8_t data[32];
memset(&ireq, 0, sizeof(ireq));
strlcpy(ireq.i_name, ifname, sizeof(ireq.i_name));
ireq.i_data = &data;
ireq.i_type = IEEE80211_IOC_SSID;
ireq.i_val = -1;
return (ioctl(olsr_cnf->ioctl_s, SIOCG80211, &ireq) >= 0) ? 1 : 0;
#else
return 0;
#endif
}
#include <sys/sockio.h>
int
calculate_if_metric(char *ifname)
{
if(check_wireless_interface(ifname))
{
/* Wireless */
return 1;
}
else
{
/* Ethernet */
#if 0
/* Andreas: Perhaps SIOCGIFMEDIA is the way to do this? */
struct ifmediareq ifm;
memset(&ifm, 0, sizeof(ifm));
strlcpy(ifm.ifm_name, ifname, sizeof(ifm.ifm_name));
if(ioctl(olsr_cnf->ioctl_s, SIOCGIFMEDIA, &ifm) < 0)
{
OLSR_PRINTF(1, "Error SIOCGIFMEDIA(%s)\n", ifm.ifm_name);
return WEIGHT_ETHERNET_DEFAULT;
}
OLSR_PRINTF(1, "%s: STATUS 0x%08x\n", ifm.ifm_name, ifm.ifm_status);
#endif
return WEIGHT_ETHERNET_DEFAULT;
}
}
syntax highlighted by Code2HTML, v. 0.9.1