⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ctosl-linux.c

📁 Vista 核心Rally技术之-LLTD 实现源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/*
 * 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 + -