From 5cb6996e4c6c501065e0f7bcf37dfc5d7559b4a7 Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Wed, 20 Oct 2010 13:44:51 +0200 Subject: Add new input plugin UNIXSOCK This input plugins creates a unix socket which can be used to log packets. Scripts or applications can connect to the socket (only one client allowed per socket) and send data in a Key-Length-Value format (including the payload). Signed-off-by: Pierre Chifflier --- input/packet/Makefile.am | 5 +- input/packet/ulogd_inppkt_UNIXSOCK.c | 826 +++++++++++++++++++++++++++++++++++ 2 files changed, 830 insertions(+), 1 deletion(-) create mode 100644 input/packet/ulogd_inppkt_UNIXSOCK.c (limited to 'input/packet') diff --git a/input/packet/Makefile.am b/input/packet/Makefile.am index e90e46e..566b817 100644 --- a/input/packet/Makefile.am +++ b/input/packet/Makefile.am @@ -3,7 +3,7 @@ AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include AM_CFLAGS=-fPIC -Wall LIBS= -pkglib_LTLIBRARIES = ulogd_inppkt_NFLOG.la ulogd_inppkt_ULOG.la +pkglib_LTLIBRARIES = ulogd_inppkt_NFLOG.la ulogd_inppkt_ULOG.la ulogd_inppkt_UNIXSOCK.la ulogd_inppkt_NFLOG_la_SOURCES = ulogd_inppkt_NFLOG.c ulogd_inppkt_NFLOG_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_LOG_LIBS) @@ -12,3 +12,6 @@ ulogd_inppkt_NFLOG_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_LOG_CFLAGS) ulogd_inppkt_ULOG_la_SOURCES = ulogd_inppkt_ULOG.c ulogd_inppkt_ULOG_la_LDFLAGS = -avoid-version -module ulogd_inppkt_ULOG_la_LIBADD = ../../libipulog/libipulog.la + +ulogd_inppkt_UNIXSOCK_la_SOURCES = ulogd_inppkt_UNIXSOCK.c +ulogd_inppkt_UNIXSOCK_la_LDFLAGS = -avoid-version -module diff --git a/input/packet/ulogd_inppkt_UNIXSOCK.c b/input/packet/ulogd_inppkt_UNIXSOCK.c new file mode 100644 index 0000000..368df93 --- /dev/null +++ b/input/packet/ulogd_inppkt_UNIXSOCK.c @@ -0,0 +1,826 @@ +/* + * UNIXSOCK input module for ulogd + * + * Copyright(C) 2008-2010 INL + * Written by Pierre Chifflier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2ยท + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Default size of the receive buffer for the unix socket + 0 means that ulogd will use getsockopt(SO_RCVBUF) to determine it + at runtime */ +#define UNIXSOCK_BUFSIZE_DEFAULT 0 + +#define UNIXSOCK_PERMS_DEFAULT 0600 + +#define UNIXSOCK_UNIXPATH_DEFAULT "/var/run/ulogd/ulogd2.sock" + +#define ULOGD_SOCKET_MARK 0x41c90fd4 + +struct unixsock_input { + char *path; + char *unixsock_buf; + unsigned int unixsock_perms; + unsigned int unixsock_buf_avail; + unsigned int unixsock_buf_size; + struct ulogd_fd unixsock_server_fd; + struct ulogd_fd unixsock_instance_fd; +}; + +enum nflog_keys { + UNIXSOCK_KEY_RAW_MAC = 0, + UNIXSOCK_KEY_RAW_PCKT, + UNIXSOCK_KEY_RAW_PCKTLEN, + UNIXSOCK_KEY_RAW_PCKTCOUNT, + UNIXSOCK_KEY_OOB_PREFIX, + UNIXSOCK_KEY_OOB_TIME_SEC, + UNIXSOCK_KEY_OOB_TIME_USEC, + UNIXSOCK_KEY_OOB_MARK, + UNIXSOCK_KEY_OOB_IN, + UNIXSOCK_KEY_OOB_OUT, + UNIXSOCK_KEY_OOB_HOOK, + UNIXSOCK_KEY_RAW_MAC_LEN, + UNIXSOCK_KEY_OOB_SEQ_LOCAL, + UNIXSOCK_KEY_OOB_SEQ_GLOBAL, + UNIXSOCK_KEY_OOB_FAMILY, + UNIXSOCK_KEY_OOB_PROTOCOL, + UNIXSOCK_KEY_OOB_UID, + UNIXSOCK_KEY_OOB_GID, + UNIXSOCK_KEY_RAW_LABEL, + UNIXSOCK_KEY_RAW_TYPE, + UNIXSOCK_KEY_RAW_MAC_SADDR, + UNIXSOCK_KEY_RAW_MAC_ADDRLEN, + UNIXSOCK_KEY_NUFW_USER_NAME, + UNIXSOCK_KEY_NUFW_USER_ID, + UNIXSOCK_KEY_NUFW_OS_NAME, + UNIXSOCK_KEY_NUFW_OS_REL, + UNIXSOCK_KEY_NUFW_OS_VERS, + UNIXSOCK_KEY_NUFW_APP_NAME, + /* Add new keys after this line */ +}; + +static struct ulogd_key output_keys[] = { + [UNIXSOCK_KEY_RAW_MAC] = { + .type = ULOGD_RET_RAW, + .flags = ULOGD_RETF_NONE, + .name = "raw.mac", + }, + [UNIXSOCK_KEY_RAW_MAC_SADDR] = { + .type = ULOGD_RET_RAW, + .flags = ULOGD_RETF_NONE, + .name = "raw.mac.saddr", + .ipfix = { + .vendor = IPFIX_VENDOR_IETF, + .field_id = IPFIX_sourceMacAddress, + }, + }, + [UNIXSOCK_KEY_RAW_PCKT] = { + .type = ULOGD_RET_RAW, + .flags = ULOGD_RETF_NONE, + .name = "raw.pkt", + .ipfix = { + .vendor = IPFIX_VENDOR_NETFILTER, + .field_id = IPFIX_NF_rawpacket, + }, + }, + [UNIXSOCK_KEY_RAW_PCKTLEN] = { + .type = ULOGD_RET_UINT32, + .flags = ULOGD_RETF_NONE, + .name = "raw.pktlen", + .ipfix = { + .vendor = IPFIX_VENDOR_NETFILTER, + .field_id = IPFIX_NF_rawpacket_length, + }, + }, + [UNIXSOCK_KEY_RAW_PCKTCOUNT] = { + .type = ULOGD_RET_UINT32, + .flags = ULOGD_RETF_NONE, + .name = "raw.pktcount", + .ipfix = { + .vendor = IPFIX_VENDOR_IETF, + .field_id = IPFIX_packetDeltaCount, + }, + }, + [UNIXSOCK_KEY_OOB_PREFIX] = { + .type = ULOGD_RET_STRING, + .flags = ULOGD_RETF_NONE, + .name = "oob.prefix", + .ipfix = { + .vendor = IPFIX_VENDOR_NETFILTER, + .field_id = IPFIX_NF_prefix, + }, + }, + [UNIXSOCK_KEY_OOB_TIME_SEC] = { + .type = ULOGD_RET_UINT32, + .flags = ULOGD_RETF_NONE, + .name = "oob.time.sec", + .ipfix = { + .vendor = IPFIX_VENDOR_IETF, + .field_id = IPFIX_flowStartSeconds, + }, + }, + [UNIXSOCK_KEY_OOB_TIME_USEC] = { + .type = ULOGD_RET_UINT32, + .flags = ULOGD_RETF_NONE, + .name = "oob.time.usec", + .ipfix = { + .vendor = IPFIX_VENDOR_IETF, + .field_id = IPFIX_flowStartMicroSeconds, + }, + }, + [UNIXSOCK_KEY_OOB_MARK] = { + .type = ULOGD_RET_UINT32, + .flags = ULOGD_RETF_NONE, + .name = "oob.mark", + .ipfix = { + .vendor = IPFIX_VENDOR_NETFILTER, + .field_id = IPFIX_NF_mark, + }, + }, + [UNIXSOCK_KEY_OOB_IN] = { + .type = ULOGD_RET_STRING, + .flags = ULOGD_RETF_NONE, + .name = "oob.in", + .ipfix = { + .vendor = IPFIX_VENDOR_IETF, + .field_id = IPFIX_ingressInterface, + }, + }, + [UNIXSOCK_KEY_OOB_OUT] = { + .type = ULOGD_RET_STRING, + .flags = ULOGD_RETF_NONE, + .name = "oob.out", + .ipfix = { + .vendor = IPFIX_VENDOR_IETF, + .field_id = IPFIX_egressInterface, + }, + }, + [UNIXSOCK_KEY_OOB_HOOK] = { + .type = ULOGD_RET_UINT8, + .flags = ULOGD_RETF_NONE, + .name = "oob.hook", + .ipfix = { + .vendor = IPFIX_VENDOR_NETFILTER, + .field_id = IPFIX_NF_hook, + }, + }, + [UNIXSOCK_KEY_RAW_MAC_LEN] = { + .type = ULOGD_RET_UINT16, + .flags = ULOGD_RETF_NONE, + .name = "raw.mac_len", + }, + [UNIXSOCK_KEY_RAW_MAC_ADDRLEN] = { + .type = ULOGD_RET_UINT16, + .flags = ULOGD_RETF_NONE, + .name = "raw.mac.addrlen", + }, + + [UNIXSOCK_KEY_OOB_SEQ_LOCAL] = { + .type = ULOGD_RET_UINT32, + .flags = ULOGD_RETF_NONE, + .name = "oob.seq.local", + .ipfix = { + .vendor = IPFIX_VENDOR_NETFILTER, + .field_id = IPFIX_NF_seq_local, + }, + }, + [UNIXSOCK_KEY_OOB_SEQ_GLOBAL] = { + .type = ULOGD_RET_UINT32, + .flags = ULOGD_RETF_NONE, + .name = "oob.seq.global", + .ipfix = { + .vendor = IPFIX_VENDOR_NETFILTER, + .field_id = IPFIX_NF_seq_global, + }, + }, + [UNIXSOCK_KEY_OOB_FAMILY] = { + .type = ULOGD_RET_UINT8, + .flags = ULOGD_RETF_NONE, + .name = "oob.family", + }, + [UNIXSOCK_KEY_OOB_PROTOCOL] = { + .type = ULOGD_RET_UINT16, + .flags = ULOGD_RETF_NONE, + .name = "oob.protocol", + }, + [UNIXSOCK_KEY_OOB_UID] = { + .type = ULOGD_RET_UINT32, + .flags = ULOGD_RETF_NONE, + .name = "oob.uid", + }, + [UNIXSOCK_KEY_OOB_GID] = { + .type = ULOGD_RET_UINT32, + .flags = ULOGD_RETF_NONE, + .name = "oob.gid", + }, + [UNIXSOCK_KEY_RAW_LABEL] = { + .type = ULOGD_RET_UINT8, + .flags = ULOGD_RETF_NONE, + .name = "raw.label", + }, + [UNIXSOCK_KEY_RAW_TYPE] = { + .type = ULOGD_RET_UINT16, + .flags = ULOGD_RETF_NONE, + .name = "raw.type", + }, + [UNIXSOCK_KEY_NUFW_USER_NAME] = { + .type = ULOGD_RET_STRING, + .flags = ULOGD_RETF_NONE, + .name = "nufw.user.name", + }, + [UNIXSOCK_KEY_NUFW_USER_ID] = { + .type = ULOGD_RET_UINT32, + .flags = ULOGD_RETF_NONE, + .name = "nufw.user.id", + }, + [UNIXSOCK_KEY_NUFW_OS_NAME] = { + .type = ULOGD_RET_STRING, + .flags = ULOGD_RETF_NONE, + .name = "nufw.os.name", + }, + [UNIXSOCK_KEY_NUFW_OS_REL] = { + .type = ULOGD_RET_STRING, + .flags = ULOGD_RETF_NONE, + .name = "nufw.os.rel", + }, + [UNIXSOCK_KEY_NUFW_OS_VERS] = { + .type = ULOGD_RET_STRING, + .flags = ULOGD_RETF_NONE, + .name = "nufw.os.vers", + }, + [UNIXSOCK_KEY_NUFW_APP_NAME] = { + .type = ULOGD_RET_STRING, + .flags = ULOGD_RETF_NONE, + .name = "nufw.app.name", + }, +}; + +static struct config_keyset libunixsock_kset = { + .num_ces = 5, + .ces = { + { + .key = "socket_path", + .type = CONFIG_TYPE_STRING, + .options = CONFIG_OPT_NONE, + .u.string = UNIXSOCK_UNIXPATH_DEFAULT, + }, + { + .key = "bufsize", + .type = CONFIG_TYPE_INT, + .options = CONFIG_OPT_NONE, + .u.value = UNIXSOCK_BUFSIZE_DEFAULT, + }, + { + .key = "perms", + .type = CONFIG_TYPE_INT, + .options = CONFIG_OPT_NONE, + .u.value = UNIXSOCK_PERMS_DEFAULT, + }, + { + .key = "owner", + .type = CONFIG_TYPE_STRING, + .options = CONFIG_OPT_NONE, + }, + { + .key = "group", + .type = CONFIG_TYPE_STRING, + .options = CONFIG_OPT_NONE, + }, + }, +}; + +enum { + UNIXSOCK_OPT_UNIXPATH = 0, + UNIXSOCK_OPT_BUFSIZE, + UNIXSOCK_OPT_PERM, + UNIXSOCK_OPT_OWNER, + UNIXSOCK_OPT_GROUP, +}; + +#define unixpath_ce(x) ((x)->ces[UNIXSOCK_OPT_UNIXPATH]) +#define bufsize_ce(x) ((x)->ces[UNIXSOCK_OPT_BUFSIZE]) +#define perms_ce(x) ((x)->ces[UNIXSOCK_OPT_PERM]) +#define owner_ce(x) ((x)->ces[UNIXSOCK_OPT_OWNER]) +#define group_ce(x) ((x)->ces[UNIXSOCK_OPT_GROUP]) + +enum ulogd2_option_type { + ULOGD2_OPT_UNUSED = 0, + ULOGD2_OPT_PREFIX, /* log prefix (string) */ + ULOGD2_OPT_OOB_IN, /* input device (string) */ + ULOGD2_OPT_OOB_OUT, /* output device (string) */ + ULOGD2_OPT_OOB_TIME_SEC, /* packet arrival time (u_int32_t) */ + + ULOGD2_OPT_USER=200, /* user name (string) */ + ULOGD2_OPT_USERID, /* user id (u_int32_t) */ + ULOGD2_OPT_OSNAME, /* OS name (string) */ + ULOGD2_OPT_OSREL, /* OS release (string) */ + ULOGD2_OPT_OSVERS, /* OS version (string) */ + ULOGD2_OPT_APPNAME, /* application name (string) */ + ULOGD2_OPT_STATE, /* connection state: 0 (drop), 1 (open), 2 (established), 3 (close), 4 (unknown) */ + + /* Add new options after this line */ +}; + +struct ulogd_unixsock_packet_t { + uint32_t marker; + uint16_t total_size; + uint32_t version:4, + reserved:28; + uint16_t payload_length; + struct iphdr payload; +} __attribute__((packed)); + +struct ulogd_unixsock_option_t { + uint32_t option_id; + uint32_t option_length; + char option_value[0]; +} __attribute__((packed)); + +#define USOCK_ALIGNTO 8 +#define USOCK_ALIGN(len) ( ((len)+USOCK_ALIGNTO-1) & ~(USOCK_ALIGNTO-1) ) + +static int handle_packet(struct ulogd_pluginstance *upi, struct ulogd_unixsock_packet_t *pkt, u_int16_t total_len) +{ + char *data = NULL; + struct iphdr *ip; + struct ulogd_key *ret = upi->output.keys; + u_int8_t oob_family; + u_int16_t payload_len; + u_int32_t option_number; + u_int32_t option_length; + char *buf; + struct ulogd_unixsock_option_t *option; + int new_offset; + char *options_start; + + ulogd_log(ULOGD_DEBUG, + "ulogd2: handling packet\n"); + + payload_len = ntohs(pkt->payload_length); + + ip = &pkt->payload; + if (ip->version == 4) + oob_family = AF_INET; + else if (ip->version == 6) + oob_family = AF_INET6; + else oob_family = 0; + + okey_set_u8(&ret[UNIXSOCK_KEY_OOB_FAMILY], oob_family); + okey_set_ptr(&ret[UNIXSOCK_KEY_RAW_PCKT], ip); + okey_set_u32(&ret[UNIXSOCK_KEY_RAW_PCKTLEN], payload_len); + + /* options */ + if (total_len > payload_len + sizeof(u_int16_t)) { + /* option starts at the next aligned address after the payload */ + new_offset = USOCK_ALIGN(payload_len); + options_start = (void*)ip + new_offset; + data = options_start; + total_len -= (options_start - (char*)pkt); + + while ( (data - options_start) < total_len) { + + option = (void*)data; + option_number = ntohl(option->option_id); + option_length = ntohl(option->option_length); + buf = option->option_value; + + /* next option is also aligned */ + new_offset = USOCK_ALIGN(option_length); + data += sizeof(option->option_id) + sizeof(option->option_length) + new_offset; + + ulogd_log(ULOGD_DEBUG, + "ulogd2: option %d (len %d) `%s'\n", + option_number, option_length, buf); + + switch(option_number) { + case ULOGD2_OPT_PREFIX: + okey_set_ptr(&ret[UNIXSOCK_KEY_OOB_PREFIX], buf); + break; + case ULOGD2_OPT_OOB_IN: + okey_set_ptr(&ret[UNIXSOCK_KEY_OOB_IN], buf); + break; + case ULOGD2_OPT_OOB_OUT: + okey_set_ptr(&ret[UNIXSOCK_KEY_OOB_OUT], buf); + break; + case ULOGD2_OPT_OOB_TIME_SEC: + okey_set_u32(&ret[UNIXSOCK_KEY_OOB_TIME_SEC], *(u_int32_t*)buf); + break; + case ULOGD2_OPT_USER: + okey_set_ptr(&ret[UNIXSOCK_KEY_NUFW_USER_NAME], buf); + break; + case ULOGD2_OPT_USERID: + okey_set_u32(&ret[UNIXSOCK_KEY_NUFW_USER_ID], *(u_int32_t*)buf); + break; + case ULOGD2_OPT_OSNAME: + okey_set_ptr(&ret[UNIXSOCK_KEY_NUFW_OS_NAME], buf); + break; + case ULOGD2_OPT_OSREL: + okey_set_ptr(&ret[UNIXSOCK_KEY_NUFW_OS_REL], buf); + break; + case ULOGD2_OPT_OSVERS: + okey_set_ptr(&ret[UNIXSOCK_KEY_NUFW_OS_VERS], buf); + break; + case ULOGD2_OPT_APPNAME: + okey_set_ptr(&ret[UNIXSOCK_KEY_NUFW_APP_NAME], buf); + break; + case ULOGD2_OPT_STATE: + okey_set_u8(&ret[UNIXSOCK_KEY_RAW_LABEL], *(u_int8_t*)buf); + break; + default: + ulogd_log(ULOGD_NOTICE, + "ulogd2: unknown option %d\n", + option_number); + break; + }; + } + } + + /* number of packets */ + okey_set_u32(&ret[UNIXSOCK_KEY_RAW_PCKTCOUNT], 1); + + ulogd_propagate_results(upi); + + return 0; +} + +static int _create_unix_socket(const char *unix_path) +{ + int ret = -1; + struct sockaddr_un server_sock; + int s; + struct stat st_dummy; + + s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s < 0) { + ulogd_log(ULOGD_ERROR, + "ulogd2: could not create unix socket\n"); + return -1; + } + + server_sock.sun_family = AF_UNIX; + strncpy(server_sock.sun_path, unix_path, sizeof(server_sock.sun_path)); + server_sock.sun_path[sizeof(server_sock.sun_path)-1] = '\0'; + + if (stat(unix_path, &st_dummy) == 0 && st_dummy.st_size > 0) { + ulogd_log(ULOGD_ERROR, + "ulogd2: unix socket \'%s\' already exists\n", + unix_path); + close(s); + return -1; + } + + ret = bind(s, (struct sockaddr *)&server_sock, sizeof(server_sock)); + if (ret < 0) { + ulogd_log(ULOGD_ERROR, + "ulogd2: could not bind to unix socket \'%s\'\n", + server_sock.sun_path); + close(s); + return -1; + } + + ret = listen(s, 10); + if (ret < 0) { + ulogd_log(ULOGD_ERROR, + "ulogd2: could not bind to unix socket \'%s\'\n", + server_sock.sun_path); + close(s); + return -1; + } + + return s; +} + +static int _unix_socket_set_permissions(struct ulogd_pluginstance *upi) +{ + const char *socket_path; + const char *owner = owner_ce(upi->config_kset).u.string; + const char *group = group_ce(upi->config_kset).u.string; + uid_t uid = (uid_t)-1; + gid_t gid = (gid_t)-1; + + socket_path = unixpath_ce(upi->config_kset).u.string; + + if (chmod(socket_path, perms_ce(upi->config_kset).u.value) < 0) { + ulogd_log(ULOGD_ERROR, "Could not set permissions on unix socket\n"); + return -1; + } + + if (owner && strlen(owner)>0) { + struct passwd *p = getpwnam(owner); + + if (p == NULL) { + ulogd_log(ULOGD_ERROR, "Invalid owner specified for unix socket (%s)\n", owner); + return -1; + } + + uid = p->pw_uid; + } + + if (group && strlen(group)>0) { + struct group *g = getgrnam(group); + + if (g == NULL) { + ulogd_log(ULOGD_ERROR, "Invalid group specified for unix socket (%s)\n", group); + return -1; + } + + gid = g->gr_gid; + } + + if (chown(socket_path, uid, gid) < 0) { + ulogd_log(ULOGD_ERROR, "Could not set owner/group of unix socket\n"); + return -1; + } + + return 0; +} + +/* warning: this code is NOT reentrant ! */ +static void _timer_unregister_cb(struct ulogd_timer *a, void *param) +{ + struct unixsock_input *ui = param; + + if (ui->unixsock_instance_fd.fd >= 0) { + ulogd_log(ULOGD_DEBUG, " removing client from list\n"); + ulogd_unregister_fd(&ui->unixsock_instance_fd); + close(ui->unixsock_instance_fd.fd); + ui->unixsock_instance_fd.fd = -1; + ui->unixsock_buf_avail = 0; + } +} + +static void _disconnect_client(struct unixsock_input *ui) +{ + struct ulogd_timer *t = malloc(sizeof(struct ulogd_timer)); + + /* we can't call ulogd_unregister_fd fd, it will segfault + * (unable to remove an entry while inside llist_for_each_entry) + * so we schedule removal for next loop + */ + ulogd_init_timer(t, ui, _timer_unregister_cb); + ulogd_add_timer(t, 0); +} + +/* callback called from ulogd core when fd is readable */ +static int unixsock_instance_read_cb(int fd, unsigned int what, void *param) +{ + struct ulogd_pluginstance *upi = param; + struct unixsock_input *ui = (struct unixsock_input*)upi->private; + int len; + u_int16_t needed_len; + u_int32_t packet_sig; + struct ulogd_unixsock_packet_t *unixsock_packet; + + char buf[4096]; + + if (!(what & ULOGD_FD_READ)) + return 0; + + len = read(fd, buf, sizeof(buf)); + if (len < 0) { + ulogd_log(ULOGD_NOTICE, " read returned %d, errno is %d (%s)\n", + len, errno, strerror(errno)); + exit(-1); + return len; + } + if (len == 0) { + _disconnect_client(ui); + ulogd_log(ULOGD_DEBUG, " client disconnected\n"); + return 0; + } + + if (ui->unixsock_buf_avail + len > ui->unixsock_buf_size) { + ulogd_log(ULOGD_NOTICE, + "We are losing events. Please consider using the clause " + "bufsize\n"); + return -1; + } + + memcpy(ui->unixsock_buf + ui->unixsock_buf_avail, buf, len); + ui->unixsock_buf_avail += len; + + while(1) { + unixsock_packet = (void*)ui->unixsock_buf; + packet_sig = ntohl(unixsock_packet->marker); + if (packet_sig != ULOGD_SOCKET_MARK) { + ulogd_log(ULOGD_ERROR, + "ulogd2: invalid packet marked received " + "(read %lx, expected %lx), closing socket.\n", + packet_sig, ULOGD_SOCKET_MARK); + _disconnect_client(ui); + return -1; + + } + + needed_len = ntohs(unixsock_packet->total_size); + + if (ui->unixsock_buf_avail >= needed_len + sizeof(u_int32_t)) { + ulogd_log(ULOGD_DEBUG, + " We have enough data (%d bytes required), handling packet\n", + needed_len); + + if (handle_packet(upi, unixsock_packet, needed_len) != 0) { + return -1; + } + /* consume data */ + ui->unixsock_buf_avail -= (sizeof(u_int32_t) + needed_len); + if (ui->unixsock_buf_avail > 0) { + /* we need to shift data .. */ + memmove(ui->unixsock_buf, + ui->unixsock_buf + (sizeof(u_int32_t) + needed_len) , + ui->unixsock_buf_avail); + } else { + /* input buffer is empty, do not loop */ + return 0; + } + + } else { + ulogd_log(ULOGD_DEBUG, " We have %d bytes, but need %d. Requesting more\n", + ui->unixsock_buf_avail, needed_len + sizeof(u_int32_t)); + return 0; + } + + /* handle_packet has shifted data in buffer */ + }; + + return 0; +} + +/* callback called from ulogd core when fd is readable */ +static int unixsock_server_read_cb(int fd, unsigned int what, void *param) +{ + struct ulogd_pluginstance *upi = param; + struct unixsock_input *ui = (struct unixsock_input*)upi->private; + socklen_t len; + int s; + struct sockaddr_storage saddr; + + if (!(what & ULOGD_FD_READ)) + return 0; + + ulogd_log(ULOGD_DEBUG, "New server connected on unixsock socket\n"); + + len = sizeof(saddr); + s = accept(fd, (struct sockaddr*)&saddr, &len); + if (s < 0) { + ulogd_log(ULOGD_NOTICE, + " error while accepting new unixsock client, errno is %d (%s)\n", + errno, strerror(errno)); + return len; + } + + if (ui->unixsock_instance_fd.fd >= 0) { + ulogd_log(ULOGD_NOTICE, "a client is already connecting, rejecting new connection"); + close(s); + return 0; + } + + ui->unixsock_instance_fd.fd = s; + ui->unixsock_instance_fd.cb = &unixsock_instance_read_cb; + ui->unixsock_instance_fd.data = upi; + ui->unixsock_instance_fd.when = ULOGD_FD_READ; + + if (ulogd_register_fd(&ui->unixsock_instance_fd) < 0) { + ulogd_log(ULOGD_ERROR, "unable to register client fd to ulogd\n"); + return -1; + } + + return 0; +} + +static int configure(struct ulogd_pluginstance *upi, + struct ulogd_pluginstance_stack *stack) +{ + ulogd_log(ULOGD_DEBUG, "parsing config file section `%s', " + "plugin `%s'\n", upi->id, upi->plugin->name); + + config_parse_file(upi->id, upi->config_kset); + return 0; +} + +static int start(struct ulogd_pluginstance *upi) +{ + struct unixsock_input *ui = (struct unixsock_input *) upi->private; + int fd; + + ulogd_log(ULOGD_DEBUG, "Starting plugin `%s'\n", + upi->plugin->name); + + ui->path = unixpath_ce(upi->config_kset).u.string; + + ulogd_log(ULOGD_DEBUG, "Creating Unix socket `%s'\n", + ui->path); + fd = _create_unix_socket(ui->path); + if (fd < 0) { + ulogd_log(ULOGD_ERROR, "Unable to create unix socket on `%s'\n", + ui->path); + return -1; + } + + if (_unix_socket_set_permissions(upi) < 0) { + return -1; + } + + ui->unixsock_buf_avail = 0; + ui->unixsock_buf_size = bufsize_ce(upi->config_kset).u.value; + + if (ui->unixsock_buf_size == 0) { + int fd_bufsize = 0; + socklen_t optlen = sizeof(fd_bufsize); + + if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &fd_bufsize, &optlen) < 0) { + ulogd_log(ULOGD_ERROR, + "Could not determine socket buffer size. You have to use the clause " + "bufsize\n"); + return -1; + } + ulogd_log(ULOGD_DEBUG, "bufsize is %d\n", fd_bufsize); + + ui->unixsock_buf_size = fd_bufsize; + } + ui->unixsock_buf = malloc(ui->unixsock_buf_size); + + ui->unixsock_server_fd.fd = fd; + ui->unixsock_server_fd.cb = &unixsock_server_read_cb; + ui->unixsock_server_fd.data = upi; + ui->unixsock_server_fd.when = ULOGD_FD_READ; + + ui->unixsock_instance_fd.fd = -1; + ui->unixsock_instance_fd.cb = &unixsock_instance_read_cb; + ui->unixsock_instance_fd.data = upi; + ui->unixsock_instance_fd.when = ULOGD_FD_READ; + + if (ulogd_register_fd(&ui->unixsock_server_fd) < 0) { + ulogd_log(ULOGD_ERROR, "Unable to register fd to ulogd\n"); + return -1; + } + + return 0; +} + +static int stop(struct ulogd_pluginstance *upi) +{ + struct unixsock_input *ui = (struct unixsock_input *) upi->private; + char *unix_path = unixpath_ce(upi->config_kset).u.string; + + ulogd_log(ULOGD_DEBUG, "Stopping plugin `%s'\n", + upi->plugin->name); + + if (unix_path) + unlink(unix_path); + + free(ui->unixsock_buf); + + return 0; +} + +struct ulogd_plugin libunixsock_plugin = { + .name = "UNIXSOCK", + .input = { + .type = ULOGD_DTYPE_SOURCE, + }, + .output = { + .type = ULOGD_DTYPE_RAW, + .keys = output_keys, + .num_keys = ARRAY_SIZE(output_keys), + }, + .priv_size = sizeof(struct unixsock_input), + .configure = &configure, + .start = &start, + .stop = &stop, + .config_kset = &libunixsock_kset, + .version = ULOGD_VERSION, +}; + +static void __attribute__ ((constructor)) init(void) +{ + ulogd_register_plugin(&libunixsock_plugin); +} -- cgit v1.2.3