|
Packit |
c22fc9 |
/*
|
|
Packit |
c22fc9 |
* Soft: Keepalived is a failover program for the LVS project
|
|
Packit |
c22fc9 |
* <www.linuxvirtualserver.org>. It monitor & manipulate
|
|
Packit |
c22fc9 |
* a loadbalanced server pool using multi-layer checks.
|
|
Packit |
c22fc9 |
*
|
|
Packit |
c22fc9 |
* Part: Layer4 checkers handling. Register worker threads &
|
|
Packit |
c22fc9 |
* upper layer checkers.
|
|
Packit |
c22fc9 |
*
|
|
Packit |
c22fc9 |
* Author: Alexandre Cassen, <acassen@linux-vs.org>
|
|
Packit |
c22fc9 |
*
|
|
Packit |
c22fc9 |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
c22fc9 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
c22fc9 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
Packit |
c22fc9 |
* See the GNU General Public License for more details.
|
|
Packit |
c22fc9 |
*
|
|
Packit |
c22fc9 |
* This program is free software; you can redistribute it and/or
|
|
Packit |
c22fc9 |
* modify it under the terms of the GNU General Public License
|
|
Packit |
c22fc9 |
* as published by the Free Software Foundation; either version
|
|
Packit |
c22fc9 |
* 2 of the License, or (at your option) any later version.
|
|
Packit |
c22fc9 |
*
|
|
Packit |
c22fc9 |
* Copyright (C) 2001-2017 Alexandre Cassen, <acassen@gmail.com>
|
|
Packit |
c22fc9 |
*/
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#include "config.h"
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#include <errno.h>
|
|
Packit |
c22fc9 |
#include <unistd.h>
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#include "layer4.h"
|
|
Packit |
c22fc9 |
#include "logger.h"
|
|
Packit |
c22fc9 |
#include "scheduler.h"
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifndef _WITH_LVS_
|
|
Packit |
c22fc9 |
static
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
enum connect_result
|
|
Packit |
c22fc9 |
socket_bind_connect(int fd, conn_opts_t *co)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
int opt;
|
|
Packit |
c22fc9 |
socklen_t optlen;
|
|
Packit |
c22fc9 |
struct linger li;
|
|
Packit |
c22fc9 |
socklen_t addrlen;
|
|
Packit |
c22fc9 |
int ret;
|
|
Packit |
c22fc9 |
struct sockaddr_storage *addr = &co->dst;
|
|
Packit |
c22fc9 |
struct sockaddr_storage *bind_addr = &co->bindto;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
optlen = sizeof(opt);
|
|
Packit |
c22fc9 |
if (getsockopt(fd, SOL_SOCKET, SO_TYPE, (void *) &opt, &optlen) < 0) {
|
|
Packit |
c22fc9 |
log_message(LOG_ERR, "Can't get socket type: %s", strerror(errno));
|
|
Packit |
c22fc9 |
return connect_error;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
if (opt == SOCK_STREAM) {
|
|
Packit |
c22fc9 |
/* free the tcp port after closing the socket descriptor */
|
|
Packit |
c22fc9 |
li.l_onoff = 1;
|
|
Packit |
c22fc9 |
li.l_linger = 0;
|
|
Packit |
c22fc9 |
setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (struct linger));
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef _WITH_SO_MARK_
|
|
Packit |
c22fc9 |
if (co->fwmark) {
|
|
Packit |
c22fc9 |
if (setsockopt (fd, SOL_SOCKET, SO_MARK, &co->fwmark, sizeof (co->fwmark)) < 0) {
|
|
Packit |
c22fc9 |
log_message(LOG_ERR, "Error setting fwmark %d to socket: %s", co->fwmark, strerror(errno));
|
|
Packit |
c22fc9 |
return connect_error;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (co->bind_if[0]) {
|
|
Packit |
c22fc9 |
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, co->bind_if, (unsigned)strlen(co->bind_if) + 1) < 0) {
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "Checker can't bind to device %s: %s", co->bind_if, strerror(errno));
|
|
Packit |
c22fc9 |
return connect_error;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Bind socket */
|
|
Packit |
c22fc9 |
if (((struct sockaddr *) bind_addr)->sa_family != AF_UNSPEC) {
|
|
Packit |
c22fc9 |
addrlen = sizeof(*bind_addr);
|
|
Packit |
c22fc9 |
if (bind(fd, (struct sockaddr *) bind_addr, addrlen) != 0) {
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "Checker bind failed: %s", strerror(errno));
|
|
Packit |
c22fc9 |
return connect_error;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Set remote IP and connect */
|
|
Packit |
c22fc9 |
addrlen = sizeof(*addr);
|
|
Packit |
c22fc9 |
ret = connect(fd, (struct sockaddr *) addr, addrlen);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Immediate success */
|
|
Packit |
c22fc9 |
if (ret == 0)
|
|
Packit |
c22fc9 |
return connect_success;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* If connect is in progress then return 1 else it's real error. */
|
|
Packit |
c22fc9 |
if (ret < 0) {
|
|
Packit |
c22fc9 |
if (errno != EINPROGRESS)
|
|
Packit |
c22fc9 |
return connect_error;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return connect_in_progress;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
enum connect_result
|
|
Packit |
c22fc9 |
socket_connect(int fd, struct sockaddr_storage *addr)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
conn_opts_t co;
|
|
Packit |
c22fc9 |
memset(&co, 0, sizeof(co));
|
|
Packit |
c22fc9 |
co.dst = *addr;
|
|
Packit |
c22fc9 |
return socket_bind_connect(fd, &co);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
enum connect_result
|
|
Packit |
c22fc9 |
socket_state(thread_t * thread, int (*func) (thread_t *))
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
int status;
|
|
Packit |
c22fc9 |
socklen_t addrlen;
|
|
Packit |
c22fc9 |
int ret = 0;
|
|
Packit |
c22fc9 |
timeval_t timer_min;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Handle connection timeout */
|
|
Packit |
c22fc9 |
if (thread->type == THREAD_WRITE_TIMEOUT) {
|
|
Packit |
c22fc9 |
thread_close_fd(thread);
|
|
Packit |
c22fc9 |
return connect_timeout;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Check file descriptor */
|
|
Packit |
c22fc9 |
addrlen = sizeof(status);
|
|
Packit |
c22fc9 |
if (getsockopt(thread->u.fd, SOL_SOCKET, SO_ERROR, (void *) &status, &addrlen) < 0)
|
|
Packit |
c22fc9 |
ret = errno;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Connection failed !!! */
|
|
Packit |
c22fc9 |
if (ret) {
|
|
Packit |
c22fc9 |
thread_close_fd(thread);
|
|
Packit |
c22fc9 |
return connect_error;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* If status = 0, TCP connection to remote host is established.
|
|
Packit |
c22fc9 |
* Otherwise register checker thread to handle connection in progress,
|
|
Packit |
c22fc9 |
* and other error code until connection is established.
|
|
Packit |
c22fc9 |
* Recompute the write timeout (or pending connection).
|
|
Packit |
c22fc9 |
*/
|
|
Packit |
c22fc9 |
if (status == 0)
|
|
Packit |
c22fc9 |
return connect_success;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (status == EINPROGRESS) {
|
|
Packit |
c22fc9 |
timer_min = timer_sub_now(thread->sands);
|
|
Packit |
c22fc9 |
thread_add_write(thread->master, func, THREAD_ARG(thread),
|
|
Packit |
c22fc9 |
thread->u.fd, -timer_long(timer_min));
|
|
Packit |
c22fc9 |
return connect_in_progress;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread_close_fd(thread);
|
|
Packit |
c22fc9 |
return connect_error;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef _WITH_LVS_
|
|
Packit |
c22fc9 |
bool
|
|
Packit |
c22fc9 |
socket_connection_state(int fd, enum connect_result status, thread_t * thread,
|
|
Packit |
c22fc9 |
int (*func) (thread_t *), unsigned long timeout)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
void *checker;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
checker = THREAD_ARG(thread);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (status == connect_success ||
|
|
Packit |
c22fc9 |
status == connect_in_progress) {
|
|
Packit |
c22fc9 |
thread_add_write(thread->master, func, checker, fd, timeout);
|
|
Packit |
c22fc9 |
return false;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return true;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|