|
Packit |
c22fc9 |
/*
|
|
Packit |
c22fc9 |
* libipvs: Library for manipulating IPVS through netlink or [gs]etsockopt
|
|
Packit |
c22fc9 |
*
|
|
Packit |
c22fc9 |
* This code is copied from the ipvsadm sources, with the unused
|
|
Packit |
c22fc9 |
* code removed. It is available at:
|
|
Packit |
c22fc9 |
* https://git.kernel.org/cgit/utils/kernel/ipvsadm/ipvsadm.git
|
|
Packit |
c22fc9 |
*
|
|
Packit |
c22fc9 |
* The upstream code should periodically be checked for updates,
|
|
Packit |
c22fc9 |
* which should then be applied to this code.
|
|
Packit |
c22fc9 |
*
|
|
Packit |
c22fc9 |
* Authors: Wensong Zhang <wensong@linuxvirtualserver.org>
|
|
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 |
|
|
Packit |
c22fc9 |
#include "config.h"
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#include <stdlib.h>
|
|
Packit |
c22fc9 |
#include <unistd.h>
|
|
Packit |
c22fc9 |
#include <errno.h>
|
|
Packit |
c22fc9 |
#include <sys/socket.h>
|
|
Packit |
c22fc9 |
#include <string.h>
|
|
Packit |
c22fc9 |
#include <stdint.h>
|
|
Packit |
c22fc9 |
#include <stdbool.h>
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef LIBIPVS_USE_NL
|
|
Packit |
c22fc9 |
#include <netlink/netlink.h>
|
|
Packit |
c22fc9 |
#include <netlink/genl/genl.h>
|
|
Packit |
c22fc9 |
#include <netlink/genl/ctrl.h>
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef _HAVE_LIBNL1_
|
|
Packit |
c22fc9 |
#define nl_sock nl_handle
|
|
Packit |
c22fc9 |
#ifndef _LIBNL_DYNAMIC_
|
|
Packit |
c22fc9 |
#define nl_socket_alloc nl_handle_alloc
|
|
Packit |
c22fc9 |
#define nl_socket_free nl_handle_destroy
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef _LIBNL_DYNAMIC_
|
|
Packit |
c22fc9 |
#include "libnl_link.h"
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#include "libipvs.h"
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#include "memory.h"
|
|
Packit |
c22fc9 |
#if !HAVE_DECL_SOCK_CLOEXEC
|
|
Packit |
c22fc9 |
#include "old_socket.h"
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
#include "logger.h"
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
typedef struct ipvs_servicedest_s {
|
|
Packit |
c22fc9 |
struct ip_vs_service_user svc;
|
|
Packit |
c22fc9 |
struct ip_vs_dest_user dest;
|
|
Packit |
c22fc9 |
} ipvs_servicedest_t;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static int sockfd = -1;
|
|
Packit |
c22fc9 |
static void* ipvs_func = NULL;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef LIBIPVS_USE_NL
|
|
Packit |
c22fc9 |
static struct nl_sock *sock = NULL;
|
|
Packit |
c22fc9 |
static int family;
|
|
Packit |
c22fc9 |
static bool try_nl = true;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Policy definitions */
|
|
Packit |
c22fc9 |
#ifdef _WITH_SNMP_CHECKER_
|
|
Packit |
c22fc9 |
static struct nla_policy ipvs_cmd_policy[IPVS_CMD_ATTR_MAX + 1] = {
|
|
Packit |
c22fc9 |
[IPVS_CMD_ATTR_SERVICE] = { .type = NLA_NESTED },
|
|
Packit |
c22fc9 |
[IPVS_CMD_ATTR_DEST] = { .type = NLA_NESTED },
|
|
Packit |
c22fc9 |
[IPVS_CMD_ATTR_DAEMON] = { .type = NLA_NESTED },
|
|
Packit |
c22fc9 |
[IPVS_CMD_ATTR_TIMEOUT_TCP] = { .type = NLA_U32 },
|
|
Packit |
c22fc9 |
[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN] = { .type = NLA_U32 },
|
|
Packit |
c22fc9 |
[IPVS_CMD_ATTR_TIMEOUT_UDP] = { .type = NLA_U32 },
|
|
Packit |
c22fc9 |
};
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static struct nla_policy ipvs_service_policy[IPVS_SVC_ATTR_MAX + 1] = {
|
|
Packit |
c22fc9 |
[IPVS_SVC_ATTR_AF] = { .type = NLA_U16 },
|
|
Packit |
c22fc9 |
[IPVS_SVC_ATTR_PROTOCOL] = { .type = NLA_U16 },
|
|
Packit |
c22fc9 |
[IPVS_SVC_ATTR_ADDR] = { .type = NLA_UNSPEC,
|
|
Packit |
c22fc9 |
.maxlen = sizeof(struct in6_addr) },
|
|
Packit |
c22fc9 |
[IPVS_SVC_ATTR_PORT] = { .type = NLA_U16 },
|
|
Packit |
c22fc9 |
[IPVS_SVC_ATTR_FWMARK] = { .type = NLA_U32 },
|
|
Packit |
c22fc9 |
[IPVS_SVC_ATTR_SCHED_NAME] = { .type = NLA_STRING,
|
|
Packit |
c22fc9 |
.maxlen = IP_VS_SCHEDNAME_MAXLEN },
|
|
Packit |
c22fc9 |
[IPVS_SVC_ATTR_FLAGS] = { .type = NLA_UNSPEC,
|
|
Packit |
c22fc9 |
.minlen = sizeof(struct ip_vs_flags),
|
|
Packit |
c22fc9 |
.maxlen = sizeof(struct ip_vs_flags) },
|
|
Packit |
c22fc9 |
[IPVS_SVC_ATTR_TIMEOUT] = { .type = NLA_U32 },
|
|
Packit |
c22fc9 |
[IPVS_SVC_ATTR_NETMASK] = { .type = NLA_U32 },
|
|
Packit |
c22fc9 |
[IPVS_SVC_ATTR_STATS] = { .type = NLA_NESTED },
|
|
Packit |
c22fc9 |
#ifdef _HAVE_PE_NAME_
|
|
Packit |
c22fc9 |
[IPVS_SVC_ATTR_PE_NAME] = { .type = NLA_STRING,
|
|
Packit |
c22fc9 |
.maxlen = IP_VS_PENAME_MAXLEN },
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
#ifdef _WITH_LVS_64BIT_STATS_
|
|
Packit |
c22fc9 |
[IPVS_SVC_ATTR_STATS64] = { .type = NLA_NESTED },
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
};
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static struct nla_policy ipvs_dest_policy[IPVS_DEST_ATTR_MAX + 1] = {
|
|
Packit |
c22fc9 |
[IPVS_DEST_ATTR_ADDR] = { .type = NLA_UNSPEC,
|
|
Packit |
c22fc9 |
.maxlen = sizeof(struct in6_addr) },
|
|
Packit |
c22fc9 |
[IPVS_DEST_ATTR_PORT] = { .type = NLA_U16 },
|
|
Packit |
c22fc9 |
[IPVS_DEST_ATTR_FWD_METHOD] = { .type = NLA_U32 },
|
|
Packit |
c22fc9 |
[IPVS_DEST_ATTR_WEIGHT] = { .type = NLA_U32 },
|
|
Packit |
c22fc9 |
[IPVS_DEST_ATTR_U_THRESH] = { .type = NLA_U32 },
|
|
Packit |
c22fc9 |
[IPVS_DEST_ATTR_L_THRESH] = { .type = NLA_U32 },
|
|
Packit |
c22fc9 |
[IPVS_DEST_ATTR_ACTIVE_CONNS] = { .type = NLA_U32 },
|
|
Packit |
c22fc9 |
[IPVS_DEST_ATTR_INACT_CONNS] = { .type = NLA_U32 },
|
|
Packit |
c22fc9 |
[IPVS_DEST_ATTR_PERSIST_CONNS] = { .type = NLA_U32 },
|
|
Packit |
c22fc9 |
[IPVS_DEST_ATTR_STATS] = { .type = NLA_NESTED },
|
|
Packit |
c22fc9 |
#if HAVE_DECL_IPVS_DEST_ATTR_ADDR_FAMILY
|
|
Packit |
c22fc9 |
[IPVS_DEST_ATTR_ADDR_FAMILY] = { .type = NLA_U16 },
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
#ifdef _WITH_LVS_64BIT_STATS_
|
|
Packit |
c22fc9 |
[IPVS_DEST_ATTR_STATS64] = {.type = NLA_NESTED },
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
};
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef _WITH_LVS_64BIT_STATS_
|
|
Packit |
c22fc9 |
static struct nla_policy ipvs_stats64_policy[IPVS_STATS_ATTR_MAX + 1] = {
|
|
Packit |
c22fc9 |
[IPVS_STATS_ATTR_CONNS] = { .type = NLA_U64 },
|
|
Packit |
c22fc9 |
[IPVS_STATS_ATTR_INPKTS] = { .type = NLA_U64 },
|
|
Packit |
c22fc9 |
[IPVS_STATS_ATTR_OUTPKTS] = { .type = NLA_U64 },
|
|
Packit |
c22fc9 |
[IPVS_STATS_ATTR_INBYTES] = { .type = NLA_U64 },
|
|
Packit |
c22fc9 |
[IPVS_STATS_ATTR_OUTBYTES] = { .type = NLA_U64 },
|
|
Packit |
c22fc9 |
[IPVS_STATS_ATTR_CPS] = { .type = NLA_U64 },
|
|
Packit |
c22fc9 |
[IPVS_STATS_ATTR_INPPS] = { .type = NLA_U64 },
|
|
Packit |
c22fc9 |
[IPVS_STATS_ATTR_OUTPPS] = { .type = NLA_U64 },
|
|
Packit |
c22fc9 |
[IPVS_STATS_ATTR_INBPS] = { .type = NLA_U64 },
|
|
Packit |
c22fc9 |
[IPVS_STATS_ATTR_OUTBPS] = { .type = NLA_U64 },
|
|
Packit |
c22fc9 |
};
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static struct nla_policy ipvs_stats_policy[IPVS_STATS_ATTR_MAX + 1] = {
|
|
Packit |
c22fc9 |
[IPVS_STATS_ATTR_CONNS] = { .type = NLA_U32 },
|
|
Packit |
c22fc9 |
[IPVS_STATS_ATTR_INPKTS] = { .type = NLA_U32 },
|
|
Packit |
c22fc9 |
[IPVS_STATS_ATTR_OUTPKTS] = { .type = NLA_U32 },
|
|
Packit |
c22fc9 |
[IPVS_STATS_ATTR_INBYTES] = { .type = NLA_U64 },
|
|
Packit |
c22fc9 |
[IPVS_STATS_ATTR_OUTBYTES] = { .type = NLA_U64 },
|
|
Packit |
c22fc9 |
[IPVS_STATS_ATTR_CPS] = { .type = NLA_U32 },
|
|
Packit |
c22fc9 |
[IPVS_STATS_ATTR_INPPS] = { .type = NLA_U32 },
|
|
Packit |
c22fc9 |
[IPVS_STATS_ATTR_OUTPPS] = { .type = NLA_U32 },
|
|
Packit |
c22fc9 |
[IPVS_STATS_ATTR_INBPS] = { .type = NLA_U32 },
|
|
Packit |
c22fc9 |
[IPVS_STATS_ATTR_OUTBPS] = { .type = NLA_U32 },
|
|
Packit |
c22fc9 |
};
|
|
Packit |
c22fc9 |
#endif /* _WITH_SNMP_CHECKER_ */
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static struct nla_policy ipvs_info_policy[IPVS_INFO_ATTR_MAX + 1] = {
|
|
Packit |
c22fc9 |
[IPVS_INFO_ATTR_VERSION] = { .type = NLA_U32 },
|
|
Packit |
c22fc9 |
[IPVS_INFO_ATTR_CONN_TAB_SIZE] = { .type = NLA_U32 },
|
|
Packit |
c22fc9 |
};
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#define CHECK_IPV4(s, ret) if (s->af && s->af != AF_INET) \
|
|
Packit |
c22fc9 |
{ errno = EAFNOSUPPORT; goto out_err; } \
|
|
Packit |
c22fc9 |
s->user.addr = s->nf_addr.ip; \
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef _HAVE_PE_NAME_
|
|
Packit |
c22fc9 |
#define CHECK_PE(s, ret) if (s->pe_name[0]) \
|
|
Packit |
c22fc9 |
{ errno = EAFNOSUPPORT; goto out_err; }
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#define CHECK_COMPAT_DEST(s, ret) CHECK_IPV4(s, ret)
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef _HAVE_PE_NAME_
|
|
Packit |
c22fc9 |
#define CHECK_COMPAT_SVC(s, ret) \
|
|
Packit |
c22fc9 |
CHECK_IPV4(s, ret); \
|
|
Packit |
c22fc9 |
CHECK_PE(s, ret);
|
|
Packit |
c22fc9 |
#else
|
|
Packit |
c22fc9 |
#define CHECK_COMPAT_SVC(s, ret) \
|
|
Packit |
c22fc9 |
CHECK_IPV4(s, ret);
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef LIBIPVS_USE_NL
|
|
Packit |
c22fc9 |
#ifndef NLA_PUT_S32
|
|
Packit |
c22fc9 |
#define NLA_PUT_S32(msg, attrtype, value) \
|
|
Packit |
c22fc9 |
NLA_PUT_TYPE(msg, int32_t, attrtype, value)
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static inline int32_t
|
|
Packit |
c22fc9 |
nla_get_s32(struct nlattr *attr)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
return (int32_t)nla_get_u32(attr);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifndef _HAVE_LIBNL1_
|
|
Packit |
c22fc9 |
static int nlerr2syserr(int err)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
switch (abs(err)) {
|
|
Packit |
c22fc9 |
case NLE_BAD_SOCK: return EBADF;
|
|
Packit |
c22fc9 |
case NLE_EXIST: return EEXIST;
|
|
Packit |
c22fc9 |
case NLE_NOADDR: return EADDRNOTAVAIL;
|
|
Packit |
c22fc9 |
case NLE_OBJ_NOTFOUND: return ENOENT;
|
|
Packit |
c22fc9 |
case NLE_INTR: return EINTR;
|
|
Packit |
c22fc9 |
case NLE_AGAIN: return EAGAIN;
|
|
Packit |
c22fc9 |
case NLE_INVAL: return EINVAL;
|
|
Packit |
c22fc9 |
case NLE_NOACCESS: return EACCES;
|
|
Packit |
c22fc9 |
case NLE_NOMEM: return ENOMEM;
|
|
Packit |
c22fc9 |
case NLE_AF_NOSUPPORT: return EAFNOSUPPORT;
|
|
Packit |
c22fc9 |
case NLE_PROTO_MISMATCH: return EPROTONOSUPPORT;
|
|
Packit |
c22fc9 |
case NLE_OPNOTSUPP: return EOPNOTSUPP;
|
|
Packit |
c22fc9 |
case NLE_PERM: return EPERM;
|
|
Packit |
c22fc9 |
case NLE_BUSY: return EBUSY;
|
|
Packit |
c22fc9 |
case NLE_RANGE: return ERANGE;
|
|
Packit |
c22fc9 |
case NLE_NODEV: return ENODEV;
|
|
Packit |
c22fc9 |
default: return err;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static struct nl_msg *ipvs_nl_message(uint8_t cmd, int flags)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
struct nl_msg *msg;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
msg = nlmsg_alloc();
|
|
Packit |
c22fc9 |
if (!msg)
|
|
Packit |
c22fc9 |
return NULL;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, flags,
|
|
Packit |
c22fc9 |
cmd, IPVS_GENL_VERSION);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return msg;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static int ipvs_nl_noop_cb(__attribute__((unused)) struct nl_msg *msg, __attribute__((unused)) void *arg)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
return NL_OK;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static int ipvs_nl_send_message(struct nl_msg *msg, nl_recvmsg_msg_cb_t func, void *arg)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
int err = EINVAL;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
sock = nl_socket_alloc();
|
|
Packit |
c22fc9 |
if (!sock) {
|
|
Packit |
c22fc9 |
if (msg)
|
|
Packit |
c22fc9 |
nlmsg_free(msg);
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (genl_connect(sock) < 0)
|
|
Packit |
c22fc9 |
goto fail_genl;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
family = genl_ctrl_resolve(sock, IPVS_GENL_NAME);
|
|
Packit |
c22fc9 |
if (family < 0)
|
|
Packit |
c22fc9 |
goto fail_genl;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* To test connections and set the family */
|
|
Packit |
c22fc9 |
if (msg == NULL) {
|
|
Packit |
c22fc9 |
nl_socket_free(sock);
|
|
Packit |
c22fc9 |
sock = NULL;
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, func, arg) != 0)
|
|
Packit |
c22fc9 |
goto fail_genl;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (nl_send_auto_complete(sock, msg) < 0)
|
|
Packit |
c22fc9 |
goto fail_genl;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if ((err = -nl_recvmsgs_default(sock)) > 0)
|
|
Packit |
c22fc9 |
goto fail_genl;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
nlmsg_free(msg);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
nl_socket_free(sock);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
fail_genl:
|
|
Packit |
c22fc9 |
nl_socket_free(sock);
|
|
Packit |
c22fc9 |
sock = NULL;
|
|
Packit |
c22fc9 |
nlmsg_free(msg);
|
|
Packit |
c22fc9 |
#ifdef _HAVE_LIBNL1_
|
|
Packit |
c22fc9 |
errno = err;
|
|
Packit |
c22fc9 |
#else
|
|
Packit |
c22fc9 |
errno = nlerr2syserr(err);
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef LIBIPVS_USE_NL
|
|
Packit |
c22fc9 |
static int ipvs_getinfo_parse_cb(struct nl_msg *msg, __attribute__((unused)) void *arg)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
struct nlmsghdr *nlh = nlmsg_hdr(msg);
|
|
Packit |
c22fc9 |
struct nlattr *attrs[IPVS_INFO_ATTR_MAX + 1];
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (genlmsg_parse(nlh, 0, attrs, IPVS_INFO_ATTR_MAX, ipvs_info_policy) != 0)
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!(attrs[IPVS_INFO_ATTR_VERSION] &&
|
|
Packit |
c22fc9 |
attrs[IPVS_INFO_ATTR_CONN_TAB_SIZE]))
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return NL_OK;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static int ipvs_getinfo(void)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
socklen_t len;
|
|
Packit |
c22fc9 |
struct ip_vs_getinfo ipvs_info;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
ipvs_func = ipvs_getinfo;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (try_nl) {
|
|
Packit |
c22fc9 |
struct nl_msg *msg;
|
|
Packit |
c22fc9 |
msg = ipvs_nl_message(IPVS_CMD_GET_INFO, 0);
|
|
Packit |
c22fc9 |
if (msg)
|
|
Packit |
c22fc9 |
return ipvs_nl_send_message(msg, ipvs_getinfo_parse_cb, NULL);
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
len = sizeof(ipvs_info);
|
|
Packit |
c22fc9 |
return getsockopt(sockfd, IPPROTO_IP, IP_VS_SO_GET_INFO,
|
|
Packit |
c22fc9 |
(char *)&ipvs_info, &len;;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
int ipvs_init(void)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
socklen_t len;
|
|
Packit |
c22fc9 |
struct ip_vs_getinfo ipvs_info;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
ipvs_func = ipvs_init;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef LIBIPVS_USE_NL
|
|
Packit |
c22fc9 |
#ifdef _LIBNL_DYNAMIC_
|
|
Packit |
c22fc9 |
try_nl = libnl_init();
|
|
Packit |
c22fc9 |
if (!try_nl)
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "Note: IPVS with IPv6 will not be supported");
|
|
Packit |
c22fc9 |
#else
|
|
Packit |
c22fc9 |
try_nl = true;
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (try_nl && ipvs_nl_send_message(NULL, NULL, NULL) == 0)
|
|
Packit |
c22fc9 |
return ipvs_getinfo();
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
try_nl = false;
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if ((sockfd = socket(AF_INET, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW)) == -1)
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#if !HAVE_DECL_SOCK_CLOEXEC
|
|
Packit |
c22fc9 |
if (set_sock_flags(sockfd, F_SETFD, FD_CLOEXEC)) {
|
|
Packit |
c22fc9 |
close(sockfd);
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
len = sizeof(ipvs_info);
|
|
Packit |
c22fc9 |
if (getsockopt(sockfd, IPPROTO_IP, IP_VS_SO_GET_INFO, (char *)&ipvs_info, &len)) {
|
|
Packit |
c22fc9 |
close(sockfd);
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
int ipvs_flush(void)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
#ifdef LIBIPVS_USE_NL
|
|
Packit |
c22fc9 |
if (try_nl) {
|
|
Packit |
c22fc9 |
struct nl_msg *msg = ipvs_nl_message(IPVS_CMD_FLUSH, 0);
|
|
Packit |
c22fc9 |
if (msg && (ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL) == 0))
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_FLUSH, NULL, 0);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef LIBIPVS_USE_NL
|
|
Packit |
c22fc9 |
static int ipvs_nl_fill_service_attr(struct nl_msg *msg, ipvs_service_t *svc)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
struct nlattr *nl_service;
|
|
Packit |
c22fc9 |
struct ip_vs_flags flags = { .flags = svc->user.flags,
|
|
Packit |
c22fc9 |
.mask = ~0U };
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
nl_service = nla_nest_start(msg, IPVS_CMD_ATTR_SERVICE);
|
|
Packit |
c22fc9 |
if (!nl_service)
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
NLA_PUT_U16(msg, IPVS_SVC_ATTR_AF, svc->af);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (svc->user.fwmark) {
|
|
Packit |
c22fc9 |
NLA_PUT_U32(msg, IPVS_SVC_ATTR_FWMARK, svc->user.fwmark);
|
|
Packit |
c22fc9 |
} else {
|
|
Packit |
c22fc9 |
NLA_PUT_U16(msg, IPVS_SVC_ATTR_PROTOCOL, svc->user.protocol);
|
|
Packit |
c22fc9 |
NLA_PUT(msg, IPVS_SVC_ATTR_ADDR, sizeof(svc->nf_addr), &(svc->nf_addr));
|
|
Packit |
c22fc9 |
NLA_PUT_U16(msg, IPVS_SVC_ATTR_PORT, svc->user.port);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
NLA_PUT_STRING(msg, IPVS_SVC_ATTR_SCHED_NAME, svc->user.sched_name);
|
|
Packit |
c22fc9 |
#ifdef _HAVE_PE_NAME_
|
|
Packit |
c22fc9 |
if (svc->pe_name[0])
|
|
Packit |
c22fc9 |
NLA_PUT_STRING(msg, IPVS_SVC_ATTR_PE_NAME, svc->pe_name);
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
NLA_PUT(msg, IPVS_SVC_ATTR_FLAGS, sizeof(flags), &flags);
|
|
Packit |
c22fc9 |
NLA_PUT_U32(msg, IPVS_SVC_ATTR_TIMEOUT, svc->user.timeout);
|
|
Packit |
c22fc9 |
NLA_PUT_U32(msg, IPVS_SVC_ATTR_NETMASK, svc->user.netmask);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
nla_nest_end(msg, nl_service);
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
nla_put_failure:
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
int ipvs_add_service(ipvs_service_t *svc)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
ipvs_func = ipvs_add_service;
|
|
Packit |
c22fc9 |
#ifdef LIBIPVS_USE_NL
|
|
Packit |
c22fc9 |
if (try_nl) {
|
|
Packit |
c22fc9 |
struct nl_msg *msg = ipvs_nl_message(IPVS_CMD_NEW_SERVICE, 0);
|
|
Packit |
c22fc9 |
if (!msg) return -1;
|
|
Packit |
c22fc9 |
if (ipvs_nl_fill_service_attr(msg, svc)) {
|
|
Packit |
c22fc9 |
nlmsg_free(msg);
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
CHECK_COMPAT_SVC(svc, -1);
|
|
Packit |
c22fc9 |
return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_ADD, (char *)svc,
|
|
Packit |
c22fc9 |
sizeof(struct ip_vs_service_user));
|
|
Packit |
c22fc9 |
out_err:
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
int ipvs_update_service(ipvs_service_t *svc)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
ipvs_func = ipvs_update_service;
|
|
Packit |
c22fc9 |
#ifdef LIBIPVS_USE_NL
|
|
Packit |
c22fc9 |
if (try_nl) {
|
|
Packit |
c22fc9 |
struct nl_msg *msg = ipvs_nl_message(IPVS_CMD_SET_SERVICE, 0);
|
|
Packit |
c22fc9 |
if (!msg) return -1;
|
|
Packit |
c22fc9 |
if (ipvs_nl_fill_service_attr(msg, svc)) {
|
|
Packit |
c22fc9 |
nlmsg_free(msg);
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
CHECK_COMPAT_SVC(svc, -1);
|
|
Packit |
c22fc9 |
return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_EDIT, (char *)svc,
|
|
Packit |
c22fc9 |
sizeof(struct ip_vs_service_user));
|
|
Packit |
c22fc9 |
out_err:
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
int ipvs_del_service(ipvs_service_t *svc)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
ipvs_func = ipvs_del_service;
|
|
Packit |
c22fc9 |
#ifdef LIBIPVS_USE_NL
|
|
Packit |
c22fc9 |
if (try_nl) {
|
|
Packit |
c22fc9 |
struct nl_msg *msg = ipvs_nl_message(IPVS_CMD_DEL_SERVICE, 0);
|
|
Packit |
c22fc9 |
if (!msg) return -1;
|
|
Packit |
c22fc9 |
if (ipvs_nl_fill_service_attr(msg, svc)) {
|
|
Packit |
c22fc9 |
nlmsg_free(msg);
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
CHECK_COMPAT_SVC(svc, -1);
|
|
Packit |
c22fc9 |
return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_DEL, (char *)svc,
|
|
Packit |
c22fc9 |
sizeof(struct ip_vs_service_user));
|
|
Packit |
c22fc9 |
out_err:
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
int ipvs_zero_service(ipvs_service_t *svc)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
ipvs_func = ipvs_zero_service;
|
|
Packit |
c22fc9 |
#ifdef LIBIPVS_USE_NL
|
|
Packit |
c22fc9 |
if (try_nl) {
|
|
Packit |
c22fc9 |
struct nl_msg *msg = ipvs_nl_message(IPVS_CMD_ZERO, 0);
|
|
Packit |
c22fc9 |
if (!msg) return -1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (svc->user.fwmark
|
|
Packit |
c22fc9 |
|| memcmp(&in6addr_any, &svc->nf_addr.in6, sizeof(struct in6_addr))
|
|
Packit |
c22fc9 |
|| svc->user.port) {
|
|
Packit |
c22fc9 |
if (ipvs_nl_fill_service_attr(msg, svc)) {
|
|
Packit |
c22fc9 |
nlmsg_free(msg);
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
CHECK_COMPAT_SVC(svc, -1);
|
|
Packit |
c22fc9 |
return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_ZERO, (char *)svc,
|
|
Packit |
c22fc9 |
sizeof(struct ip_vs_service_user));
|
|
Packit |
c22fc9 |
out_err:
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef LIBIPVS_USE_NL
|
|
Packit |
c22fc9 |
static int ipvs_nl_fill_dest_attr(struct nl_msg *msg, ipvs_dest_t *dst)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
struct nlattr *nl_dest;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
nl_dest = nla_nest_start(msg, IPVS_CMD_ATTR_DEST);
|
|
Packit |
c22fc9 |
if (!nl_dest)
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#if HAVE_DECL_IPVS_DEST_ATTR_ADDR_FAMILY
|
|
Packit |
c22fc9 |
NLA_PUT_U16(msg, IPVS_DEST_ATTR_ADDR_FAMILY, dst->af);
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
NLA_PUT(msg, IPVS_DEST_ATTR_ADDR, sizeof(dst->nf_addr), &(dst->nf_addr));
|
|
Packit |
c22fc9 |
NLA_PUT_U16(msg, IPVS_DEST_ATTR_PORT, dst->user.port);
|
|
Packit |
c22fc9 |
NLA_PUT_U32(msg, IPVS_DEST_ATTR_FWD_METHOD, dst->user.conn_flags & IP_VS_CONN_F_FWD_MASK);
|
|
Packit |
c22fc9 |
NLA_PUT_U32(msg, IPVS_DEST_ATTR_WEIGHT, (uint32_t)dst->user.weight);
|
|
Packit |
c22fc9 |
NLA_PUT_U32(msg, IPVS_DEST_ATTR_U_THRESH, dst->user.u_threshold);
|
|
Packit |
c22fc9 |
NLA_PUT_U32(msg, IPVS_DEST_ATTR_L_THRESH, dst->user.l_threshold);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
nla_nest_end(msg, nl_dest);
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
nla_put_failure:
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
int ipvs_add_dest(ipvs_service_t *svc, ipvs_dest_t *dest)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
ipvs_servicedest_t svcdest;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
ipvs_func = ipvs_add_dest;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef LIBIPVS_USE_NL
|
|
Packit |
c22fc9 |
if (try_nl) {
|
|
Packit |
c22fc9 |
struct nl_msg *msg = ipvs_nl_message(IPVS_CMD_NEW_DEST, 0);
|
|
Packit |
c22fc9 |
if (!msg) return -1;
|
|
Packit |
c22fc9 |
if (ipvs_nl_fill_service_attr(msg, svc))
|
|
Packit |
c22fc9 |
goto nla_put_failure;
|
|
Packit |
c22fc9 |
if (ipvs_nl_fill_dest_attr(msg, dest))
|
|
Packit |
c22fc9 |
goto nla_put_failure;
|
|
Packit |
c22fc9 |
return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
nla_put_failure:
|
|
Packit |
c22fc9 |
nlmsg_free(msg);
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
CHECK_COMPAT_SVC(svc, -1);
|
|
Packit |
c22fc9 |
CHECK_COMPAT_DEST(dest, -1);
|
|
Packit |
c22fc9 |
memcpy(&svcdest.svc, svc, sizeof(svcdest.svc));
|
|
Packit |
c22fc9 |
memcpy(&svcdest.dest, dest, sizeof(svcdest.dest));
|
|
Packit |
c22fc9 |
return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_ADDDEST,
|
|
Packit |
c22fc9 |
(char *)&svcdest, sizeof(svcdest));
|
|
Packit |
c22fc9 |
out_err:
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
int ipvs_update_dest(ipvs_service_t *svc, ipvs_dest_t *dest)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
ipvs_servicedest_t svcdest;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
ipvs_func = ipvs_update_dest;
|
|
Packit |
c22fc9 |
#ifdef LIBIPVS_USE_NL
|
|
Packit |
c22fc9 |
if (try_nl) {
|
|
Packit |
c22fc9 |
struct nl_msg *msg = ipvs_nl_message(IPVS_CMD_SET_DEST, 0);
|
|
Packit |
c22fc9 |
if (!msg) return -1;
|
|
Packit |
c22fc9 |
if (ipvs_nl_fill_service_attr(msg, svc))
|
|
Packit |
c22fc9 |
goto nla_put_failure;
|
|
Packit |
c22fc9 |
if (ipvs_nl_fill_dest_attr(msg, dest))
|
|
Packit |
c22fc9 |
goto nla_put_failure;
|
|
Packit |
c22fc9 |
return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
nla_put_failure:
|
|
Packit |
c22fc9 |
nlmsg_free(msg);
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
CHECK_COMPAT_SVC(svc, -1);
|
|
Packit |
c22fc9 |
CHECK_COMPAT_DEST(dest, -1);
|
|
Packit |
c22fc9 |
memcpy(&svcdest.svc, svc, sizeof(svcdest.svc));
|
|
Packit |
c22fc9 |
memcpy(&svcdest.dest, dest, sizeof(svcdest.dest));
|
|
Packit |
c22fc9 |
return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_EDITDEST,
|
|
Packit |
c22fc9 |
(char *)&svcdest, sizeof(svcdest));
|
|
Packit |
c22fc9 |
out_err:
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
int ipvs_del_dest(ipvs_service_t *svc, ipvs_dest_t *dest)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
ipvs_servicedest_t svcdest;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
ipvs_func = ipvs_del_dest;
|
|
Packit |
c22fc9 |
#ifdef LIBIPVS_USE_NL
|
|
Packit |
c22fc9 |
if (try_nl) {
|
|
Packit |
c22fc9 |
struct nl_msg *msg = ipvs_nl_message(IPVS_CMD_DEL_DEST, 0);
|
|
Packit |
c22fc9 |
if (!msg) return -1;
|
|
Packit |
c22fc9 |
if (ipvs_nl_fill_service_attr(msg, svc))
|
|
Packit |
c22fc9 |
goto nla_put_failure;
|
|
Packit |
c22fc9 |
if (ipvs_nl_fill_dest_attr(msg, dest))
|
|
Packit |
c22fc9 |
goto nla_put_failure;
|
|
Packit |
c22fc9 |
return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
nla_put_failure:
|
|
Packit |
c22fc9 |
nlmsg_free(msg);
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
CHECK_COMPAT_SVC(svc, -1);
|
|
Packit |
c22fc9 |
CHECK_COMPAT_DEST(dest, -1);
|
|
Packit |
c22fc9 |
memcpy(&svcdest.svc, svc, sizeof(svcdest.svc));
|
|
Packit |
c22fc9 |
memcpy(&svcdest.dest, dest, sizeof(svcdest.dest));
|
|
Packit |
c22fc9 |
return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_DELDEST,
|
|
Packit |
c22fc9 |
(char *)&svcdest, sizeof(svcdest));
|
|
Packit |
c22fc9 |
out_err:
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
int ipvs_set_timeout(ipvs_timeout_t *to)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
ipvs_func = ipvs_set_timeout;
|
|
Packit |
c22fc9 |
#ifdef LIBIPVS_USE_NL
|
|
Packit |
c22fc9 |
if (try_nl) {
|
|
Packit |
c22fc9 |
struct nl_msg *msg = ipvs_nl_message(IPVS_CMD_SET_CONFIG, 0);
|
|
Packit |
c22fc9 |
if (!msg) return -1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (to->tcp_timeout)
|
|
Packit |
c22fc9 |
NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP, (uint32_t)to->tcp_timeout);
|
|
Packit |
c22fc9 |
if (to->tcp_fin_timeout)
|
|
Packit |
c22fc9 |
NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP_FIN, (uint32_t)to->tcp_fin_timeout);
|
|
Packit |
c22fc9 |
if (to->udp_timeout)
|
|
Packit |
c22fc9 |
NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_UDP, (uint32_t)to->udp_timeout);
|
|
Packit |
c22fc9 |
return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
nla_put_failure:
|
|
Packit |
c22fc9 |
nlmsg_free(msg);
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_TIMEOUT, (char *)to,
|
|
Packit |
c22fc9 |
sizeof(*to));
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
int ipvs_start_daemon(ipvs_daemon_t *dm)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
struct ip_vs_daemon_kern dmk;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
ipvs_func = ipvs_start_daemon;
|
|
Packit |
c22fc9 |
#ifdef LIBIPVS_USE_NL
|
|
Packit |
c22fc9 |
if (try_nl) {
|
|
Packit |
c22fc9 |
struct nlattr *nl_daemon;
|
|
Packit |
c22fc9 |
struct nl_msg *msg = ipvs_nl_message(IPVS_CMD_NEW_DAEMON, 0);
|
|
Packit |
c22fc9 |
if (!msg) return -1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
nl_daemon = nla_nest_start(msg, IPVS_CMD_ATTR_DAEMON);
|
|
Packit |
c22fc9 |
if (!nl_daemon)
|
|
Packit |
c22fc9 |
goto nla_put_failure;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
NLA_PUT_S32(msg, IPVS_DAEMON_ATTR_STATE, dm->state);
|
|
Packit |
c22fc9 |
NLA_PUT_STRING(msg, IPVS_DAEMON_ATTR_MCAST_IFN, dm->mcast_ifn);
|
|
Packit |
c22fc9 |
NLA_PUT_S32(msg, IPVS_DAEMON_ATTR_SYNC_ID, dm->syncid);
|
|
Packit |
c22fc9 |
#ifdef _HAVE_IPVS_SYNCD_ATTRIBUTES_
|
|
Packit |
c22fc9 |
if (dm->sync_maxlen)
|
|
Packit |
c22fc9 |
NLA_PUT_U16(msg, IPVS_DAEMON_ATTR_SYNC_MAXLEN, dm->sync_maxlen);
|
|
Packit |
c22fc9 |
if (dm->mcast_port)
|
|
Packit |
c22fc9 |
NLA_PUT_U16(msg, IPVS_DAEMON_ATTR_MCAST_PORT, dm->mcast_port);
|
|
Packit |
c22fc9 |
if (dm->mcast_ttl)
|
|
Packit |
c22fc9 |
NLA_PUT_U8(msg, IPVS_DAEMON_ATTR_MCAST_TTL, dm->mcast_ttl);
|
|
Packit |
c22fc9 |
if (dm->mcast_af == AF_INET6)
|
|
Packit |
c22fc9 |
NLA_PUT(msg, IPVS_DAEMON_ATTR_MCAST_GROUP6, sizeof(dm->mcast_group.in6), &dm->mcast_group.in6);
|
|
Packit |
c22fc9 |
else if (dm->mcast_af == AF_INET)
|
|
Packit |
c22fc9 |
NLA_PUT_U32(msg, IPVS_DAEMON_ATTR_MCAST_GROUP, dm->mcast_group.ip);
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
nla_nest_end(msg, nl_daemon);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
nla_put_failure:
|
|
Packit |
c22fc9 |
nlmsg_free(msg);
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
memset(&dmk, 0, sizeof(dmk));
|
|
Packit |
c22fc9 |
dmk.state = (int)dm->state;
|
|
Packit |
c22fc9 |
strcpy(dmk.mcast_ifn, dm->mcast_ifn);
|
|
Packit |
c22fc9 |
dmk.syncid = (int)dm->syncid;
|
|
Packit |
c22fc9 |
return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_STARTDAEMON,
|
|
Packit |
c22fc9 |
(char *)&dmk, sizeof(dmk));
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
int ipvs_stop_daemon(ipvs_daemon_t *dm)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
struct ip_vs_daemon_kern dmk;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
ipvs_func = ipvs_stop_daemon;
|
|
Packit |
c22fc9 |
#ifdef LIBIPVS_USE_NL
|
|
Packit |
c22fc9 |
if (try_nl) {
|
|
Packit |
c22fc9 |
struct nlattr *nl_daemon;
|
|
Packit |
c22fc9 |
struct nl_msg *msg = ipvs_nl_message(IPVS_CMD_DEL_DAEMON, 0);
|
|
Packit |
c22fc9 |
if (!msg) return -1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
nl_daemon = nla_nest_start(msg, IPVS_CMD_ATTR_DAEMON);
|
|
Packit |
c22fc9 |
if (!nl_daemon)
|
|
Packit |
c22fc9 |
goto nla_put_failure;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
NLA_PUT_S32(msg, IPVS_DAEMON_ATTR_STATE, dm->state);
|
|
Packit |
c22fc9 |
NLA_PUT_STRING(msg, IPVS_DAEMON_ATTR_MCAST_IFN, dm->mcast_ifn);
|
|
Packit |
c22fc9 |
NLA_PUT_S32(msg, IPVS_DAEMON_ATTR_SYNC_ID, dm->syncid);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
nla_nest_end(msg, nl_daemon);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
nla_put_failure:
|
|
Packit |
c22fc9 |
nlmsg_free(msg);
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
memset(&dmk, 0, sizeof(dmk));
|
|
Packit |
c22fc9 |
dmk.state = (int)dm->state;
|
|
Packit |
c22fc9 |
return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_STOPDAEMON,
|
|
Packit |
c22fc9 |
(char *)&dmk, sizeof(dmk));
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef _WITH_SNMP_CHECKER_
|
|
Packit |
c22fc9 |
#ifdef LIBIPVS_USE_NL
|
|
Packit |
c22fc9 |
#ifdef _WITH_LVS_64BIT_STATS_
|
|
Packit |
c22fc9 |
static int ipvs_parse_stats64(ip_vs_stats_t *stats, struct nlattr *nla)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
struct nlattr *attrs[IPVS_STATS_ATTR_MAX + 1];
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (nla_parse_nested(attrs, IPVS_STATS_ATTR_MAX, nla, ipvs_stats64_policy))
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!(attrs[IPVS_STATS_ATTR_CONNS] &&
|
|
Packit |
c22fc9 |
attrs[IPVS_STATS_ATTR_INPKTS] &&
|
|
Packit |
c22fc9 |
attrs[IPVS_STATS_ATTR_OUTPKTS] &&
|
|
Packit |
c22fc9 |
attrs[IPVS_STATS_ATTR_INBYTES] &&
|
|
Packit |
c22fc9 |
attrs[IPVS_STATS_ATTR_OUTBYTES] &&
|
|
Packit |
c22fc9 |
attrs[IPVS_STATS_ATTR_CPS] &&
|
|
Packit |
c22fc9 |
attrs[IPVS_STATS_ATTR_INPPS] &&
|
|
Packit |
c22fc9 |
attrs[IPVS_STATS_ATTR_OUTPPS] &&
|
|
Packit |
c22fc9 |
attrs[IPVS_STATS_ATTR_INBPS] &&
|
|
Packit |
c22fc9 |
attrs[IPVS_STATS_ATTR_OUTBPS]))
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
stats->conns = nla_get_u64(attrs[IPVS_STATS_ATTR_CONNS]);
|
|
Packit |
c22fc9 |
stats->inpkts = nla_get_u64(attrs[IPVS_STATS_ATTR_INPKTS]);
|
|
Packit |
c22fc9 |
stats->outpkts = nla_get_u64(attrs[IPVS_STATS_ATTR_OUTPKTS]);
|
|
Packit |
c22fc9 |
stats->inbytes = nla_get_u64(attrs[IPVS_STATS_ATTR_INBYTES]);
|
|
Packit |
c22fc9 |
stats->outbytes = nla_get_u64(attrs[IPVS_STATS_ATTR_OUTBYTES]);
|
|
Packit |
c22fc9 |
stats->cps = nla_get_u64(attrs[IPVS_STATS_ATTR_CPS]);
|
|
Packit |
c22fc9 |
stats->inpps = nla_get_u64(attrs[IPVS_STATS_ATTR_INPPS]);
|
|
Packit |
c22fc9 |
stats->outpps = nla_get_u64(attrs[IPVS_STATS_ATTR_OUTPPS]);
|
|
Packit |
c22fc9 |
stats->inbps = nla_get_u64(attrs[IPVS_STATS_ATTR_INBPS]);
|
|
Packit |
c22fc9 |
stats->outbps = nla_get_u64(attrs[IPVS_STATS_ATTR_OUTBPS]);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static int ipvs_parse_stats(ip_vs_stats_t *stats, struct nlattr *nla)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
struct nlattr *attrs[IPVS_STATS_ATTR_MAX + 1];
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (nla_parse_nested(attrs, IPVS_STATS_ATTR_MAX, nla, ipvs_stats_policy))
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!(attrs[IPVS_STATS_ATTR_CONNS] &&
|
|
Packit |
c22fc9 |
attrs[IPVS_STATS_ATTR_INPKTS] &&
|
|
Packit |
c22fc9 |
attrs[IPVS_STATS_ATTR_OUTPKTS] &&
|
|
Packit |
c22fc9 |
attrs[IPVS_STATS_ATTR_INBYTES] &&
|
|
Packit |
c22fc9 |
attrs[IPVS_STATS_ATTR_OUTBYTES] &&
|
|
Packit |
c22fc9 |
attrs[IPVS_STATS_ATTR_CPS] &&
|
|
Packit |
c22fc9 |
attrs[IPVS_STATS_ATTR_INPPS] &&
|
|
Packit |
c22fc9 |
attrs[IPVS_STATS_ATTR_OUTPPS] &&
|
|
Packit |
c22fc9 |
attrs[IPVS_STATS_ATTR_INBPS] &&
|
|
Packit |
c22fc9 |
attrs[IPVS_STATS_ATTR_OUTBPS]))
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
stats->conns = nla_get_u32(attrs[IPVS_STATS_ATTR_CONNS]);
|
|
Packit |
c22fc9 |
stats->inpkts = nla_get_u32(attrs[IPVS_STATS_ATTR_INPKTS]);
|
|
Packit |
c22fc9 |
stats->outpkts = nla_get_u32(attrs[IPVS_STATS_ATTR_OUTPKTS]);
|
|
Packit |
c22fc9 |
stats->inbytes = nla_get_u64(attrs[IPVS_STATS_ATTR_INBYTES]);
|
|
Packit |
c22fc9 |
stats->outbytes = nla_get_u64(attrs[IPVS_STATS_ATTR_OUTBYTES]);
|
|
Packit |
c22fc9 |
stats->cps = nla_get_u32(attrs[IPVS_STATS_ATTR_CPS]);
|
|
Packit |
c22fc9 |
stats->inpps = nla_get_u32(attrs[IPVS_STATS_ATTR_INPPS]);
|
|
Packit |
c22fc9 |
stats->outpps = nla_get_u32(attrs[IPVS_STATS_ATTR_OUTPPS]);
|
|
Packit |
c22fc9 |
stats->inbps = nla_get_u32(attrs[IPVS_STATS_ATTR_INBPS]);
|
|
Packit |
c22fc9 |
stats->outbps = nla_get_u32(attrs[IPVS_STATS_ATTR_OUTBPS]);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static int ipvs_services_parse_cb(struct nl_msg *msg, void *arg)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
struct nlmsghdr *nlh = nlmsg_hdr(msg);
|
|
Packit |
c22fc9 |
struct nlattr *attrs[IPVS_CMD_ATTR_MAX + 1];
|
|
Packit |
c22fc9 |
struct nlattr *svc_attrs[IPVS_SVC_ATTR_MAX + 1];
|
|
Packit |
c22fc9 |
struct ip_vs_get_services_app **getp = (struct ip_vs_get_services_app **)arg;
|
|
Packit |
c22fc9 |
struct ip_vs_get_services_app *get = (struct ip_vs_get_services_app *)*getp;
|
|
Packit |
c22fc9 |
struct ip_vs_flags flags;
|
|
Packit |
c22fc9 |
unsigned i = get->user.num_services;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (genlmsg_parse(nlh, 0, attrs, IPVS_CMD_ATTR_MAX, ipvs_cmd_policy) != 0)
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!attrs[IPVS_CMD_ATTR_SERVICE])
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (nla_parse_nested(svc_attrs, IPVS_SVC_ATTR_MAX, attrs[IPVS_CMD_ATTR_SERVICE], ipvs_service_policy))
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
memset(&(get->user.entrytable[i]), 0, sizeof(get->user.entrytable[i]));
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!(svc_attrs[IPVS_SVC_ATTR_AF] &&
|
|
Packit |
c22fc9 |
(svc_attrs[IPVS_SVC_ATTR_FWMARK] ||
|
|
Packit |
c22fc9 |
(svc_attrs[IPVS_SVC_ATTR_PROTOCOL] &&
|
|
Packit |
c22fc9 |
svc_attrs[IPVS_SVC_ATTR_ADDR] &&
|
|
Packit |
c22fc9 |
svc_attrs[IPVS_SVC_ATTR_PORT])) &&
|
|
Packit |
c22fc9 |
svc_attrs[IPVS_SVC_ATTR_SCHED_NAME] &&
|
|
Packit |
c22fc9 |
svc_attrs[IPVS_SVC_ATTR_NETMASK] &&
|
|
Packit |
c22fc9 |
svc_attrs[IPVS_SVC_ATTR_TIMEOUT] &&
|
|
Packit |
c22fc9 |
svc_attrs[IPVS_SVC_ATTR_FLAGS]))
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
get->user.entrytable[i].af = nla_get_u16(svc_attrs[IPVS_SVC_ATTR_AF]);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (svc_attrs[IPVS_SVC_ATTR_FWMARK])
|
|
Packit |
c22fc9 |
get->user.entrytable[i].user.fwmark = nla_get_u32(svc_attrs[IPVS_SVC_ATTR_FWMARK]);
|
|
Packit |
c22fc9 |
else {
|
|
Packit |
c22fc9 |
get->user.entrytable[i].user.protocol = nla_get_u16(svc_attrs[IPVS_SVC_ATTR_PROTOCOL]);
|
|
Packit |
c22fc9 |
memcpy(&(get->user.entrytable[i].nf_addr), nla_data(svc_attrs[IPVS_SVC_ATTR_ADDR]),
|
|
Packit |
c22fc9 |
sizeof(get->user.entrytable[i].nf_addr));
|
|
Packit |
c22fc9 |
get->user.entrytable[i].user.port = nla_get_u16(svc_attrs[IPVS_SVC_ATTR_PORT]);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
strncpy(get->user.entrytable[i].user.sched_name,
|
|
Packit |
c22fc9 |
nla_get_string(svc_attrs[IPVS_SVC_ATTR_SCHED_NAME]),
|
|
Packit |
c22fc9 |
IP_VS_SCHEDNAME_MAXLEN);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef _HAVE_PE_NAME_
|
|
Packit |
c22fc9 |
if (svc_attrs[IPVS_SVC_ATTR_PE_NAME])
|
|
Packit |
c22fc9 |
strncpy(get->user.entrytable[i].pe_name,
|
|
Packit |
c22fc9 |
nla_get_string(svc_attrs[IPVS_SVC_ATTR_PE_NAME]),
|
|
Packit |
c22fc9 |
IP_VS_PENAME_MAXLEN);
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
get->user.entrytable[i].user.netmask = nla_get_u32(svc_attrs[IPVS_SVC_ATTR_NETMASK]);
|
|
Packit |
c22fc9 |
get->user.entrytable[i].user.timeout = nla_get_u32(svc_attrs[IPVS_SVC_ATTR_TIMEOUT]);
|
|
Packit |
c22fc9 |
nla_memcpy(&flags, svc_attrs[IPVS_SVC_ATTR_FLAGS], sizeof(flags));
|
|
Packit |
c22fc9 |
get->user.entrytable[i].user.flags = flags.flags & flags.mask;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef _WITH_LVS_64BIT_STATS_
|
|
Packit |
c22fc9 |
if (svc_attrs[IPVS_SVC_ATTR_STATS64]) {
|
|
Packit |
c22fc9 |
if (ipvs_parse_stats64(&(get->user.entrytable[i].stats),
|
|
Packit |
c22fc9 |
svc_attrs[IPVS_SVC_ATTR_STATS64]) != 0)
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
} else if (svc_attrs[IPVS_SVC_ATTR_STATS])
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
if (ipvs_parse_stats(&(get->user.entrytable[i].stats),
|
|
Packit |
c22fc9 |
svc_attrs[IPVS_SVC_ATTR_STATS]) != 0)
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
get->user.entrytable[i].user.num_dests = 0;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
i++;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
get->user.num_services = i;
|
|
Packit |
c22fc9 |
get = realloc(get, sizeof(*get)
|
|
Packit |
c22fc9 |
+ sizeof(ipvs_service_entry_t) * (get->user.num_services + 1));
|
|
Packit |
c22fc9 |
*getp = get;
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static int ipvs_dests_parse_cb(struct nl_msg *msg, void *arg)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
struct nlmsghdr *nlh = nlmsg_hdr(msg);
|
|
Packit |
c22fc9 |
struct nlattr *attrs[IPVS_CMD_ATTR_MAX + 1];
|
|
Packit |
c22fc9 |
struct nlattr *dest_attrs[IPVS_DEST_ATTR_MAX + 1];
|
|
Packit |
c22fc9 |
#if HAVE_DECL_IPVS_DEST_ATTR_ADDR_FAMILY
|
|
Packit |
c22fc9 |
struct nlattr *attr_addr_family = NULL;
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
struct ip_vs_get_dests_app **dp = (struct ip_vs_get_dests_app **)arg;
|
|
Packit |
c22fc9 |
struct ip_vs_get_dests_app *d = (struct ip_vs_get_dests_app *)*dp;
|
|
Packit |
c22fc9 |
unsigned i = d->user.num_dests;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (genlmsg_parse(nlh, 0, attrs, IPVS_CMD_ATTR_MAX, ipvs_cmd_policy) != 0)
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!attrs[IPVS_CMD_ATTR_DEST])
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (nla_parse_nested(dest_attrs, IPVS_DEST_ATTR_MAX, attrs[IPVS_CMD_ATTR_DEST], ipvs_dest_policy))
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
memset(&(d->user.entrytable[i]), 0, sizeof(d->user.entrytable[i]));
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!(dest_attrs[IPVS_DEST_ATTR_ADDR] &&
|
|
Packit |
c22fc9 |
dest_attrs[IPVS_DEST_ATTR_PORT] &&
|
|
Packit |
c22fc9 |
dest_attrs[IPVS_DEST_ATTR_FWD_METHOD] &&
|
|
Packit |
c22fc9 |
dest_attrs[IPVS_DEST_ATTR_WEIGHT] &&
|
|
Packit |
c22fc9 |
dest_attrs[IPVS_DEST_ATTR_U_THRESH] &&
|
|
Packit |
c22fc9 |
dest_attrs[IPVS_DEST_ATTR_L_THRESH] &&
|
|
Packit |
c22fc9 |
dest_attrs[IPVS_DEST_ATTR_ACTIVE_CONNS] &&
|
|
Packit |
c22fc9 |
dest_attrs[IPVS_DEST_ATTR_INACT_CONNS] &&
|
|
Packit |
c22fc9 |
dest_attrs[IPVS_DEST_ATTR_PERSIST_CONNS]))
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
memcpy(&(d->user.entrytable[i].nf_addr),
|
|
Packit |
c22fc9 |
nla_data(dest_attrs[IPVS_DEST_ATTR_ADDR]),
|
|
Packit |
c22fc9 |
sizeof(d->user.entrytable[i].nf_addr));
|
|
Packit |
c22fc9 |
d->user.entrytable[i].user.port = nla_get_u16(dest_attrs[IPVS_DEST_ATTR_PORT]);
|
|
Packit |
c22fc9 |
d->user.entrytable[i].user.conn_flags = nla_get_u32(dest_attrs[IPVS_DEST_ATTR_FWD_METHOD]);
|
|
Packit |
c22fc9 |
d->user.entrytable[i].user.weight = nla_get_s32(dest_attrs[IPVS_DEST_ATTR_WEIGHT]);
|
|
Packit |
c22fc9 |
d->user.entrytable[i].user.u_threshold = nla_get_u32(dest_attrs[IPVS_DEST_ATTR_U_THRESH]);
|
|
Packit |
c22fc9 |
d->user.entrytable[i].user.l_threshold = nla_get_u32(dest_attrs[IPVS_DEST_ATTR_L_THRESH]);
|
|
Packit |
c22fc9 |
d->user.entrytable[i].user.activeconns = nla_get_u32(dest_attrs[IPVS_DEST_ATTR_ACTIVE_CONNS]);
|
|
Packit |
c22fc9 |
d->user.entrytable[i].user.inactconns = nla_get_u32(dest_attrs[IPVS_DEST_ATTR_INACT_CONNS]);
|
|
Packit |
c22fc9 |
d->user.entrytable[i].user.persistconns = nla_get_u32(dest_attrs[IPVS_DEST_ATTR_PERSIST_CONNS]);
|
|
Packit |
c22fc9 |
#if HAVE_DECL_IPVS_DEST_ATTR_ADDR_FAMILY
|
|
Packit |
c22fc9 |
attr_addr_family = dest_attrs[IPVS_DEST_ATTR_ADDR_FAMILY];
|
|
Packit |
c22fc9 |
if (attr_addr_family)
|
|
Packit |
c22fc9 |
d->user.entrytable[i].af = nla_get_u16(attr_addr_family);
|
|
Packit |
c22fc9 |
else
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
d->user.entrytable[i].af = d->af;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef _WITH_LVS_64BIT_STATS_
|
|
Packit |
c22fc9 |
if (dest_attrs[IPVS_DEST_ATTR_STATS64]) {
|
|
Packit |
c22fc9 |
if (ipvs_parse_stats64(&(d->user.entrytable[i].stats),
|
|
Packit |
c22fc9 |
dest_attrs[IPVS_DEST_ATTR_STATS64]) != 0)
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
} else if (dest_attrs[IPVS_DEST_ATTR_STATS])
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
if (ipvs_parse_stats(&(d->user.entrytable[i].stats),
|
|
Packit |
c22fc9 |
dest_attrs[IPVS_DEST_ATTR_STATS]) != 0)
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
i++;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
d->user.num_dests = i;
|
|
Packit |
c22fc9 |
d = realloc(d, sizeof(*d) + sizeof(ipvs_dest_entry_t) * (d->user.num_dests + 1));
|
|
Packit |
c22fc9 |
*dp = d;
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif /* LIBIPVS_USE_NL */
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
struct ip_vs_get_dests_app *ipvs_get_dests(ipvs_service_entry_t *svc)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
struct ip_vs_get_dests_app *d;
|
|
Packit |
c22fc9 |
struct ip_vs_get_dests *dk;
|
|
Packit |
c22fc9 |
socklen_t len;
|
|
Packit |
c22fc9 |
unsigned i;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
len = (socklen_t)(sizeof(*d) + sizeof(ipvs_dest_entry_t) * svc->user.num_dests);
|
|
Packit |
c22fc9 |
if (!(d = MALLOC(len)))
|
|
Packit |
c22fc9 |
return NULL;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
ipvs_func = ipvs_get_dests;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef LIBIPVS_USE_NL
|
|
Packit |
c22fc9 |
if (try_nl) {
|
|
Packit |
c22fc9 |
struct nl_msg *msg;
|
|
Packit |
c22fc9 |
struct nlattr *nl_service;
|
|
Packit |
c22fc9 |
if (svc->user.num_dests == 0)
|
|
Packit |
c22fc9 |
d = realloc(d,sizeof(*d) + sizeof(ipvs_dest_entry_t));
|
|
Packit |
c22fc9 |
d->user.fwmark = svc->user.fwmark;
|
|
Packit |
c22fc9 |
d->user.protocol = svc->user.protocol;
|
|
Packit |
c22fc9 |
d->nf_addr = svc->nf_addr;
|
|
Packit |
c22fc9 |
d->user.port = svc->user.port;
|
|
Packit |
c22fc9 |
d->user.num_dests = svc->user.num_dests;
|
|
Packit |
c22fc9 |
d->af = svc->af;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
msg = ipvs_nl_message(IPVS_CMD_GET_DEST, NLM_F_DUMP);
|
|
Packit |
c22fc9 |
if (!msg)
|
|
Packit |
c22fc9 |
goto ipvs_nl_dest_failure;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
nl_service = nla_nest_start(msg, IPVS_CMD_ATTR_SERVICE);
|
|
Packit |
c22fc9 |
if (!nl_service)
|
|
Packit |
c22fc9 |
goto nla_put_failure;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
NLA_PUT_U16(msg, IPVS_SVC_ATTR_AF, svc->af);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (svc->user.fwmark) {
|
|
Packit |
c22fc9 |
NLA_PUT_U32(msg, IPVS_SVC_ATTR_FWMARK, svc->user.fwmark);
|
|
Packit |
c22fc9 |
} else {
|
|
Packit |
c22fc9 |
NLA_PUT_U16(msg, IPVS_SVC_ATTR_PROTOCOL, svc->user.protocol);
|
|
Packit |
c22fc9 |
NLA_PUT(msg, IPVS_SVC_ATTR_ADDR, sizeof(svc->nf_addr),
|
|
Packit |
c22fc9 |
&svc->nf_addr);
|
|
Packit |
c22fc9 |
NLA_PUT_U16(msg, IPVS_SVC_ATTR_PORT, svc->user.port);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
nla_nest_end(msg, nl_service);
|
|
Packit |
c22fc9 |
if (ipvs_nl_send_message(msg, ipvs_dests_parse_cb, &d))
|
|
Packit |
c22fc9 |
goto ipvs_nl_dest_failure;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return d;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
nla_put_failure:
|
|
Packit |
c22fc9 |
nlmsg_free(msg);
|
|
Packit |
c22fc9 |
ipvs_nl_dest_failure:
|
|
Packit |
c22fc9 |
FREE(d);
|
|
Packit |
c22fc9 |
return NULL;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif /* LIBIPVS_USE_NL */
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (svc->af != AF_INET) {
|
|
Packit |
c22fc9 |
errno = EAFNOSUPPORT;
|
|
Packit |
c22fc9 |
FREE(d);
|
|
Packit |
c22fc9 |
return NULL;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
len = (socklen_t)(sizeof(*dk) + sizeof(struct ip_vs_dest_entry) * svc->user.num_dests);
|
|
Packit |
c22fc9 |
if (!(dk = MALLOC(len))) {
|
|
Packit |
c22fc9 |
FREE(d);
|
|
Packit |
c22fc9 |
return NULL;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
dk->fwmark = svc->user.fwmark;
|
|
Packit |
c22fc9 |
dk->protocol = svc->user.protocol;
|
|
Packit |
c22fc9 |
dk->addr = svc->nf_addr.ip;
|
|
Packit |
c22fc9 |
dk->port = svc->user.port;
|
|
Packit |
c22fc9 |
dk->num_dests = svc->user.num_dests;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (getsockopt(sockfd, IPPROTO_IP,
|
|
Packit |
c22fc9 |
IP_VS_SO_GET_DESTS, dk, &len) < 0) {
|
|
Packit |
c22fc9 |
FREE(d);
|
|
Packit |
c22fc9 |
FREE(dk);
|
|
Packit |
c22fc9 |
return NULL;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
memcpy(d, dk, sizeof(struct ip_vs_get_dests));
|
|
Packit |
c22fc9 |
d->af = AF_INET;
|
|
Packit |
c22fc9 |
d->nf_addr.ip = d->user.addr;
|
|
Packit |
c22fc9 |
for (i = 0; i < dk->num_dests; i++) {
|
|
Packit |
c22fc9 |
memcpy(&d->user.entrytable[i], &dk->entrytable[i],
|
|
Packit |
c22fc9 |
sizeof(struct ip_vs_dest_entry));
|
|
Packit |
c22fc9 |
d->user.entrytable[i].af = AF_INET;
|
|
Packit |
c22fc9 |
d->user.entrytable[i].nf_addr.ip = d->user.entrytable[i].user.addr;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
FREE(dk);
|
|
Packit |
c22fc9 |
return d;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
ipvs_service_entry_t *
|
|
Packit |
c22fc9 |
ipvs_get_service(__u32 fwmark, __u16 af, __u16 protocol, union nf_inet_addr *addr, __u16 port)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
ipvs_service_entry_t *svc;
|
|
Packit |
c22fc9 |
socklen_t len;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
ipvs_func = ipvs_get_service;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef LIBIPVS_USE_NL
|
|
Packit |
c22fc9 |
if (try_nl) {
|
|
Packit |
c22fc9 |
struct ip_vs_get_services *get;
|
|
Packit |
c22fc9 |
struct nl_msg *msg;
|
|
Packit |
c22fc9 |
ipvs_service_t tsvc;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
svc = MALLOC(sizeof(*svc));
|
|
Packit |
c22fc9 |
if (!svc)
|
|
Packit |
c22fc9 |
return NULL;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
memset(&tsvc, 0, sizeof(tsvc));
|
|
Packit |
c22fc9 |
tsvc.user.fwmark = fwmark;
|
|
Packit |
c22fc9 |
tsvc.af = af;
|
|
Packit |
c22fc9 |
tsvc.user.protocol= protocol;
|
|
Packit |
c22fc9 |
tsvc.nf_addr = *addr;
|
|
Packit |
c22fc9 |
tsvc.user.port = port;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!(get = MALLOC(sizeof(*get) + sizeof(ipvs_service_entry_t))))
|
|
Packit |
c22fc9 |
goto ipvs_get_service_err2;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
get->num_services = 0;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
msg = ipvs_nl_message(IPVS_CMD_GET_SERVICE, 0);
|
|
Packit |
c22fc9 |
if (!msg)
|
|
Packit |
c22fc9 |
goto ipvs_get_service_err;
|
|
Packit |
c22fc9 |
if (ipvs_nl_fill_service_attr(msg, &tsvc))
|
|
Packit |
c22fc9 |
goto nla_put_failure;
|
|
Packit |
c22fc9 |
if (ipvs_nl_send_message(msg, ipvs_services_parse_cb, &get))
|
|
Packit |
c22fc9 |
goto ipvs_get_service_err;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
memcpy(svc, &(get->entrytable[0]), sizeof(*svc));
|
|
Packit |
c22fc9 |
FREE(get);
|
|
Packit |
c22fc9 |
return svc;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
nla_put_failure:
|
|
Packit |
c22fc9 |
nlmsg_free(msg);
|
|
Packit |
c22fc9 |
ipvs_get_service_err:
|
|
Packit |
c22fc9 |
FREE(get);
|
|
Packit |
c22fc9 |
ipvs_get_service_err2:
|
|
Packit |
c22fc9 |
FREE(svc);
|
|
Packit |
c22fc9 |
return NULL;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
len = sizeof(*svc);
|
|
Packit |
c22fc9 |
svc = calloc(1, len);
|
|
Packit |
c22fc9 |
if (!svc)
|
|
Packit |
c22fc9 |
return NULL;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
svc->user.fwmark = fwmark;
|
|
Packit |
c22fc9 |
svc->af = af;
|
|
Packit |
c22fc9 |
svc->user.protocol = protocol;
|
|
Packit |
c22fc9 |
svc->nf_addr = *addr;
|
|
Packit |
c22fc9 |
svc->user.port = port;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
CHECK_COMPAT_SVC(svc, NULL);
|
|
Packit |
c22fc9 |
if (getsockopt(sockfd, IPPROTO_IP, IP_VS_SO_GET_SERVICE,
|
|
Packit |
c22fc9 |
(char *)svc, &len)) {
|
|
Packit |
c22fc9 |
FREE(svc);
|
|
Packit |
c22fc9 |
return NULL;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
svc->af = AF_INET;
|
|
Packit |
c22fc9 |
svc->nf_addr.ip = svc->user.addr;
|
|
Packit |
c22fc9 |
#ifdef _HAVE_PE_NAME_
|
|
Packit |
c22fc9 |
svc->pe_name[0] = '\0';
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
return svc;
|
|
Packit |
c22fc9 |
out_err:
|
|
Packit |
c22fc9 |
FREE(svc);
|
|
Packit |
c22fc9 |
return NULL;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif /* _WITH_SNMP_CHECKER_ */
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
void ipvs_close(void)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
#ifdef LIBIPVS_USE_NL
|
|
Packit |
c22fc9 |
if (try_nl)
|
|
Packit |
c22fc9 |
return;
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
if (sockfd != -1) {
|
|
Packit |
c22fc9 |
close(sockfd);
|
|
Packit |
c22fc9 |
sockfd = -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
const char *ipvs_strerror(int err)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
unsigned int i;
|
|
Packit |
c22fc9 |
struct table_struct {
|
|
Packit |
c22fc9 |
void *func;
|
|
Packit |
c22fc9 |
int err;
|
|
Packit |
c22fc9 |
const char *message;
|
|
Packit |
c22fc9 |
} table [] = {
|
|
Packit |
c22fc9 |
{ ipvs_add_service, EEXIST, "Service already exists" },
|
|
Packit |
c22fc9 |
{ ipvs_add_service, ENOENT, "Scheduler or persistence engine not found" },
|
|
Packit |
c22fc9 |
{ ipvs_update_service, ESRCH, "No such service" },
|
|
Packit |
c22fc9 |
{ ipvs_update_service, ENOENT, "Scheduler or persistence engine not found" },
|
|
Packit |
c22fc9 |
{ ipvs_del_service, ESRCH, "No such service" },
|
|
Packit |
c22fc9 |
{ ipvs_zero_service, ESRCH, "No such service" },
|
|
Packit |
c22fc9 |
{ ipvs_add_dest, ESRCH, "Service not defined" },
|
|
Packit |
c22fc9 |
{ ipvs_add_dest, EEXIST, "Destination already exists" },
|
|
Packit |
c22fc9 |
{ ipvs_update_dest, ESRCH, "Service not defined" },
|
|
Packit |
c22fc9 |
{ ipvs_update_dest, ENOENT, "No such destination" },
|
|
Packit |
c22fc9 |
{ ipvs_del_dest, ESRCH, "Service not defined" },
|
|
Packit |
c22fc9 |
{ ipvs_del_dest, ENOENT, "No such destination" },
|
|
Packit |
c22fc9 |
{ ipvs_start_daemon, EEXIST, "Daemon has already run" },
|
|
Packit |
c22fc9 |
{ ipvs_stop_daemon, ESRCH, "No daemon is running" },
|
|
Packit |
c22fc9 |
#ifdef _WITH_SNMP_CHECKER_
|
|
Packit |
c22fc9 |
{ ipvs_get_dests, ESRCH, "No such service" },
|
|
Packit |
c22fc9 |
{ ipvs_get_service, ESRCH, "No such service" },
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
{ 0, EPERM, "Permission denied (you must be root)" },
|
|
Packit |
c22fc9 |
{ 0, EINVAL, "Invalid operation. Possibly wrong module version, address not unicast, ..." },
|
|
Packit |
c22fc9 |
{ 0, ENOPROTOOPT, "Protocol not available" },
|
|
Packit |
c22fc9 |
{ 0, ENOMEM, "Memory allocation problem" },
|
|
Packit |
c22fc9 |
{ 0, EOPNOTSUPP, "Operation not supported with IPv6" },
|
|
Packit |
c22fc9 |
{ 0, EAFNOSUPPORT, "Operation not supported with specified address family" },
|
|
Packit |
c22fc9 |
{ 0, EMSGSIZE, "Module is wrong version" },
|
|
Packit |
c22fc9 |
};
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
|
|
Packit |
c22fc9 |
if ((!table[i].func || table[i].func == ipvs_func)
|
|
Packit |
c22fc9 |
&& table[i].err == err)
|
|
Packit |
c22fc9 |
return table[i].message;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return strerror(err);
|
|
Packit |
c22fc9 |
}
|