?? ctosl-linux.c
字號:
/*
* LICENSE NOTICE.
*
* Use of the Microsoft Windows Rally Development Kit is covered under
* the Microsoft Windows Rally Development Kit License Agreement,
* which is provided within the Microsoft Windows Rally Development
* Kit or at http://www.microsoft.com/whdc/rally/rallykit.mspx. If you
* want a license from Microsoft to use the software in the Microsoft
* Windows Rally Development Kit, you must (1) complete the designated
* "licensee" information in the Windows Rally Development Kit License
* Agreement, and (2) sign and return the Agreement AS IS to Microsoft
* at the address provided in the Agreement.
*/
/*
* Copyright (c) Microsoft Corporation 2005. All rights reserved.
* This software is provided with NO WARRANTY.
*/
#include <stdio.h>
#include <ctype.h>
#include <sys/socket.h>
#include <features.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
#include <net/if_arp.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <signal.h>
/* We use the POSIX.1e capability subsystem to drop all but
* CAP_NET_ADMIN rights */
//#define HAVE_CAPABILITIES
#ifdef HAVE_CAPABILITIES
# include <sys/capability.h>
#endif
/* Do you have wireless extensions available? (most modern kernels do) */
#define HAVE_WIRELESS
#ifdef HAVE_WIRELESS
/* for get access-point address (BSSID) and infrastructure mode */
# include <linux/wireless.h>
#else /* ! HAVE_WIRELESS */
/* still want struct ifreq and friends */
# include <net/if.h>
#endif /* ! HAVE_WIRELESS */
/* for uname: */
#include <sys/utsname.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include "globals.h"
#include "packetio.h"
/* helper functions */
/* Convert from name "interface" to its index, or die on error. */
static int
if_get_index(int sock, char *interface)
{
struct ifreq req;
int ret;
memset(&req, 0, sizeof(req));
strncpy(req.ifr_name, interface, sizeof(req.ifr_name));
ret = ioctl(sock, SIOCGIFINDEX, &req);
if (ret != 0)
die("if_get_index: for interface %s: %s\n",
interface, strerror(errno));
return req.ifr_ifindex;
}
osl_t *
osl_init(void)
{
osl_t *osl;
osl = xmalloc(sizeof(*osl));
osl->sock = -1;
osl->arpsock = -1;
osl->arp_enabled = FALSE;
return osl;
}
/* pidfile maintenance: this is not locking (there's plenty of scope
* for races here!) but it should stop most accidental runs of two
* lld2d instances on the same interface. We open the pidfile for
* read: if it exists and the named pid is running we abort ourselves.
* Otherwise we reopen the pidfile for write and log our pid to it. */
void
osl_write_pidfile(osl_t *osl)
{
char pidfile[80];
char pidbuf[16];
int fd;
int ret;
snprintf(pidfile, sizeof(pidfile), "/var/run/lld2d-ct-%.10s.pid", osl->interface);
fd = open(pidfile, O_RDONLY);
if (fd < 0)
{
if (errno != ENOENT)
die("osl_write_pidfile: opening pidfile %s for read: %s\n",
pidfile, strerror(errno));
/* ENOENT is good: the pidfile doesn't exist */
}
else
{
/* the pidfile exists: read it and check whether the named pid
is still around */
int pid;
char *end;
ret = read(fd, pidbuf, sizeof(pidbuf));
if (ret < 0)
die("osl_write_pidfile: read of pre-existing %s failed: %s\n",
pidfile, strerror(errno));
pid = strtol(pidbuf, &end, 10);
if (*end != '\0' && *end != '\n')
die("osl_write_pidfile: couldn't parse \"%s\" as a pid (from file %s); "
"aborting\n", pidbuf, pidfile);
ret = kill(pid, 0); /* try sending signal 0 to the pid to check it exists */
if (ret == 0)
die("osl_write_pidfile: %s contains pid %d which is still running; aborting\n",
pidfile, pid);
/* pid doesn't exist, looks like we can proceed */
close(fd);
}
/* re-open pidfile for write, possibly creating it */
fd = open(pidfile, O_WRONLY|O_CREAT|O_TRUNC, 0644);
if (fd < 0)
die("osl_write_pidfile: open %s for write/create: %s\n", pidfile, strerror(errno));
snprintf(pidbuf, sizeof(pidbuf), "%d\n", getpid());
ret = write(fd, pidbuf, strlen(pidbuf));
if (ret < 0)
die("osl_write_pidfile: writing my PID to lockfile %s: %s\n",
pidfile, strerror(errno));
close(fd);
}
/* Open "interface", and add packetio_recv_handler(state) as the IO
* event handler for its packets (or die on failure). If possible,
* the OSL should try to set the OS to filter packets so just frames
* with ethertype == topology protocol are received, but if not the
* packetio_recv_handler will filter appropriately, so providing more
* frames than necessary is safe. */
void
osl_interface_open(osl_t *osl, char *interface, void *state)
{
struct sockaddr_ll addr;
int ret;
osl->sock = socket(PF_PACKET, SOCK_RAW, TOPO_ETHERTYPE);
if (osl->sock < 0)
die("osl_interface_open: open %s failed: %s\n",
interface, strerror(errno));
osl->interface = interface;
/* perhaps check interface flags indicate it is up? */
/* set filter to only topology frames on this one interface */
memset(&addr, 0, sizeof(addr));
addr.sll_family = AF_PACKET;
addr.sll_protocol = TOPO_ETHERTYPE;
addr.sll_ifindex = if_get_index(osl->sock, interface);
DEBUG({printf("binding raw socket (index= %d, fd=%d) on %s to TOPO_ETHERTYPE\n", addr.sll_ifindex, osl->sock, osl->interface);})
ret = bind(osl->sock, (struct sockaddr*)&addr, sizeof(addr));
if (ret != 0)
die("osl_interface_open: binding to interface %s (index %d): %s\n",
osl->interface, addr.sll_ifindex, strerror(errno));
event_add_io(osl->sock, packetio_recv_handler, state);
/* create the ARP socket here too, while we still have enough rights */
osl->arpsock = socket(PF_PACKET, SOCK_RAW, TOPO_ARP_ETHERTYPE);
if (osl->arpsock < 0)
die("osl_interface_open: open ARP socket on %s failed: %s\n",
osl->interface, strerror(errno));
}
/* Permanently drop elevated privilleges. */
/* Actually, drop all but CAP_NET_ADMIN rights, so we can still enable
* and disable promiscuous mode, and listen to ARPs. */
void
osl_drop_privs(osl_t *osl)
{
#ifdef HAVE_CAPABILITIES
cap_t caps = cap_init();
cap_value_t netadmin[] = {CAP_NET_ADMIN};
if (!caps)
die("osl_drop_privs: cap_init failed: %s\n", strerror(errno));
if (cap_set_flag(caps, CAP_PERMITTED, 1, netadmin, CAP_SET) < 0)
die("osl_drop_privs: cap_set_flag (permitted) %s\n", strerror(errno));
if (cap_set_flag(caps, CAP_EFFECTIVE, 1, netadmin, CAP_SET) < 0)
die("osl_drop_privs: cap_set_flag (effective): %s\n", strerror(errno));
if (cap_set_proc(caps) < 0)
die("osl_drop_privs: cap_set_proc: %s\n", strerror(errno));
cap_free(caps);
#endif
}
/* Turn promiscuous mode on or off */
void
osl_set_promisc(osl_t *osl, bool_t promisc)
{
struct ifreq req;
int ret=0;
/* we query to get the previous state of the flags so as not to
* change any others by accident */
memset(&req, 0, sizeof(req));
strncpy(req.ifr_name, osl->interface, sizeof(req.ifr_name)-1);
ret = ioctl(osl->sock, SIOCGIFFLAGS, &req);
if (ret != 0)
die("osl_set_promisc: couldn't get interface flags for %s: %s\n",
osl->interface, strerror(errno));
/* now clear (and optionally set) the IFF_PROMISC bit */
req.ifr_flags &= ~IFF_PROMISC;
if (promisc)
req.ifr_flags |= IFF_PROMISC;
ret = ioctl(osl->sock, SIOCSIFFLAGS, &req);
if (ret != 0)
die("osl_set_promisc: couldn't set interface flags for %s: %s\n",
osl->interface, strerror(errno));
}
/* Return the Ethernet address for "interface", or FALSE on error. */
static bool_t
get_hwaddr(const char *interface, /*OUT*/ etheraddr_t *addr,
bool_t expect_ethernet)
{
int rqfd;
struct ifreq req;
int ret;
memset(&req, 0, sizeof(req));
strncpy(req.ifr_name, interface, sizeof(req.ifr_name));
rqfd = socket(AF_INET,SOCK_DGRAM,0);
if (rqfd<0)
{
warn("get_hwaddr: FAILED creating request socket for \'%s\' : %s\n",interface,strerror(errno));
return FALSE;
}
ret = ioctl(rqfd, SIOCGIFHWADDR, &req);
if (ret < 0)
{
warn("get_hwaddr(%d,%s): FAILED : %s\n", rqfd, interface, strerror(errno));
return FALSE;
}
close(rqfd);
if (req.ifr_hwaddr.sa_family != ARPHRD_ETHER)
{
if (expect_ethernet)
warn("get_hwaddr: was expecting addr type to be Ether, not type %d\n",
req.ifr_hwaddr.sa_family);
return FALSE;
}
memcpy(addr, req.ifr_hwaddr.sa_data, sizeof(*addr));
return TRUE;
}
/* Return the Ethernet address for socket "sock", or die. */
void
osl_get_hwaddr(osl_t *osl, /*OUT*/ etheraddr_t *addr)
{
if (!get_hwaddr(osl->interface, addr, TRUE))
die("osl_get_hw_addr: expected an ethernet address on our interface\n");
}
ssize_t
osl_read(int fd, void *buf, size_t count)
{
return read(fd, buf, count);
}
ssize_t
osl_write(osl_t *osl, const void *buf, size_t count)
{
return write(osl->sock, buf, count);
}
/* TRUE if x is less than y (lexographically) */
static bool_t
etheraddr_lt(const etheraddr_t *x, const etheraddr_t *y)
{
int i;
for (i=0; i<6; i++)
{
if (x->a[i] > y->a[i])
return FALSE;
else if (x->a[i] < y->a[i])
return TRUE;
}
return FALSE; /* they're equal */
}
/* Find hardware address of "interface" and if it's lower than
* "lowest", update lowest. */
static void
pick_lowest_address(const char *interface, void *state)
{
etheraddr_t *lowest = state;
etheraddr_t candidate;
/* query the interface's hardware address */
if (!get_hwaddr(interface, &candidate, FALSE))
return; /* failure; just ignore (maybe it's not an Ethernet NIC, eg loopback) */
if (etheraddr_lt(&candidate, lowest))
*lowest = candidate;
uint8_t *bytes = (uint8_t*)state;
}
typedef void (*foreach_interface_fn)(const char *interface, void *state);
/* enumerate all interfaces on this host via /proc/net/dev */
char buf[160];
static bool_t
foreach_interface(foreach_interface_fn fn, void *state)
{
char *p, *colon, *name;
FILE *netdev;
/* Note: When I dropped this on the WRT54GS version 4, the following fopen() hung the box... who knows why? */
/* the workaround involves keeping the descriptor open from before the first select in the event loop. */
#if CAN_FOPEN_IN_SELECT_LOOP
netdev = fopen("/proc/net/dev", "r");
#else
netdev = g_procnetdev;
#endif
if (!netdev)
{
warn("foreach_interface: open(\"/proc/net/dev\") for read failed: %s; "
"not able to determine hostId, so no support for multiple NICs.\n",
strerror(errno));
return FALSE;
}
/* skip first 2 lines (header) */
fgets(buf, sizeof(buf), netdev);
fgets(buf, sizeof(buf), netdev);
/* interface name is non-whitespace up to the colon (but a colon
* might be part of a virtual interface eg eth0:0 */
while (fgets(buf, sizeof(buf), netdev))
{
name = buf;
while (isspace(*name))
name++;
colon = strchr(name, ':');
if (!colon)
{
warn("foreach_interface: parse error reading /proc/net/dev: missing colon\n");
fclose(netdev);
return FALSE;
}
/* see if there's an optional ":DIGITS" virtual interface here */
p = colon+1;
while (isdigit(*p))
p++;
if (*p == ':')
*p = '\0'; /* virtual interface name */
else
*colon = '\0'; /* normal interface name */
fn(name, state);
}
if (ferror(netdev))
{
warn("foreach_interface: error during reading of /proc/net/dev\n");
fclose(netdev);
return FALSE;
}
#if CAN_FOPEN_IN_SELECT_LOOP
fclose(netdev);
#endif
return TRUE;
}
/* Recipe from section 1.7 of the Unix Programming FAQ */
/* http://www.erlenstar.demon.co.uk/unix/faq_toc.html */
void
osl_become_daemon(osl_t *osl)
{
/* 1) fork */
pid_t pid = fork();
if (pid == -1)
die("osl_become_daemon: fork to drop process group leadership failed: %s\n",
strerror(errno));
if (pid != 0)
exit(0); /* parent exits; child continues */
/* we are now guaranteed not to be a process group leader */
/* 2) setsid() */
pid = setsid();
if (pid == -1)
die("osl_become_daemon: setsid failed?!: %s\n", strerror(errno));
/* we are now the new group leader, and have successfully
* jettisonned our controlling terminal - hooray! */
/* 3) fork again */
pid = fork();
if (pid == -1)
die("osl_become_daemon: fork to lose group leadership (again) failed: %s\n",
strerror(errno));
if (pid != 0)
exit(0); /* parent exits; child continues */
/* we now have no controlling terminal, and are not a group leader
(and thus have no way of acquiring a controlling terminal). */
/* 4) chdir("/") */
if (chdir("/") != 0)
die("osl_become_daemon: chdir(\"/\") failed: %s\n", strerror(errno));
/* this allows admins to umount filesystems etc since we're not
* keeping them busy.*/
/* 5) umask(0) */
umask(0); /* if we create any files, we specify full mode bits */
/* 6) close fds */
/* 7) re-establish stdin/out/err to logfiles or /dev/console as needed */
}
/********************** T L V _ G E T _ F N s ******************************/
/* Return a 6-byte identifier which will be unique and consistent for
* this host, across all of its interfaces. Here we enumerate all the
* interfaces that are up and have assigned Ethernet addresses, and
* pick the lowest of them to represent this host. */
int
get_hostid(void *data)
{
/* TLVDEF( etheraddr_t, hostid, , 1, Access_unset ) */
etheraddr_t *hid = (etheraddr_t*) data;
*hid = Etheraddr_broadcast;
if (!foreach_interface(pick_lowest_address, hid))
{
if (TRACE(TRC_TLVINFO))
dbgprintf("get_hostid(): FAILED picking lowest address.\n");
return TLV_GET_FAILED;
}
if (ETHERADDR_EQUALS(hid, &Etheraddr_broadcast))
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -