diff -u ioemu.git/hw/xen_platform.c ioemu.git/hw/xen_platform.c --- ioemu.git/hw/xen_platform.c 2008-09-30 13:35:23.000000000 +0100 +++ ioemu.git/hw/xen_platform.c 2008-09-30 13:39:23.000000000 +0100 @@ -24,13 +24,18 @@ */ #include "hw.h" +#include "pc.h" #include "pci.h" #include "irq.h" +#include "exec-all.h" #include "qemu-xen.h" +#include #include -extern FILE *logfile; +static int throttling_disabled; +static char log_buffer[4096]; +static int log_buffer_off; #define PFFLAG_ROM_LOCK 1 /* Sets whether ROM memory area is RW or RO */ @@ -41,6 +46,88 @@ uint64_t vga_stolen_ram; } PCIXenPlatformState; +/* We throttle access to dom0 syslog, to avoid DOS attacks. This is + modelled as a token bucket, with one token for every byte of log. + The bucket size is 128KB (->1024 lines of 128 bytes each) and + refills at 256B/s. It starts full. The guest is blocked if no + tokens are available when it tries to generate a log message. */ +#define BUCKET_MAX_SIZE (128*1024) +#define BUCKET_FILL_RATE 256 + +static void throttle(unsigned count) +{ + static unsigned available; + static struct timespec last_refil; + static int started; + static int warned; + + struct timespec waiting_for, now; + double delay; + struct timespec ts; + + if (throttling_disabled) + return; + + if (!started) { + clock_gettime(CLOCK_MONOTONIC, &last_refil); + available = BUCKET_MAX_SIZE; + started = 1; + } + + if (count > BUCKET_MAX_SIZE) { + fprintf(logfile, "tried to get %d tokens, but bucket size is %d\n", + BUCKET_MAX_SIZE, count); + exit(1); + } + + if (available < count) { + /* The bucket is empty. Refil it */ + + /* When will it be full enough to handle this request? */ + delay = (double)(count - available) / BUCKET_FILL_RATE; + waiting_for = last_refil; + waiting_for.tv_sec += delay; + waiting_for.tv_nsec += (delay - (int)delay) * 1e9; + if (waiting_for.tv_nsec >= 1000000000) { + waiting_for.tv_nsec -= 1000000000; + waiting_for.tv_sec++; + } + + /* How long do we have to wait? (might be negative) */ + clock_gettime(CLOCK_MONOTONIC, &now); + ts.tv_sec = waiting_for.tv_sec - now.tv_sec; + ts.tv_nsec = waiting_for.tv_nsec - now.tv_nsec; + if (ts.tv_nsec < 0) { + ts.tv_sec--; + ts.tv_nsec += 1000000000; + } + + /* Wait for it. */ + if (ts.tv_sec > 0 || + (ts.tv_sec == 0 && ts.tv_nsec > 0)) { + if (!warned) { + fprintf(logfile, "throttling guest access to syslog"); + warned = 1; + } + while (nanosleep(&ts, &ts) < 0 && errno == EINTR) + ; + } + + /* Refil */ + clock_gettime(CLOCK_MONOTONIC, &now); + delay = (now.tv_sec - last_refil.tv_sec) + + (now.tv_nsec - last_refil.tv_nsec) * 1.0e-9; + available += BUCKET_FILL_RATE * delay; + if (available > BUCKET_MAX_SIZE) + available = BUCKET_MAX_SIZE; + last_refil = now; + } + + assert(available >= count); + + available -= count; +} + static uint32_t xen_platform_ioport_readb(void *opaque, uint32_t addr) { PCIXenPlatformState *s = opaque; @@ -220,10 +320,51 @@ return 0; } +static uint32_t platform_fixed_ioport_read2(void *opaque, uint32_t addr) +{ + switch (addr - 0x10) { + case 0: + return 0x49d2; /* Magic value so that you can identify the + interface. */ + default: + return 0xffff; + } +} + +static void platform_fixed_ioport_write1(void *opaque, uint32_t addr, uint32_t val) +{ + switch (addr - 0x10) { + case 2: + /* Send bytes to syslog */ + if (val == '\n' || log_buffer_off == sizeof(log_buffer) - 1) { + /* Flush buffer */ + log_buffer[log_buffer_off] = 0; + throttle(log_buffer_off); + fprintf(logfile, "%s\n", log_buffer); + log_buffer_off = 0; + break; + } + log_buffer[log_buffer_off++] = val; + break; + } +} + +static uint32_t platform_fixed_ioport_read1(void *opaque, uint32_t addr) +{ + switch (addr - 0x10) { + case 2: + /* Version number */ + return 0; + default: + return 0xff; + } +} + void pci_xen_platform_init(PCIBus *bus) { PCIXenPlatformState *d; struct pci_config_header *pch; + struct stat stbuf; printf("Register xen platform.\n"); d = (PCIXenPlatformState *)pci_register_device( @@ -257,2 +422,9 @@ printf("Done register platform.\n"); + register_ioport_write(0x10, 16, 1, platform_fixed_ioport_write1, NULL); + register_ioport_read(0x10, 16, 2, platform_fixed_ioport_read2, NULL); + register_ioport_read(0x10, 16, 1, platform_fixed_ioport_read1, NULL); + + if (stat("/etc/disable-guest-log-throttle", &stbuf) == 0) + throttling_disabled = 1; + }