* [PATCHv3 0/2] IPVS: Add Generic Netlink configuration interface
@ 2008-08-07 14:43 Julius Volz
2008-08-07 14:43 ` [PATCHv3 1/2] IPVS: Add genetlink interface definitions to ip_vs.h Julius Volz
` (2 more replies)
0 siblings, 3 replies; 23+ messages in thread
From: Julius Volz @ 2008-08-07 14:43 UTC (permalink / raw)
To: julius.volz, netdev, lvs-devel; +Cc: horms, kaber, davem, tgraf, vbusam
This is the third iteration of the IPVS Netlink interface, this time
with only a small fix for a typo found by Thomas Graf. If there are no
further major issues, can this be applied?
The two patches add a Generic Netlink interface to IPVS while keeping
the old get/setsockopt interface for userspace backwards compatibility.
The motivation for this is to have a more extensible interface for
future changes, such as the planned IPv6 support.
An ipvsadm that already uses the new interface is available here:
http://sixpak.org/vince/google/ipvsadm/
(by Vince Busam)
Old ipvsadms continue to work with this change.
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCHv3 1/2] IPVS: Add genetlink interface definitions to ip_vs.h
2008-08-07 14:43 [PATCHv3 0/2] IPVS: Add Generic Netlink configuration interface Julius Volz
@ 2008-08-07 14:43 ` Julius Volz
2008-08-07 14:43 ` [PATCHv3 2/2] IPVS: Add genetlink interface implementation Julius Volz
2008-08-08 2:26 ` [PATCHv3 0/2] IPVS: Add Generic Netlink configuration interface Simon Horman
2 siblings, 0 replies; 23+ messages in thread
From: Julius Volz @ 2008-08-07 14:43 UTC (permalink / raw)
To: julius.volz, netdev, lvs-devel
Cc: horms, kaber, davem, tgraf, vbusam, Julius Volz
Add IPVS Generic Netlink interface definitions to include/linux/ip_vs.h.
Signed-off-by: Julius Volz <juliusv@google.com>
1 files changed, 160 insertions(+), 0 deletions(-)
diff --git a/include/linux/ip_vs.h b/include/linux/ip_vs.h
index ec6eb49..0f434a2 100644
--- a/include/linux/ip_vs.h
+++ b/include/linux/ip_vs.h
@@ -242,4 +242,164 @@ struct ip_vs_daemon_user {
int syncid;
};
+/*
+ *
+ * IPVS Generic Netlink interface definitions
+ *
+ */
+
+/* Generic Netlink family info */
+
+#define IPVS_GENL_NAME "IPVS"
+#define IPVS_GENL_VERSION 0x1
+
+struct ip_vs_flags {
+ __be32 flags;
+ __be32 mask;
+};
+
+/* Generic Netlink command attributes */
+enum {
+ IPVS_CMD_UNSPEC = 0,
+
+ IPVS_CMD_NEW_SERVICE, /* add service */
+ IPVS_CMD_SET_SERVICE, /* modify service */
+ IPVS_CMD_DEL_SERVICE, /* delete service */
+ IPVS_CMD_GET_SERVICE, /* get service info */
+
+ IPVS_CMD_NEW_DEST, /* add destination */
+ IPVS_CMD_SET_DEST, /* modify destination */
+ IPVS_CMD_DEL_DEST, /* delete destination */
+ IPVS_CMD_GET_DEST, /* get destination info */
+
+ IPVS_CMD_NEW_DAEMON, /* start sync daemon */
+ IPVS_CMD_DEL_DAEMON, /* stop sync daemon */
+ IPVS_CMD_GET_DAEMON, /* get sync daemon status */
+
+ IPVS_CMD_SET_CONFIG, /* set config settings */
+ IPVS_CMD_GET_CONFIG, /* get config settings */
+
+ IPVS_CMD_SET_INFO, /* only used in GET_INFO reply */
+ IPVS_CMD_GET_INFO, /* get general IPVS info */
+
+ IPVS_CMD_ZERO, /* zero all counters and stats */
+ IPVS_CMD_FLUSH, /* flush services and dests */
+
+ __IPVS_CMD_MAX,
+};
+
+#define IPVS_CMD_MAX (__IPVS_CMD_MAX - 1)
+
+/* Attributes used in the first level of commands */
+enum {
+ IPVS_CMD_ATTR_UNSPEC = 0,
+ IPVS_CMD_ATTR_SERVICE, /* nested service attribute */
+ IPVS_CMD_ATTR_DEST, /* nested destination attribute */
+ IPVS_CMD_ATTR_DAEMON, /* nested sync daemon attribute */
+ IPVS_CMD_ATTR_TIMEOUT_TCP, /* TCP connection timeout */
+ IPVS_CMD_ATTR_TIMEOUT_TCP_FIN, /* TCP FIN wait timeout */
+ IPVS_CMD_ATTR_TIMEOUT_UDP, /* UDP timeout */
+ __IPVS_CMD_ATTR_MAX,
+};
+
+#define IPVS_CMD_ATTR_MAX (__IPVS_SVC_ATTR_MAX - 1)
+
+/*
+ * Attributes used to describe a service
+ *
+ * Used inside nested attribute IPVS_CMD_ATTR_SERVICE
+ */
+enum {
+ IPVS_SVC_ATTR_UNSPEC = 0,
+ IPVS_SVC_ATTR_AF, /* address family */
+ IPVS_SVC_ATTR_PROTOCOL, /* virtual service protocol */
+ IPVS_SVC_ATTR_ADDR, /* virtual service address */
+ IPVS_SVC_ATTR_PORT, /* virtual service port */
+ IPVS_SVC_ATTR_FWMARK, /* firewall mark of service */
+
+ IPVS_SVC_ATTR_SCHED_NAME, /* name of scheduler */
+ IPVS_SVC_ATTR_FLAGS, /* virtual service flags */
+ IPVS_SVC_ATTR_TIMEOUT, /* persistent timeout */
+ IPVS_SVC_ATTR_NETMASK, /* persistent netmask */
+
+ IPVS_SVC_ATTR_STATS, /* nested attribute for service stats */
+ __IPVS_SVC_ATTR_MAX,
+};
+
+#define IPVS_SVC_ATTR_MAX (__IPVS_SVC_ATTR_MAX - 1)
+
+/*
+ * Attributes used to describe a destination (real server)
+ *
+ * Used inside nested attribute IPVS_CMD_ATTR_DEST
+ */
+enum {
+ IPVS_DEST_ATTR_UNSPEC = 0,
+ IPVS_DEST_ATTR_ADDR, /* real server address */
+ IPVS_DEST_ATTR_PORT, /* real server port */
+
+ IPVS_DEST_ATTR_FWD_METHOD, /* forwarding method */
+ IPVS_DEST_ATTR_WEIGHT, /* destination weight */
+
+ IPVS_DEST_ATTR_U_THRESH, /* upper threshold */
+ IPVS_DEST_ATTR_L_THRESH, /* lower threshold */
+
+ IPVS_DEST_ATTR_ACTIVE_CONNS, /* active connections */
+ IPVS_DEST_ATTR_INACT_CONNS, /* inactive connections */
+ IPVS_DEST_ATTR_PERSIST_CONNS, /* persistent connections */
+
+ IPVS_DEST_ATTR_STATS, /* nested attribute for dest stats */
+ __IPVS_DEST_ATTR_MAX,
+};
+
+#define IPVS_DEST_ATTR_MAX (__IPVS_DEST_ATTR_MAX - 1)
+
+/*
+ * Attributes describing a sync daemon
+ *
+ * Used inside nested attribute IPVS_CMD_ATTR_DAEMON
+ */
+enum {
+ IPVS_DAEMON_ATTR_UNSPEC = 0,
+ IPVS_DAEMON_ATTR_STATE, /* sync daemon state (master/backup) */
+ IPVS_DAEMON_ATTR_MCAST_IFN, /* multicast interface name */
+ IPVS_DAEMON_ATTR_SYNC_ID, /* SyncID we belong to */
+ __IPVS_DAEMON_ATTR_MAX,
+};
+
+#define IPVS_DAEMON_ATTR_MAX (__IPVS_DAEMON_ATTR_MAX - 1)
+
+/*
+ * Attributes used to describe service or destination entry statistics
+ *
+ * Used inside nested attributes IPVS_SVC_ATTR_STATS and IPVS_DEST_ATTR_STATS
+ */
+enum {
+ IPVS_STATS_ATTR_UNSPEC = 0,
+ IPVS_STATS_ATTR_CONNS, /* connections scheduled */
+ IPVS_STATS_ATTR_INPKTS, /* incoming packets */
+ IPVS_STATS_ATTR_OUTPKTS, /* outgoing packets */
+ IPVS_STATS_ATTR_INBYTES, /* incoming bytes */
+ IPVS_STATS_ATTR_OUTBYTES, /* outgoing bytes */
+
+ IPVS_STATS_ATTR_CPS, /* current connection rate */
+ IPVS_STATS_ATTR_INPPS, /* current in packet rate */
+ IPVS_STATS_ATTR_OUTPPS, /* current out packet rate */
+ IPVS_STATS_ATTR_INBPS, /* current in byte rate */
+ IPVS_STATS_ATTR_OUTBPS, /* current out byte rate */
+ __IPVS_STATS_ATTR_MAX,
+};
+
+#define IPVS_STATS_ATTR_MAX (__IPVS_STATS_ATTR_MAX - 1)
+
+/* Attributes used in response to IPVS_CMD_GET_INFO command */
+enum {
+ IPVS_INFO_ATTR_UNSPEC = 0,
+ IPVS_INFO_ATTR_VERSION, /* IPVS version number */
+ IPVS_INFO_ATTR_CONN_TAB_SIZE, /* size of connection hash table */
+ __IPVS_INFO_ATTR_MAX,
+};
+
+#define IPVS_INFO_ATTR_MAX (__IPVS_INFO_ATTR_MAX - 1)
+
#endif /* _IP_VS_H */
--
1.5.4.5
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCHv3 2/2] IPVS: Add genetlink interface implementation
2008-08-07 14:43 [PATCHv3 0/2] IPVS: Add Generic Netlink configuration interface Julius Volz
2008-08-07 14:43 ` [PATCHv3 1/2] IPVS: Add genetlink interface definitions to ip_vs.h Julius Volz
@ 2008-08-07 14:43 ` Julius Volz
2008-08-08 11:29 ` Julius Volz
2008-08-08 2:26 ` [PATCHv3 0/2] IPVS: Add Generic Netlink configuration interface Simon Horman
2 siblings, 1 reply; 23+ messages in thread
From: Julius Volz @ 2008-08-07 14:43 UTC (permalink / raw)
To: julius.volz, netdev, lvs-devel
Cc: horms, kaber, davem, tgraf, vbusam, Julius Volz
Add the implementation of the new Generic Netlink interface to IPVS and
keep the old set/getsockopt interface for userspace backwards
compatibility.
Signed-off-by: Julius Volz <juliusv@google.com>
1 files changed, 880 insertions(+), 0 deletions(-)
diff --git a/net/ipv4/ipvs/ip_vs_ctl.c b/net/ipv4/ipvs/ip_vs_ctl.c
index 9a5ace0..b4c5cc3 100644
--- a/net/ipv4/ipvs/ip_vs_ctl.c
+++ b/net/ipv4/ipvs/ip_vs_ctl.c
@@ -37,6 +37,7 @@
#include <net/ip.h>
#include <net/route.h>
#include <net/sock.h>
+#include <net/genetlink.h>
#include <asm/uaccess.h>
@@ -2305,6 +2306,877 @@ static struct nf_sockopt_ops ip_vs_sockopts = {
.owner = THIS_MODULE,
};
+/*
+ * Generic Netlink interface
+ */
+
+/* IPVS genetlink family */
+static struct genl_family ip_vs_genl_family = {
+ .id = GENL_ID_GENERATE,
+ .hdrsize = 0,
+ .name = IPVS_GENL_NAME,
+ .version = IPVS_GENL_VERSION,
+ .maxattr = IPVS_CMD_MAX,
+};
+
+/* Policy used for first-level command attributes */
+static const struct nla_policy ip_vs_cmd_policy[IPVS_CMD_ATTR_MAX + 1] = {
+ [IPVS_CMD_ATTR_SERVICE] = { .type = NLA_NESTED },
+ [IPVS_CMD_ATTR_DEST] = { .type = NLA_NESTED },
+ [IPVS_CMD_ATTR_DAEMON] = { .type = NLA_NESTED },
+ [IPVS_CMD_ATTR_TIMEOUT_TCP] = { .type = NLA_U32 },
+ [IPVS_CMD_ATTR_TIMEOUT_TCP_FIN] = { .type = NLA_U32 },
+ [IPVS_CMD_ATTR_TIMEOUT_UDP] = { .type = NLA_U32 },
+};
+
+/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_DAEMON */
+static const struct nla_policy ip_vs_daemon_policy[IPVS_DAEMON_ATTR_MAX + 1] = {
+ [IPVS_DAEMON_ATTR_STATE] = { .type = NLA_U32 },
+ [IPVS_DAEMON_ATTR_MCAST_IFN] = { .type = NLA_NUL_STRING,
+ .len = IP_VS_IFNAME_MAXLEN },
+ [IPVS_DAEMON_ATTR_SYNC_ID] = { .type = NLA_U32 },
+};
+
+/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_SERVICE */
+static const struct nla_policy ip_vs_svc_policy[IPVS_SVC_ATTR_MAX + 1] = {
+ [IPVS_SVC_ATTR_AF] = { .type = NLA_U16 },
+ [IPVS_SVC_ATTR_PROTOCOL] = { .type = NLA_U16 },
+ [IPVS_SVC_ATTR_ADDR] = { .type = NLA_BINARY,
+ .len = sizeof(union nf_inet_addr) },
+ [IPVS_SVC_ATTR_PORT] = { .type = NLA_U16 },
+ [IPVS_SVC_ATTR_FWMARK] = { .type = NLA_U32 },
+ [IPVS_SVC_ATTR_SCHED_NAME] = { .type = NLA_NUL_STRING,
+ .len = IP_VS_SCHEDNAME_MAXLEN },
+ [IPVS_SVC_ATTR_FLAGS] = { .type = NLA_U32 },
+ [IPVS_SVC_ATTR_TIMEOUT] = { .type = NLA_U32 },
+ [IPVS_SVC_ATTR_NETMASK] = { .type = NLA_U32 },
+ [IPVS_SVC_ATTR_STATS] = { .type = NLA_NESTED },
+};
+
+/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_DEST */
+static const struct nla_policy ip_vs_dest_policy[IPVS_DEST_ATTR_MAX + 1] = {
+ [IPVS_DEST_ATTR_ADDR] = { .type = NLA_BINARY,
+ .len = sizeof(union nf_inet_addr) },
+ [IPVS_DEST_ATTR_PORT] = { .type = NLA_U16 },
+ [IPVS_DEST_ATTR_FWD_METHOD] = { .type = NLA_BINARY,
+ .len = sizeof(struct ip_vs_flags) },
+ [IPVS_DEST_ATTR_WEIGHT] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_U_THRESH] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_L_THRESH] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_ACTIVE_CONNS] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_INACT_CONNS] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_PERSIST_CONNS] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_STATS] = { .type = NLA_NESTED },
+};
+
+static int ip_vs_genl_fill_stats(struct sk_buff *skb, int container_type,
+ struct ip_vs_stats *stats)
+{
+ struct nlattr *nl_stats = nla_nest_start(skb, container_type);
+ if (!nl_stats)
+ return -EMSGSIZE;
+
+ spin_lock_bh(&stats->lock);
+
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_CONNS, stats->conns);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_INPKTS, stats->inpkts);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTPKTS, stats->outpkts);
+ NLA_PUT_U64(skb, IPVS_STATS_ATTR_INBYTES, stats->inbytes);
+ NLA_PUT_U64(skb, IPVS_STATS_ATTR_OUTBYTES, stats->outbytes);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_CPS, stats->cps);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_INPPS, stats->inpps);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTPPS, stats->outpps);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_INBPS, stats->inbps);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTBPS, stats->outbps);
+
+ spin_unlock_bh(&stats->lock);
+
+ nla_nest_end(skb, nl_stats);
+
+ return 0;
+
+nla_put_failure:
+ spin_unlock_bh(&stats->lock);
+ nla_nest_cancel(skb, nl_stats);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_fill_service(struct sk_buff *skb,
+ struct ip_vs_service *svc)
+{
+ struct nlattr *nl_service;
+ struct ip_vs_flags flags = { .flags = svc->flags,
+ .mask = 0 };
+
+ nl_service = nla_nest_start(skb, IPVS_CMD_ATTR_SERVICE);
+ if (!nl_service)
+ return -EMSGSIZE;
+
+ NLA_PUT_U16(skb, IPVS_SVC_ATTR_AF, AF_INET);
+
+ if (svc->fwmark) {
+ NLA_PUT_U32(skb, IPVS_SVC_ATTR_FWMARK, svc->fwmark);
+ } else {
+ NLA_PUT_U16(skb, IPVS_SVC_ATTR_PROTOCOL, svc->protocol);
+ NLA_PUT(skb, IPVS_SVC_ATTR_ADDR, sizeof(svc->addr), &svc->addr);
+ NLA_PUT_U16(skb, IPVS_SVC_ATTR_PORT, svc->port);
+ }
+
+ NLA_PUT_STRING(skb, IPVS_SVC_ATTR_SCHED_NAME, svc->scheduler->name);
+ NLA_PUT(skb, IPVS_SVC_ATTR_FLAGS, sizeof(flags), &flags);
+ NLA_PUT_U32(skb, IPVS_SVC_ATTR_TIMEOUT, svc->timeout / HZ);
+ NLA_PUT_U32(skb, IPVS_SVC_ATTR_NETMASK, svc->netmask);
+
+ if (ip_vs_genl_fill_stats(skb, IPVS_SVC_ATTR_STATS, &svc->stats))
+ goto nla_put_failure;
+
+ nla_nest_end(skb, nl_service);
+
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(skb, nl_service);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_dump_service(struct sk_buff *skb,
+ struct ip_vs_service *svc,
+ struct netlink_callback *cb)
+{
+ void *hdr;
+
+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
+ &ip_vs_genl_family, NLM_F_MULTI,
+ IPVS_CMD_NEW_SERVICE);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ if (ip_vs_genl_fill_service(skb, svc) < 0)
+ goto nla_put_failure;
+
+ return genlmsg_end(skb, hdr);
+
+nla_put_failure:
+ genlmsg_cancel(skb, hdr);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_dump_services(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ int idx = 0, i;
+ int start = cb->args[0];
+ struct ip_vs_service *svc;
+
+ mutex_lock(&__ip_vs_mutex);
+ for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) {
+ list_for_each_entry(svc, &ip_vs_svc_table[i], s_list) {
+ if (++idx <= start)
+ continue;
+ if (ip_vs_genl_dump_service(skb, svc, cb) < 0) {
+ idx--;
+ goto nla_put_failure;
+ }
+ }
+ }
+
+ for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) {
+ list_for_each_entry(svc, &ip_vs_svc_fwm_table[i], f_list) {
+ if (++idx <= start)
+ continue;
+ if (ip_vs_genl_dump_service(skb, svc, cb) < 0) {
+ idx--;
+ goto nla_put_failure;
+ }
+ }
+ }
+
+nla_put_failure:
+ mutex_unlock(&__ip_vs_mutex);
+ cb->args[0] = idx;
+
+ return skb->len;
+}
+
+static int ip_vs_genl_parse_service(struct ip_vs_service_user *usvc,
+ struct nlattr *nla, int full_entry)
+{
+ struct nlattr *attrs[IPVS_SVC_ATTR_MAX + 1];
+ struct nlattr *nla_af, *nla_port, *nla_fwmark, *nla_protocol, *nla_addr;
+
+ /* Parse mandatory identifying service fields first */
+ if (nla == NULL ||
+ nla_parse_nested(attrs, IPVS_SVC_ATTR_MAX, nla, ip_vs_svc_policy))
+ return -EINVAL;
+
+ nla_af = attrs[IPVS_SVC_ATTR_AF];
+ nla_protocol = attrs[IPVS_SVC_ATTR_PROTOCOL];
+ nla_addr = attrs[IPVS_SVC_ATTR_ADDR];
+ nla_port = attrs[IPVS_SVC_ATTR_PORT];
+ nla_fwmark = attrs[IPVS_SVC_ATTR_FWMARK];
+
+ if (!(nla_af && (nla_fwmark || (nla_port && nla_protocol && nla_addr))))
+ return -EINVAL;
+
+ /* For now, only support IPv4 */
+ if (nla_get_u16(nla_af) != AF_INET)
+ return -EAFNOSUPPORT;
+
+ if (nla_fwmark) {
+ usvc->protocol = IPPROTO_TCP;
+ usvc->fwmark = nla_get_u32(nla_fwmark);
+ } else {
+ usvc->protocol = nla_get_u16(nla_protocol);
+ nla_memcpy(&usvc->addr, nla_addr, sizeof(usvc->addr));
+ usvc->port = nla_get_u16(nla_port);
+ usvc->fwmark = 0;
+ }
+
+ /* If a full entry was requested, check for the additional fields */
+ if (full_entry) {
+ struct nlattr *nla_sched, *nla_flags, *nla_timeout,
+ *nla_netmask;
+ struct ip_vs_flags flags;
+ struct ip_vs_service *svc;
+
+ nla_sched = attrs[IPVS_SVC_ATTR_SCHED_NAME];
+ nla_flags = attrs[IPVS_SVC_ATTR_FLAGS];
+ nla_timeout = attrs[IPVS_SVC_ATTR_TIMEOUT];
+ nla_netmask = attrs[IPVS_SVC_ATTR_NETMASK];
+
+ if (!(nla_sched && nla_flags && nla_timeout && nla_netmask))
+ return -EINVAL;
+
+ nla_memcpy(&flags, nla_flags, sizeof(flags));
+
+ /* prefill flags from service if it already exists */
+ if (usvc->fwmark)
+ svc = __ip_vs_svc_fwm_get(usvc->fwmark);
+ else
+ svc = __ip_vs_service_get(usvc->protocol, usvc->addr,
+ usvc->port);
+ if (svc) {
+ usvc->flags = svc->flags;
+ ip_vs_service_put(svc);
+ } else
+ usvc->flags = 0;
+
+ /* set new flags from userland */
+ usvc->flags = (usvc->flags & ~flags.mask) |
+ (flags.flags & flags.mask);
+
+ strlcpy(usvc->sched_name, nla_data(nla_sched),
+ sizeof(usvc->sched_name));
+ usvc->timeout = nla_get_u32(nla_timeout);
+ usvc->netmask = nla_get_u32(nla_netmask);
+ }
+
+ return 0;
+}
+
+static struct ip_vs_service *ip_vs_genl_find_service(struct nlattr *nla)
+{
+ struct ip_vs_service_user usvc;
+ int ret;
+
+ ret = ip_vs_genl_parse_service(&usvc, nla, 0);
+ if (ret)
+ return ERR_PTR(ret);
+
+ if (usvc.fwmark)
+ return __ip_vs_svc_fwm_get(usvc.fwmark);
+ else
+ return __ip_vs_service_get(usvc.protocol, usvc.addr,
+ usvc.port);
+}
+
+static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest)
+{
+ struct nlattr *nl_dest;
+
+ nl_dest = nla_nest_start(skb, IPVS_CMD_ATTR_DEST);
+ if (!nl_dest)
+ return -EMSGSIZE;
+
+ NLA_PUT(skb, IPVS_DEST_ATTR_ADDR, sizeof(dest->addr), &dest->addr);
+ NLA_PUT_U16(skb, IPVS_DEST_ATTR_PORT, dest->port);
+
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_FWD_METHOD,
+ atomic_read(&dest->conn_flags) & IP_VS_CONN_F_FWD_MASK);
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_WEIGHT, atomic_read(&dest->weight));
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_U_THRESH, dest->u_threshold);
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_L_THRESH, dest->l_threshold);
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_ACTIVE_CONNS,
+ atomic_read(&dest->activeconns));
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_INACT_CONNS,
+ atomic_read(&dest->inactconns));
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_PERSIST_CONNS,
+ atomic_read(&dest->persistconns));
+
+ if (ip_vs_genl_fill_stats(skb, IPVS_DEST_ATTR_STATS, &dest->stats))
+ goto nla_put_failure;
+
+ nla_nest_end(skb, nl_dest);
+
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(skb, nl_dest);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_dump_dest(struct sk_buff *skb, struct ip_vs_dest *dest,
+ struct netlink_callback *cb)
+{
+ void *hdr;
+
+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
+ &ip_vs_genl_family, NLM_F_MULTI,
+ IPVS_CMD_NEW_DEST);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ if (ip_vs_genl_fill_dest(skb, dest) < 0)
+ goto nla_put_failure;
+
+ return genlmsg_end(skb, hdr);
+
+nla_put_failure:
+ genlmsg_cancel(skb, hdr);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_dump_dests(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ int idx = 0;
+ int start = cb->args[0];
+ struct ip_vs_service *svc;
+ struct ip_vs_dest *dest;
+ struct nlattr *attrs[IPVS_CMD_ATTR_MAX + 1];
+
+ mutex_lock(&__ip_vs_mutex);
+
+ /* Try to find the service for which to dump destinations */
+ if (nlmsg_parse(cb->nlh, GENL_HDRLEN, attrs,
+ IPVS_CMD_ATTR_MAX, ip_vs_cmd_policy))
+ goto out_err;
+
+ svc = ip_vs_genl_find_service(attrs[IPVS_CMD_ATTR_SERVICE]);
+ if (IS_ERR(svc) || svc == NULL)
+ goto out_err;
+
+ /* Dump the destinations */
+ list_for_each_entry(dest, &svc->destinations, n_list) {
+ if (++idx <= start)
+ continue;
+ if (ip_vs_genl_dump_dest(skb, dest, cb) < 0) {
+ idx--;
+ goto nla_put_failure;
+ }
+ }
+
+nla_put_failure:
+ cb->args[0] = idx;
+ ip_vs_service_put(svc);
+
+out_err:
+ mutex_unlock(&__ip_vs_mutex);
+
+ return skb->len;
+}
+
+static int ip_vs_genl_parse_dest(struct ip_vs_dest_user *udest,
+ struct nlattr *nla, int full_entry)
+{
+ struct nlattr *attrs[IPVS_DEST_ATTR_MAX + 1];
+ struct nlattr *nla_addr, *nla_port;
+
+ /* Parse mandatory identifying destination fields first */
+ if (nla == NULL ||
+ nla_parse_nested(attrs, IPVS_DEST_ATTR_MAX, nla, ip_vs_dest_policy))
+ return -EINVAL;
+
+ nla_addr = attrs[IPVS_DEST_ATTR_ADDR];
+ nla_port = attrs[IPVS_DEST_ATTR_PORT];
+
+ if (!(nla_addr && nla_port))
+ return -EINVAL;
+
+ nla_memcpy(&udest->addr, nla_addr, sizeof(udest->addr));
+ udest->port = nla_get_u16(nla_port);
+
+ /* If a full entry was requested, check for the additional fields */
+ if (full_entry) {
+ struct nlattr *nla_fwd, *nla_weight, *nla_u_thresh,
+ *nla_l_thresh;
+
+ nla_fwd = attrs[IPVS_DEST_ATTR_FWD_METHOD];
+ nla_weight = attrs[IPVS_DEST_ATTR_WEIGHT];
+ nla_u_thresh = attrs[IPVS_DEST_ATTR_U_THRESH];
+ nla_l_thresh = attrs[IPVS_DEST_ATTR_L_THRESH];
+
+ if (!(nla_fwd && nla_weight && nla_u_thresh && nla_l_thresh))
+ return -EINVAL;
+
+ udest->conn_flags = nla_get_u32(nla_fwd)
+ & IP_VS_CONN_F_FWD_MASK;
+ udest->weight = nla_get_u32(nla_weight);
+ udest->u_threshold = nla_get_u32(nla_u_thresh);
+ udest->l_threshold = nla_get_u32(nla_l_thresh);
+ }
+
+ return 0;
+}
+
+static int ip_vs_genl_fill_daemon(struct sk_buff *skb, __be32 state,
+ const char *mcast_ifn, __be32 syncid)
+{
+ struct nlattr *nl_daemon;
+
+ nl_daemon = nla_nest_start(skb, IPVS_CMD_ATTR_DAEMON);
+ if (!nl_daemon)
+ return -EMSGSIZE;
+
+ NLA_PUT_U32(skb, IPVS_DAEMON_ATTR_STATE, state);
+ NLA_PUT_STRING(skb, IPVS_DAEMON_ATTR_MCAST_IFN, mcast_ifn);
+ NLA_PUT_U32(skb, IPVS_DAEMON_ATTR_SYNC_ID, syncid);
+
+ nla_nest_end(skb, nl_daemon);
+
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(skb, nl_daemon);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_dump_daemon(struct sk_buff *skb, __be32 state,
+ const char *mcast_ifn, __be32 syncid,
+ struct netlink_callback *cb)
+{
+ void *hdr;
+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
+ &ip_vs_genl_family, NLM_F_MULTI,
+ IPVS_CMD_NEW_DAEMON);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ if (ip_vs_genl_fill_daemon(skb, state, mcast_ifn, syncid))
+ goto nla_put_failure;
+
+ return genlmsg_end(skb, hdr);
+
+nla_put_failure:
+ genlmsg_cancel(skb, hdr);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_dump_daemons(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ mutex_lock(&__ip_vs_mutex);
+ if ((ip_vs_sync_state & IP_VS_STATE_MASTER) && !cb->args[0]) {
+ if (ip_vs_genl_dump_daemon(skb, IP_VS_STATE_MASTER,
+ ip_vs_master_mcast_ifn,
+ ip_vs_master_syncid, cb) < 0)
+ goto nla_put_failure;
+
+ cb->args[0] = 1;
+ }
+
+ if ((ip_vs_sync_state & IP_VS_STATE_BACKUP) && !cb->args[1]) {
+ if (ip_vs_genl_dump_daemon(skb, IP_VS_STATE_BACKUP,
+ ip_vs_backup_mcast_ifn,
+ ip_vs_backup_syncid, cb) < 0)
+ goto nla_put_failure;
+
+ cb->args[1] = 1;
+ }
+
+nla_put_failure:
+ mutex_unlock(&__ip_vs_mutex);
+
+ return skb->len;
+}
+
+static int ip_vs_genl_new_daemon(struct nlattr **attrs)
+{
+ if (!(attrs[IPVS_DAEMON_ATTR_STATE] &&
+ attrs[IPVS_DAEMON_ATTR_MCAST_IFN] &&
+ attrs[IPVS_DAEMON_ATTR_SYNC_ID]))
+ return -EINVAL;
+
+ return start_sync_thread(nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]),
+ nla_data(attrs[IPVS_DAEMON_ATTR_MCAST_IFN]),
+ nla_get_u32(attrs[IPVS_DAEMON_ATTR_SYNC_ID]));
+}
+
+static int ip_vs_genl_del_daemon(struct nlattr **attrs)
+{
+ if (!attrs[IPVS_DAEMON_ATTR_STATE])
+ return -EINVAL;
+
+ return stop_sync_thread(nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]));
+}
+
+static int ip_vs_genl_set_config(struct nlattr **attrs)
+{
+ struct ip_vs_timeout_user t;
+
+ __ip_vs_get_timeouts(&t);
+
+ if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP])
+ t.tcp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP]);
+
+ if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN])
+ t.tcp_fin_timeout =
+ nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN]);
+
+ if (attrs[IPVS_CMD_ATTR_TIMEOUT_UDP])
+ t.udp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_UDP]);
+
+ return ip_vs_set_timeout(&t);
+}
+
+static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info)
+{
+ struct ip_vs_service *svc;
+ struct ip_vs_service_user usvc;
+ struct ip_vs_dest_user udest;
+ int ret = 0, cmd;
+ int need_full_svc = 0, need_full_dest = 0;
+
+ cmd = info->genlhdr->cmd;
+
+ mutex_lock(&__ip_vs_mutex);
+
+ if (cmd == IPVS_CMD_FLUSH) {
+ ret = ip_vs_flush();
+ goto out;
+ } else if (cmd == IPVS_CMD_SET_CONFIG) {
+ ret = ip_vs_genl_set_config(info->attrs);
+ goto out;
+ } else if (cmd == IPVS_CMD_NEW_DAEMON ||
+ cmd == IPVS_CMD_DEL_DAEMON) {
+
+ struct nlattr *daemon_attrs[IPVS_DAEMON_ATTR_MAX + 1];
+
+ if (!info->attrs[IPVS_CMD_ATTR_DAEMON] ||
+ nla_parse_nested(daemon_attrs, IPVS_DAEMON_ATTR_MAX,
+ info->attrs[IPVS_CMD_ATTR_DAEMON],
+ ip_vs_daemon_policy)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (cmd == IPVS_CMD_NEW_DAEMON)
+ ret = ip_vs_genl_new_daemon(daemon_attrs);
+ else
+ ret = ip_vs_genl_del_daemon(daemon_attrs);
+ goto out;
+ } else if (cmd == IPVS_CMD_ZERO &&
+ !info->attrs[IPVS_CMD_ATTR_SERVICE]) {
+ ret = ip_vs_zero_all();
+ goto out;
+ }
+
+ /* All following commands require a service argument, so check if we
+ * received a valid one. We need a full service specification when
+ * adding / editing a service. Only identifying members otherwise. */
+ if (cmd == IPVS_CMD_NEW_SERVICE || cmd == IPVS_CMD_SET_SERVICE)
+ need_full_svc = 1;
+
+ ret = ip_vs_genl_parse_service(&usvc,
+ info->attrs[IPVS_CMD_ATTR_SERVICE],
+ need_full_svc);
+ if (ret)
+ goto out;
+
+ /* Lookup the exact service by <protocol, addr, port> or fwmark */
+ if (usvc.fwmark == 0)
+ svc = __ip_vs_service_get(usvc.protocol, usvc.addr, usvc.port);
+ else
+ svc = __ip_vs_svc_fwm_get(usvc.fwmark);
+
+ /* Unless we're adding a new service, the service must already exist */
+ if ((cmd != IPVS_CMD_NEW_SERVICE) && (svc == NULL)) {
+ ret = -ESRCH;
+ goto out;
+ }
+
+ /* Destination commands require a valid destination argument. For
+ * adding / editing a destination, we need a full destination
+ * specification. */
+ if (cmd == IPVS_CMD_NEW_DEST || cmd == IPVS_CMD_SET_DEST ||
+ cmd == IPVS_CMD_DEL_DEST) {
+ if (cmd != IPVS_CMD_DEL_DEST)
+ need_full_dest = 1;
+
+ ret = ip_vs_genl_parse_dest(&udest,
+ info->attrs[IPVS_CMD_ATTR_DEST],
+ need_full_dest);
+ if (ret)
+ goto out;
+ }
+
+ switch (cmd) {
+ case IPVS_CMD_NEW_SERVICE:
+ if (svc == NULL)
+ ret = ip_vs_add_service(&usvc, &svc);
+ else
+ ret = -EEXIST;
+ break;
+ case IPVS_CMD_SET_SERVICE:
+ ret = ip_vs_edit_service(svc, &usvc);
+ break;
+ case IPVS_CMD_DEL_SERVICE:
+ ret = ip_vs_del_service(svc);
+ break;
+ case IPVS_CMD_NEW_DEST:
+ ret = ip_vs_add_dest(svc, &udest);
+ break;
+ case IPVS_CMD_SET_DEST:
+ ret = ip_vs_edit_dest(svc, &udest);
+ break;
+ case IPVS_CMD_DEL_DEST:
+ ret = ip_vs_del_dest(svc, &udest);
+ break;
+ case IPVS_CMD_ZERO:
+ ret = ip_vs_zero_service(svc);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+out:
+ if (svc)
+ ip_vs_service_put(svc);
+ mutex_unlock(&__ip_vs_mutex);
+
+ return ret;
+}
+
+static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info)
+{
+ struct sk_buff *msg;
+ void *reply;
+ int ret, cmd, reply_cmd;
+
+ mutex_lock(&__ip_vs_mutex);
+
+ cmd = info->genlhdr->cmd;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg) {
+ ret = -ENOMEM;
+ goto out_err;
+ }
+
+ if (cmd == IPVS_CMD_GET_SERVICE)
+ reply_cmd = IPVS_CMD_NEW_SERVICE;
+ else if (cmd == IPVS_CMD_GET_INFO)
+ reply_cmd = IPVS_CMD_SET_INFO;
+ else if (cmd == IPVS_CMD_GET_CONFIG)
+ reply_cmd = IPVS_CMD_SET_CONFIG;
+ else {
+ IP_VS_ERR("unknown Generic Netlink command\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ reply = genlmsg_put_reply(msg, info, &ip_vs_genl_family, 0, reply_cmd);
+ if (reply == NULL)
+ goto nla_put_failure;
+
+ switch (cmd) {
+ case IPVS_CMD_GET_SERVICE:
+ {
+ struct ip_vs_service *svc;
+
+ svc = ip_vs_genl_find_service(info->attrs[IPVS_CMD_ATTR_SERVICE]);
+ if (IS_ERR(svc)) {
+ ret = PTR_ERR(svc);
+ goto out_err;
+ } else if (svc) {
+ ret = ip_vs_genl_fill_service(msg, svc);
+ ip_vs_service_put(svc);
+ if (ret)
+ goto nla_put_failure;
+ } else {
+ ret = -ESRCH;
+ goto out_err;
+ }
+
+ break;
+ }
+
+ case IPVS_CMD_GET_CONFIG:
+ {
+ struct ip_vs_timeout_user t;
+
+ __ip_vs_get_timeouts(&t);
+#ifdef CONFIG_IP_VS_PROTO_TCP
+ NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP, t.tcp_timeout);
+ NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP_FIN,
+ t.tcp_fin_timeout);
+#endif
+#ifdef CONFIG_IP_VS_PROTO_UDP
+ NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_UDP, t.udp_timeout);
+#endif
+
+ break;
+ }
+
+ case IPVS_CMD_GET_INFO:
+ NLA_PUT_U32(msg, IPVS_INFO_ATTR_VERSION, IP_VS_VERSION_CODE);
+ NLA_PUT_U32(msg, IPVS_INFO_ATTR_CONN_TAB_SIZE,
+ IP_VS_CONN_TAB_SIZE);
+ break;
+ }
+
+ genlmsg_end(msg, reply);
+ ret = genlmsg_unicast(msg, info->snd_pid);
+ goto out;
+
+nla_put_failure:
+ IP_VS_ERR("not enough space in Netlink message\n");
+ ret = -EMSGSIZE;
+
+out_err:
+ if (msg)
+ nlmsg_free(msg);
+out:
+ mutex_unlock(&__ip_vs_mutex);
+
+ return ret;
+}
+
+
+static struct genl_ops ip_vs_genl_ops[] __read_mostly = {
+ /* SET commands */
+ {
+ .cmd = IPVS_CMD_NEW_SERVICE,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_SET_SERVICE,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_DEL_SERVICE,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_GET_SERVICE,
+ .flags = GENL_ADMIN_PERM,
+ .doit = ip_vs_genl_get_cmd,
+ .dumpit = ip_vs_genl_dump_services,
+ .policy = ip_vs_cmd_policy,
+ },
+ {
+ .cmd = IPVS_CMD_NEW_DEST,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_SET_DEST,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_DEL_DEST,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_GET_DEST,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .dumpit = ip_vs_genl_dump_dests,
+ },
+ {
+ .cmd = IPVS_CMD_NEW_DAEMON,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_DEL_DAEMON,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_GET_DAEMON,
+ .flags = GENL_ADMIN_PERM,
+ .dumpit = ip_vs_genl_dump_daemons,
+ },
+ {
+ .cmd = IPVS_CMD_SET_CONFIG,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_GET_CONFIG,
+ .flags = GENL_ADMIN_PERM,
+ .doit = ip_vs_genl_get_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_GET_INFO,
+ .flags = GENL_ADMIN_PERM,
+ .doit = ip_vs_genl_get_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_ZERO,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_FLUSH,
+ .flags = GENL_ADMIN_PERM,
+ .doit = ip_vs_genl_set_cmd,
+ },
+};
+
+int ip_vs_genl_register(void)
+{
+ int ret, i;
+
+ ret = genl_register_family(&ip_vs_genl_family);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(ip_vs_genl_ops); i++) {
+ ret = genl_register_ops(&ip_vs_genl_family, &ip_vs_genl_ops[i]);
+ if (ret)
+ goto err_out;
+ }
+ return 0;
+
+err_out:
+ genl_unregister_family(&ip_vs_genl_family);
+ return ret;
+}
+
+void ip_vs_genl_unregister(void)
+{
+ genl_unregister_family(&ip_vs_genl_family);
+}
+
+/* End of Generic Netlink interface definitions */
+
int ip_vs_control_init(void)
{
@@ -2319,6 +3191,13 @@ int ip_vs_control_init(void)
return ret;
}
+ ret = ip_vs_genl_register();
+ if (ret) {
+ IP_VS_ERR("cannot register Generic Netlink interface.\n");
+ nf_unregister_sockopt(&ip_vs_sockopts);
+ return ret;
+ }
+
proc_net_fops_create(&init_net, "ip_vs", 0, &ip_vs_info_fops);
proc_net_fops_create(&init_net, "ip_vs_stats",0, &ip_vs_stats_fops);
@@ -2355,6 +3234,7 @@ void ip_vs_control_cleanup(void)
unregister_sysctl_table(sysctl_header);
proc_net_remove(&init_net, "ip_vs_stats");
proc_net_remove(&init_net, "ip_vs");
+ ip_vs_genl_unregister();
nf_unregister_sockopt(&ip_vs_sockopts);
LeaveFunction(2);
}
--
1.5.4.5
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCHv3 0/2] IPVS: Add Generic Netlink configuration interface
2008-08-07 14:43 [PATCHv3 0/2] IPVS: Add Generic Netlink configuration interface Julius Volz
2008-08-07 14:43 ` [PATCHv3 1/2] IPVS: Add genetlink interface definitions to ip_vs.h Julius Volz
2008-08-07 14:43 ` [PATCHv3 2/2] IPVS: Add genetlink interface implementation Julius Volz
@ 2008-08-08 2:26 ` Simon Horman
2008-08-08 12:06 ` Julius Volz
2 siblings, 1 reply; 23+ messages in thread
From: Simon Horman @ 2008-08-08 2:26 UTC (permalink / raw)
To: Julius Volz; +Cc: julius.volz, netdev, lvs-devel, kaber, davem, tgraf, vbusam
On Thu, Aug 07, 2008 at 04:43:37PM +0200, Julius Volz wrote:
> This is the third iteration of the IPVS Netlink interface, this time
> with only a small fix for a typo found by Thomas Graf. If there are no
> further major issues, can this be applied?
>
> The two patches add a Generic Netlink interface to IPVS while keeping
> the old get/setsockopt interface for userspace backwards compatibility.
> The motivation for this is to have a more extensible interface for
> future changes, such as the planned IPv6 support.
>
> An ipvsadm that already uses the new interface is available here:
>
> http://sixpak.org/vince/google/ipvsadm/
> (by Vince Busam)
>
> Old ipvsadms continue to work with this change.
Hi Julius,
these patches seem fine to me, however I am still seeing an oops
when using the new ipvsadm. Are we sure that this bug is in generic
code?
# ipvsadm -A -t 10.4.0.132:80
# ipvsadm -C
BUG: unable to handle kernel NULL pointer dereference at 00000028
IP: [<c021f034>] ip_vs_genl_set_cmd+0x254/0x494
*pde = 00000000
Oops: 0000 [#1]
Pid: 33, comm: ipvsadm Not tainted (2.6.27-rc2-kexec-11901-ge6fce5b-dirty #13)
EIP: 0060:[<c021f034>] EFLAGS: 00000206 CPU: 0
EIP is at ip_vs_genl_set_cmd+0x254/0x494
EAX: 00000014 EBX: 00000000 ECX: c7401300 EDX: c02e3018
ESI: 00000000 EDI: 00000011 EBP: c746dce4 ESP: c746dc20
DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 0068
Process ipvsadm (pid: 33, ti=c746c000 task=c6f22700 task.ti=c746c000)
Stack: 00000021 c0289340 c0289364 c0289374 c6faf088 00001102 c6faf0b4 c6faf058
00000003 c0289340 00000001 00000000 c02e0968 c01e948e 00000002 c6feb960
00000001 c6f84160 c6feb960 00000010 00000000 00000246 c01e4c57 00000246
Call Trace:
[<c01e948e>] ctrl_dumpfamily+0x86/0xd4
[<c01e4c57>] sk_filter+0x67/0x7c
[<c01e693d>] netlink_dump+0x15d/0x1a4
[<c01e9858>] genl_rcv_msg+0x1dc/0x204
[<c01e967c>] genl_rcv_msg+0x0/0x204
[<c01e89d2>] netlink_rcv_skb+0x72/0x80
[<c01e9671>] genl_rcv+0x19/0x24
[<c01e84d2>] netlink_unicast+0x232/0x284
[<c01e86dd>] netlink_sendmsg+0x1b9/0x288
[<c01d0d3b>] sock_sendmsg+0xb7/0xe0
[<c0120adc>] autoremove_wake_function+0x0/0x30
[<c0120adc>] autoremove_wake_function+0x0/0x30
[<c013a383>] __do_fault+0x15b/0x340
[<c01d7832>] verify_iovec+0x2a/0x88
[<c01d0e9f>] sys_sendmsg+0x13b/0x230
[<c01d184a>] sys_recvmsg+0x1b6/0x240
[<c01d1665>] move_addr_to_user+0x45/0x74
[<c01d1b50>] sys_getsockname+0x94/0xa0
[<c01d2a09>] release_sock+0x9/0x70
[<c01d3bca>] sock_setsockopt+0x106/0x638
[<c01d0a41>] sock_attach_fd+0x65/0xa0
[<c01d1e88>] sys_socketcall+0x80/0x1d8
[<c0103d42>] syscall_call+0x7/0xb
=======================
Code: 8b 40 04 89 44 24 1c 8b 47 18 85 c0 74 07 8b 40 04 89 44 24 20 8d 44 24 18 e8 09 ca ff ff 89 c3 8b 84 24 88 00 00 00 85 c0 74 03 <ff> 48 14 b8 f0 90 28 c0 e8 e3 a5 00 00 89 d8 81 c4 8c 00 00 00
EIP: [<c021f034>] ip_vs_genl_set_cmd+0x254/0x494 SS:ESP 0068:c746dc20
---[ end trace d371c2e324a230e1 ]---
Segmentation fault
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCHv3 2/2] IPVS: Add genetlink interface implementation
2008-08-07 14:43 ` [PATCHv3 2/2] IPVS: Add genetlink interface implementation Julius Volz
@ 2008-08-08 11:29 ` Julius Volz
2008-08-13 21:51 ` Sven Wegener
2008-08-14 5:39 ` Sven Wegener
0 siblings, 2 replies; 23+ messages in thread
From: Julius Volz @ 2008-08-08 11:29 UTC (permalink / raw)
To: netdev, lvs-devel; +Cc: horms, kaber, davem, tgraf, vbusam
This still had two bugs:
- policies for IPVS_DEST_ATTR_FWD_METHOD and IPVS_SVC_ATTR_FLAGS
were swapped
- svc not initialized to NULL at the beginning of ip_vs_genl_set_cmd()
The version below fixes this:
----
Add the implementation of the new Generic Netlink interface to IPVS and
keep the old set/getsockopt interface for userspace backwards
compatibility.
Signed-off-by: Julius Volz <juliusv@google.com>
1 files changed, 880 insertions(+), 0 deletions(-)
diff --git a/net/ipv4/ipvs/ip_vs_ctl.c b/net/ipv4/ipvs/ip_vs_ctl.c
index 9a5ace0..8038420 100644
--- a/net/ipv4/ipvs/ip_vs_ctl.c
+++ b/net/ipv4/ipvs/ip_vs_ctl.c
@@ -37,6 +37,7 @@
#include <net/ip.h>
#include <net/route.h>
#include <net/sock.h>
+#include <net/genetlink.h>
#include <asm/uaccess.h>
@@ -2305,6 +2306,877 @@ static struct nf_sockopt_ops ip_vs_sockopts = {
.owner = THIS_MODULE,
};
+/*
+ * Generic Netlink interface
+ */
+
+/* IPVS genetlink family */
+static struct genl_family ip_vs_genl_family = {
+ .id = GENL_ID_GENERATE,
+ .hdrsize = 0,
+ .name = IPVS_GENL_NAME,
+ .version = IPVS_GENL_VERSION,
+ .maxattr = IPVS_CMD_MAX,
+};
+
+/* Policy used for first-level command attributes */
+static const struct nla_policy ip_vs_cmd_policy[IPVS_CMD_ATTR_MAX + 1] = {
+ [IPVS_CMD_ATTR_SERVICE] = { .type = NLA_NESTED },
+ [IPVS_CMD_ATTR_DEST] = { .type = NLA_NESTED },
+ [IPVS_CMD_ATTR_DAEMON] = { .type = NLA_NESTED },
+ [IPVS_CMD_ATTR_TIMEOUT_TCP] = { .type = NLA_U32 },
+ [IPVS_CMD_ATTR_TIMEOUT_TCP_FIN] = { .type = NLA_U32 },
+ [IPVS_CMD_ATTR_TIMEOUT_UDP] = { .type = NLA_U32 },
+};
+
+/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_DAEMON */
+static const struct nla_policy ip_vs_daemon_policy[IPVS_DAEMON_ATTR_MAX + 1] = {
+ [IPVS_DAEMON_ATTR_STATE] = { .type = NLA_U32 },
+ [IPVS_DAEMON_ATTR_MCAST_IFN] = { .type = NLA_NUL_STRING,
+ .len = IP_VS_IFNAME_MAXLEN },
+ [IPVS_DAEMON_ATTR_SYNC_ID] = { .type = NLA_U32 },
+};
+
+/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_SERVICE */
+static const struct nla_policy ip_vs_svc_policy[IPVS_SVC_ATTR_MAX + 1] = {
+ [IPVS_SVC_ATTR_AF] = { .type = NLA_U16 },
+ [IPVS_SVC_ATTR_PROTOCOL] = { .type = NLA_U16 },
+ [IPVS_SVC_ATTR_ADDR] = { .type = NLA_BINARY,
+ .len = sizeof(union nf_inet_addr) },
+ [IPVS_SVC_ATTR_PORT] = { .type = NLA_U16 },
+ [IPVS_SVC_ATTR_FWMARK] = { .type = NLA_U32 },
+ [IPVS_SVC_ATTR_SCHED_NAME] = { .type = NLA_NUL_STRING,
+ .len = IP_VS_SCHEDNAME_MAXLEN },
+ [IPVS_SVC_ATTR_FLAGS] = { .type = NLA_BINARY,
+ .len = sizeof(struct ip_vs_flags) },
+ [IPVS_SVC_ATTR_TIMEOUT] = { .type = NLA_U32 },
+ [IPVS_SVC_ATTR_NETMASK] = { .type = NLA_U32 },
+ [IPVS_SVC_ATTR_STATS] = { .type = NLA_NESTED },
+};
+
+/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_DEST */
+static const struct nla_policy ip_vs_dest_policy[IPVS_DEST_ATTR_MAX + 1] = {
+ [IPVS_DEST_ATTR_ADDR] = { .type = NLA_BINARY,
+ .len = sizeof(union nf_inet_addr) },
+ [IPVS_DEST_ATTR_PORT] = { .type = NLA_U16 },
+ [IPVS_DEST_ATTR_FWD_METHOD] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_WEIGHT] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_U_THRESH] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_L_THRESH] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_ACTIVE_CONNS] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_INACT_CONNS] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_PERSIST_CONNS] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_STATS] = { .type = NLA_NESTED },
+};
+
+static int ip_vs_genl_fill_stats(struct sk_buff *skb, int container_type,
+ struct ip_vs_stats *stats)
+{
+ struct nlattr *nl_stats = nla_nest_start(skb, container_type);
+ if (!nl_stats)
+ return -EMSGSIZE;
+
+ spin_lock_bh(&stats->lock);
+
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_CONNS, stats->conns);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_INPKTS, stats->inpkts);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTPKTS, stats->outpkts);
+ NLA_PUT_U64(skb, IPVS_STATS_ATTR_INBYTES, stats->inbytes);
+ NLA_PUT_U64(skb, IPVS_STATS_ATTR_OUTBYTES, stats->outbytes);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_CPS, stats->cps);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_INPPS, stats->inpps);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTPPS, stats->outpps);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_INBPS, stats->inbps);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTBPS, stats->outbps);
+
+ spin_unlock_bh(&stats->lock);
+
+ nla_nest_end(skb, nl_stats);
+
+ return 0;
+
+nla_put_failure:
+ spin_unlock_bh(&stats->lock);
+ nla_nest_cancel(skb, nl_stats);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_fill_service(struct sk_buff *skb,
+ struct ip_vs_service *svc)
+{
+ struct nlattr *nl_service;
+ struct ip_vs_flags flags = { .flags = svc->flags,
+ .mask = ~0 };
+
+ nl_service = nla_nest_start(skb, IPVS_CMD_ATTR_SERVICE);
+ if (!nl_service)
+ return -EMSGSIZE;
+
+ NLA_PUT_U16(skb, IPVS_SVC_ATTR_AF, AF_INET);
+
+ if (svc->fwmark) {
+ NLA_PUT_U32(skb, IPVS_SVC_ATTR_FWMARK, svc->fwmark);
+ } else {
+ NLA_PUT_U16(skb, IPVS_SVC_ATTR_PROTOCOL, svc->protocol);
+ NLA_PUT(skb, IPVS_SVC_ATTR_ADDR, sizeof(svc->addr), &svc->addr);
+ NLA_PUT_U16(skb, IPVS_SVC_ATTR_PORT, svc->port);
+ }
+
+ NLA_PUT_STRING(skb, IPVS_SVC_ATTR_SCHED_NAME, svc->scheduler->name);
+ NLA_PUT(skb, IPVS_SVC_ATTR_FLAGS, sizeof(flags), &flags);
+ NLA_PUT_U32(skb, IPVS_SVC_ATTR_TIMEOUT, svc->timeout / HZ);
+ NLA_PUT_U32(skb, IPVS_SVC_ATTR_NETMASK, svc->netmask);
+
+ if (ip_vs_genl_fill_stats(skb, IPVS_SVC_ATTR_STATS, &svc->stats))
+ goto nla_put_failure;
+
+ nla_nest_end(skb, nl_service);
+
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(skb, nl_service);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_dump_service(struct sk_buff *skb,
+ struct ip_vs_service *svc,
+ struct netlink_callback *cb)
+{
+ void *hdr;
+
+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
+ &ip_vs_genl_family, NLM_F_MULTI,
+ IPVS_CMD_NEW_SERVICE);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ if (ip_vs_genl_fill_service(skb, svc) < 0)
+ goto nla_put_failure;
+
+ return genlmsg_end(skb, hdr);
+
+nla_put_failure:
+ genlmsg_cancel(skb, hdr);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_dump_services(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ int idx = 0, i;
+ int start = cb->args[0];
+ struct ip_vs_service *svc;
+
+ mutex_lock(&__ip_vs_mutex);
+ for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) {
+ list_for_each_entry(svc, &ip_vs_svc_table[i], s_list) {
+ if (++idx <= start)
+ continue;
+ if (ip_vs_genl_dump_service(skb, svc, cb) < 0) {
+ idx--;
+ goto nla_put_failure;
+ }
+ }
+ }
+
+ for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) {
+ list_for_each_entry(svc, &ip_vs_svc_fwm_table[i], f_list) {
+ if (++idx <= start)
+ continue;
+ if (ip_vs_genl_dump_service(skb, svc, cb) < 0) {
+ idx--;
+ goto nla_put_failure;
+ }
+ }
+ }
+
+nla_put_failure:
+ mutex_unlock(&__ip_vs_mutex);
+ cb->args[0] = idx;
+
+ return skb->len;
+}
+
+static int ip_vs_genl_parse_service(struct ip_vs_service_user *usvc,
+ struct nlattr *nla, int full_entry)
+{
+ struct nlattr *attrs[IPVS_SVC_ATTR_MAX + 1];
+ struct nlattr *nla_af, *nla_port, *nla_fwmark, *nla_protocol, *nla_addr;
+
+ /* Parse mandatory identifying service fields first */
+ if (nla == NULL ||
+ nla_parse_nested(attrs, IPVS_SVC_ATTR_MAX, nla, ip_vs_svc_policy))
+ return -EINVAL;
+
+ nla_af = attrs[IPVS_SVC_ATTR_AF];
+ nla_protocol = attrs[IPVS_SVC_ATTR_PROTOCOL];
+ nla_addr = attrs[IPVS_SVC_ATTR_ADDR];
+ nla_port = attrs[IPVS_SVC_ATTR_PORT];
+ nla_fwmark = attrs[IPVS_SVC_ATTR_FWMARK];
+
+ if (!(nla_af && (nla_fwmark || (nla_port && nla_protocol && nla_addr))))
+ return -EINVAL;
+
+ /* For now, only support IPv4 */
+ if (nla_get_u16(nla_af) != AF_INET)
+ return -EAFNOSUPPORT;
+
+ if (nla_fwmark) {
+ usvc->protocol = IPPROTO_TCP;
+ usvc->fwmark = nla_get_u32(nla_fwmark);
+ } else {
+ usvc->protocol = nla_get_u16(nla_protocol);
+ nla_memcpy(&usvc->addr, nla_addr, sizeof(usvc->addr));
+ usvc->port = nla_get_u16(nla_port);
+ usvc->fwmark = 0;
+ }
+
+ /* If a full entry was requested, check for the additional fields */
+ if (full_entry) {
+ struct nlattr *nla_sched, *nla_flags, *nla_timeout,
+ *nla_netmask;
+ struct ip_vs_flags flags;
+ struct ip_vs_service *svc;
+
+ nla_sched = attrs[IPVS_SVC_ATTR_SCHED_NAME];
+ nla_flags = attrs[IPVS_SVC_ATTR_FLAGS];
+ nla_timeout = attrs[IPVS_SVC_ATTR_TIMEOUT];
+ nla_netmask = attrs[IPVS_SVC_ATTR_NETMASK];
+
+ if (!(nla_sched && nla_flags && nla_timeout && nla_netmask))
+ return -EINVAL;
+
+ nla_memcpy(&flags, nla_flags, sizeof(flags));
+
+ /* prefill flags from service if it already exists */
+ if (usvc->fwmark)
+ svc = __ip_vs_svc_fwm_get(usvc->fwmark);
+ else
+ svc = __ip_vs_service_get(usvc->protocol, usvc->addr,
+ usvc->port);
+ if (svc) {
+ usvc->flags = svc->flags;
+ ip_vs_service_put(svc);
+ } else
+ usvc->flags = 0;
+
+ /* set new flags from userland */
+ usvc->flags = (usvc->flags & ~flags.mask) |
+ (flags.flags & flags.mask);
+
+ strlcpy(usvc->sched_name, nla_data(nla_sched),
+ sizeof(usvc->sched_name));
+ usvc->timeout = nla_get_u32(nla_timeout);
+ usvc->netmask = nla_get_u32(nla_netmask);
+ }
+
+ return 0;
+}
+
+static struct ip_vs_service *ip_vs_genl_find_service(struct nlattr *nla)
+{
+ struct ip_vs_service_user usvc;
+ int ret;
+
+ ret = ip_vs_genl_parse_service(&usvc, nla, 0);
+ if (ret)
+ return ERR_PTR(ret);
+
+ if (usvc.fwmark)
+ return __ip_vs_svc_fwm_get(usvc.fwmark);
+ else
+ return __ip_vs_service_get(usvc.protocol, usvc.addr,
+ usvc.port);
+}
+
+static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest)
+{
+ struct nlattr *nl_dest;
+
+ nl_dest = nla_nest_start(skb, IPVS_CMD_ATTR_DEST);
+ if (!nl_dest)
+ return -EMSGSIZE;
+
+ NLA_PUT(skb, IPVS_DEST_ATTR_ADDR, sizeof(dest->addr), &dest->addr);
+ NLA_PUT_U16(skb, IPVS_DEST_ATTR_PORT, dest->port);
+
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_FWD_METHOD,
+ atomic_read(&dest->conn_flags) & IP_VS_CONN_F_FWD_MASK);
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_WEIGHT, atomic_read(&dest->weight));
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_U_THRESH, dest->u_threshold);
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_L_THRESH, dest->l_threshold);
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_ACTIVE_CONNS,
+ atomic_read(&dest->activeconns));
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_INACT_CONNS,
+ atomic_read(&dest->inactconns));
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_PERSIST_CONNS,
+ atomic_read(&dest->persistconns));
+
+ if (ip_vs_genl_fill_stats(skb, IPVS_DEST_ATTR_STATS, &dest->stats))
+ goto nla_put_failure;
+
+ nla_nest_end(skb, nl_dest);
+
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(skb, nl_dest);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_dump_dest(struct sk_buff *skb, struct ip_vs_dest *dest,
+ struct netlink_callback *cb)
+{
+ void *hdr;
+
+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
+ &ip_vs_genl_family, NLM_F_MULTI,
+ IPVS_CMD_NEW_DEST);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ if (ip_vs_genl_fill_dest(skb, dest) < 0)
+ goto nla_put_failure;
+
+ return genlmsg_end(skb, hdr);
+
+nla_put_failure:
+ genlmsg_cancel(skb, hdr);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_dump_dests(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ int idx = 0;
+ int start = cb->args[0];
+ struct ip_vs_service *svc;
+ struct ip_vs_dest *dest;
+ struct nlattr *attrs[IPVS_CMD_ATTR_MAX + 1];
+
+ mutex_lock(&__ip_vs_mutex);
+
+ /* Try to find the service for which to dump destinations */
+ if (nlmsg_parse(cb->nlh, GENL_HDRLEN, attrs,
+ IPVS_CMD_ATTR_MAX, ip_vs_cmd_policy))
+ goto out_err;
+
+ svc = ip_vs_genl_find_service(attrs[IPVS_CMD_ATTR_SERVICE]);
+ if (IS_ERR(svc) || svc == NULL)
+ goto out_err;
+
+ /* Dump the destinations */
+ list_for_each_entry(dest, &svc->destinations, n_list) {
+ if (++idx <= start)
+ continue;
+ if (ip_vs_genl_dump_dest(skb, dest, cb) < 0) {
+ idx--;
+ goto nla_put_failure;
+ }
+ }
+
+nla_put_failure:
+ cb->args[0] = idx;
+ ip_vs_service_put(svc);
+
+out_err:
+ mutex_unlock(&__ip_vs_mutex);
+
+ return skb->len;
+}
+
+static int ip_vs_genl_parse_dest(struct ip_vs_dest_user *udest,
+ struct nlattr *nla, int full_entry)
+{
+ struct nlattr *attrs[IPVS_DEST_ATTR_MAX + 1];
+ struct nlattr *nla_addr, *nla_port;
+
+ /* Parse mandatory identifying destination fields first */
+ if (nla == NULL ||
+ nla_parse_nested(attrs, IPVS_DEST_ATTR_MAX, nla, ip_vs_dest_policy))
+ return -EINVAL;
+
+ nla_addr = attrs[IPVS_DEST_ATTR_ADDR];
+ nla_port = attrs[IPVS_DEST_ATTR_PORT];
+
+ if (!(nla_addr && nla_port))
+ return -EINVAL;
+
+ nla_memcpy(&udest->addr, nla_addr, sizeof(udest->addr));
+ udest->port = nla_get_u16(nla_port);
+
+ /* If a full entry was requested, check for the additional fields */
+ if (full_entry) {
+ struct nlattr *nla_fwd, *nla_weight, *nla_u_thresh,
+ *nla_l_thresh;
+
+ nla_fwd = attrs[IPVS_DEST_ATTR_FWD_METHOD];
+ nla_weight = attrs[IPVS_DEST_ATTR_WEIGHT];
+ nla_u_thresh = attrs[IPVS_DEST_ATTR_U_THRESH];
+ nla_l_thresh = attrs[IPVS_DEST_ATTR_L_THRESH];
+
+ if (!(nla_fwd && nla_weight && nla_u_thresh && nla_l_thresh))
+ return -EINVAL;
+
+ udest->conn_flags = nla_get_u32(nla_fwd)
+ & IP_VS_CONN_F_FWD_MASK;
+ udest->weight = nla_get_u32(nla_weight);
+ udest->u_threshold = nla_get_u32(nla_u_thresh);
+ udest->l_threshold = nla_get_u32(nla_l_thresh);
+ }
+
+ return 0;
+}
+
+static int ip_vs_genl_fill_daemon(struct sk_buff *skb, __be32 state,
+ const char *mcast_ifn, __be32 syncid)
+{
+ struct nlattr *nl_daemon;
+
+ nl_daemon = nla_nest_start(skb, IPVS_CMD_ATTR_DAEMON);
+ if (!nl_daemon)
+ return -EMSGSIZE;
+
+ NLA_PUT_U32(skb, IPVS_DAEMON_ATTR_STATE, state);
+ NLA_PUT_STRING(skb, IPVS_DAEMON_ATTR_MCAST_IFN, mcast_ifn);
+ NLA_PUT_U32(skb, IPVS_DAEMON_ATTR_SYNC_ID, syncid);
+
+ nla_nest_end(skb, nl_daemon);
+
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(skb, nl_daemon);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_dump_daemon(struct sk_buff *skb, __be32 state,
+ const char *mcast_ifn, __be32 syncid,
+ struct netlink_callback *cb)
+{
+ void *hdr;
+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
+ &ip_vs_genl_family, NLM_F_MULTI,
+ IPVS_CMD_NEW_DAEMON);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ if (ip_vs_genl_fill_daemon(skb, state, mcast_ifn, syncid))
+ goto nla_put_failure;
+
+ return genlmsg_end(skb, hdr);
+
+nla_put_failure:
+ genlmsg_cancel(skb, hdr);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_dump_daemons(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ mutex_lock(&__ip_vs_mutex);
+ if ((ip_vs_sync_state & IP_VS_STATE_MASTER) && !cb->args[0]) {
+ if (ip_vs_genl_dump_daemon(skb, IP_VS_STATE_MASTER,
+ ip_vs_master_mcast_ifn,
+ ip_vs_master_syncid, cb) < 0)
+ goto nla_put_failure;
+
+ cb->args[0] = 1;
+ }
+
+ if ((ip_vs_sync_state & IP_VS_STATE_BACKUP) && !cb->args[1]) {
+ if (ip_vs_genl_dump_daemon(skb, IP_VS_STATE_BACKUP,
+ ip_vs_backup_mcast_ifn,
+ ip_vs_backup_syncid, cb) < 0)
+ goto nla_put_failure;
+
+ cb->args[1] = 1;
+ }
+
+nla_put_failure:
+ mutex_unlock(&__ip_vs_mutex);
+
+ return skb->len;
+}
+
+static int ip_vs_genl_new_daemon(struct nlattr **attrs)
+{
+ if (!(attrs[IPVS_DAEMON_ATTR_STATE] &&
+ attrs[IPVS_DAEMON_ATTR_MCAST_IFN] &&
+ attrs[IPVS_DAEMON_ATTR_SYNC_ID]))
+ return -EINVAL;
+
+ return start_sync_thread(nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]),
+ nla_data(attrs[IPVS_DAEMON_ATTR_MCAST_IFN]),
+ nla_get_u32(attrs[IPVS_DAEMON_ATTR_SYNC_ID]));
+}
+
+static int ip_vs_genl_del_daemon(struct nlattr **attrs)
+{
+ if (!attrs[IPVS_DAEMON_ATTR_STATE])
+ return -EINVAL;
+
+ return stop_sync_thread(nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]));
+}
+
+static int ip_vs_genl_set_config(struct nlattr **attrs)
+{
+ struct ip_vs_timeout_user t;
+
+ __ip_vs_get_timeouts(&t);
+
+ if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP])
+ t.tcp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP]);
+
+ if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN])
+ t.tcp_fin_timeout =
+ nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN]);
+
+ if (attrs[IPVS_CMD_ATTR_TIMEOUT_UDP])
+ t.udp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_UDP]);
+
+ return ip_vs_set_timeout(&t);
+}
+
+static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info)
+{
+ struct ip_vs_service *svc = NULL;
+ struct ip_vs_service_user usvc;
+ struct ip_vs_dest_user udest;
+ int ret = 0, cmd;
+ int need_full_svc = 0, need_full_dest = 0;
+
+ cmd = info->genlhdr->cmd;
+
+ mutex_lock(&__ip_vs_mutex);
+
+ if (cmd == IPVS_CMD_FLUSH) {
+ ret = ip_vs_flush();
+ goto out;
+ } else if (cmd == IPVS_CMD_SET_CONFIG) {
+ ret = ip_vs_genl_set_config(info->attrs);
+ goto out;
+ } else if (cmd == IPVS_CMD_NEW_DAEMON ||
+ cmd == IPVS_CMD_DEL_DAEMON) {
+
+ struct nlattr *daemon_attrs[IPVS_DAEMON_ATTR_MAX + 1];
+
+ if (!info->attrs[IPVS_CMD_ATTR_DAEMON] ||
+ nla_parse_nested(daemon_attrs, IPVS_DAEMON_ATTR_MAX,
+ info->attrs[IPVS_CMD_ATTR_DAEMON],
+ ip_vs_daemon_policy)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (cmd == IPVS_CMD_NEW_DAEMON)
+ ret = ip_vs_genl_new_daemon(daemon_attrs);
+ else
+ ret = ip_vs_genl_del_daemon(daemon_attrs);
+ goto out;
+ } else if (cmd == IPVS_CMD_ZERO &&
+ !info->attrs[IPVS_CMD_ATTR_SERVICE]) {
+ ret = ip_vs_zero_all();
+ goto out;
+ }
+
+ /* All following commands require a service argument, so check if we
+ * received a valid one. We need a full service specification when
+ * adding / editing a service. Only identifying members otherwise. */
+ if (cmd == IPVS_CMD_NEW_SERVICE || cmd == IPVS_CMD_SET_SERVICE)
+ need_full_svc = 1;
+
+ ret = ip_vs_genl_parse_service(&usvc,
+ info->attrs[IPVS_CMD_ATTR_SERVICE],
+ need_full_svc);
+ if (ret)
+ goto out;
+
+ /* Lookup the exact service by <protocol, addr, port> or fwmark */
+ if (usvc.fwmark == 0)
+ svc = __ip_vs_service_get(usvc.protocol, usvc.addr, usvc.port);
+ else
+ svc = __ip_vs_svc_fwm_get(usvc.fwmark);
+
+ /* Unless we're adding a new service, the service must already exist */
+ if ((cmd != IPVS_CMD_NEW_SERVICE) && (svc == NULL)) {
+ ret = -ESRCH;
+ goto out;
+ }
+
+ /* Destination commands require a valid destination argument. For
+ * adding / editing a destination, we need a full destination
+ * specification. */
+ if (cmd == IPVS_CMD_NEW_DEST || cmd == IPVS_CMD_SET_DEST ||
+ cmd == IPVS_CMD_DEL_DEST) {
+ if (cmd != IPVS_CMD_DEL_DEST)
+ need_full_dest = 1;
+
+ ret = ip_vs_genl_parse_dest(&udest,
+ info->attrs[IPVS_CMD_ATTR_DEST],
+ need_full_dest);
+ if (ret)
+ goto out;
+ }
+
+ switch (cmd) {
+ case IPVS_CMD_NEW_SERVICE:
+ if (svc == NULL)
+ ret = ip_vs_add_service(&usvc, &svc);
+ else
+ ret = -EEXIST;
+ break;
+ case IPVS_CMD_SET_SERVICE:
+ ret = ip_vs_edit_service(svc, &usvc);
+ break;
+ case IPVS_CMD_DEL_SERVICE:
+ ret = ip_vs_del_service(svc);
+ break;
+ case IPVS_CMD_NEW_DEST:
+ ret = ip_vs_add_dest(svc, &udest);
+ break;
+ case IPVS_CMD_SET_DEST:
+ ret = ip_vs_edit_dest(svc, &udest);
+ break;
+ case IPVS_CMD_DEL_DEST:
+ ret = ip_vs_del_dest(svc, &udest);
+ break;
+ case IPVS_CMD_ZERO:
+ ret = ip_vs_zero_service(svc);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+out:
+ if (svc)
+ ip_vs_service_put(svc);
+ mutex_unlock(&__ip_vs_mutex);
+
+ return ret;
+}
+
+static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info)
+{
+ struct sk_buff *msg;
+ void *reply;
+ int ret, cmd, reply_cmd;
+
+ mutex_lock(&__ip_vs_mutex);
+
+ cmd = info->genlhdr->cmd;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg) {
+ ret = -ENOMEM;
+ goto out_err;
+ }
+
+ if (cmd == IPVS_CMD_GET_SERVICE)
+ reply_cmd = IPVS_CMD_NEW_SERVICE;
+ else if (cmd == IPVS_CMD_GET_INFO)
+ reply_cmd = IPVS_CMD_SET_INFO;
+ else if (cmd == IPVS_CMD_GET_CONFIG)
+ reply_cmd = IPVS_CMD_SET_CONFIG;
+ else {
+ IP_VS_ERR("unknown Generic Netlink command\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ reply = genlmsg_put_reply(msg, info, &ip_vs_genl_family, 0, reply_cmd);
+ if (reply == NULL)
+ goto nla_put_failure;
+
+ switch (cmd) {
+ case IPVS_CMD_GET_SERVICE:
+ {
+ struct ip_vs_service *svc;
+
+ svc = ip_vs_genl_find_service(info->attrs[IPVS_CMD_ATTR_SERVICE]);
+ if (IS_ERR(svc)) {
+ ret = PTR_ERR(svc);
+ goto out_err;
+ } else if (svc) {
+ ret = ip_vs_genl_fill_service(msg, svc);
+ ip_vs_service_put(svc);
+ if (ret)
+ goto nla_put_failure;
+ } else {
+ ret = -ESRCH;
+ goto out_err;
+ }
+
+ break;
+ }
+
+ case IPVS_CMD_GET_CONFIG:
+ {
+ struct ip_vs_timeout_user t;
+
+ __ip_vs_get_timeouts(&t);
+#ifdef CONFIG_IP_VS_PROTO_TCP
+ NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP, t.tcp_timeout);
+ NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP_FIN,
+ t.tcp_fin_timeout);
+#endif
+#ifdef CONFIG_IP_VS_PROTO_UDP
+ NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_UDP, t.udp_timeout);
+#endif
+
+ break;
+ }
+
+ case IPVS_CMD_GET_INFO:
+ NLA_PUT_U32(msg, IPVS_INFO_ATTR_VERSION, IP_VS_VERSION_CODE);
+ NLA_PUT_U32(msg, IPVS_INFO_ATTR_CONN_TAB_SIZE,
+ IP_VS_CONN_TAB_SIZE);
+ break;
+ }
+
+ genlmsg_end(msg, reply);
+ ret = genlmsg_unicast(msg, info->snd_pid);
+ goto out;
+
+nla_put_failure:
+ IP_VS_ERR("not enough space in Netlink message\n");
+ ret = -EMSGSIZE;
+
+out_err:
+ if (msg)
+ nlmsg_free(msg);
+out:
+ mutex_unlock(&__ip_vs_mutex);
+
+ return ret;
+}
+
+
+static struct genl_ops ip_vs_genl_ops[] __read_mostly = {
+ /* SET commands */
+ {
+ .cmd = IPVS_CMD_NEW_SERVICE,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_SET_SERVICE,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_DEL_SERVICE,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_GET_SERVICE,
+ .flags = GENL_ADMIN_PERM,
+ .doit = ip_vs_genl_get_cmd,
+ .dumpit = ip_vs_genl_dump_services,
+ .policy = ip_vs_cmd_policy,
+ },
+ {
+ .cmd = IPVS_CMD_NEW_DEST,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_SET_DEST,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_DEL_DEST,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_GET_DEST,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .dumpit = ip_vs_genl_dump_dests,
+ },
+ {
+ .cmd = IPVS_CMD_NEW_DAEMON,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_DEL_DAEMON,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_GET_DAEMON,
+ .flags = GENL_ADMIN_PERM,
+ .dumpit = ip_vs_genl_dump_daemons,
+ },
+ {
+ .cmd = IPVS_CMD_SET_CONFIG,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_GET_CONFIG,
+ .flags = GENL_ADMIN_PERM,
+ .doit = ip_vs_genl_get_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_GET_INFO,
+ .flags = GENL_ADMIN_PERM,
+ .doit = ip_vs_genl_get_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_ZERO,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_FLUSH,
+ .flags = GENL_ADMIN_PERM,
+ .doit = ip_vs_genl_set_cmd,
+ },
+};
+
+int ip_vs_genl_register(void)
+{
+ int ret, i;
+
+ ret = genl_register_family(&ip_vs_genl_family);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(ip_vs_genl_ops); i++) {
+ ret = genl_register_ops(&ip_vs_genl_family, &ip_vs_genl_ops[i]);
+ if (ret)
+ goto err_out;
+ }
+ return 0;
+
+err_out:
+ genl_unregister_family(&ip_vs_genl_family);
+ return ret;
+}
+
+void ip_vs_genl_unregister(void)
+{
+ genl_unregister_family(&ip_vs_genl_family);
+}
+
+/* End of Generic Netlink interface definitions */
+
int ip_vs_control_init(void)
{
@@ -2319,6 +3191,13 @@ int ip_vs_control_init(void)
return ret;
}
+ ret = ip_vs_genl_register();
+ if (ret) {
+ IP_VS_ERR("cannot register Generic Netlink interface.\n");
+ nf_unregister_sockopt(&ip_vs_sockopts);
+ return ret;
+ }
+
proc_net_fops_create(&init_net, "ip_vs", 0, &ip_vs_info_fops);
proc_net_fops_create(&init_net, "ip_vs_stats",0, &ip_vs_stats_fops);
@@ -2355,6 +3234,7 @@ void ip_vs_control_cleanup(void)
unregister_sysctl_table(sysctl_header);
proc_net_remove(&init_net, "ip_vs_stats");
proc_net_remove(&init_net, "ip_vs");
+ ip_vs_genl_unregister();
nf_unregister_sockopt(&ip_vs_sockopts);
LeaveFunction(2);
}
--
1.5.4.5
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCHv3 0/2] IPVS: Add Generic Netlink configuration interface
2008-08-08 2:26 ` [PATCHv3 0/2] IPVS: Add Generic Netlink configuration interface Simon Horman
@ 2008-08-08 12:06 ` Julius Volz
2008-08-09 14:23 ` Simon Horman
0 siblings, 1 reply; 23+ messages in thread
From: Julius Volz @ 2008-08-08 12:06 UTC (permalink / raw)
To: Simon Horman; +Cc: julius.volz, netdev, lvs-devel, kaber, davem, tgraf, vbusam
On Fri, Aug 08, 2008 at 12:26:09PM +1000, Simon Horman wrote:
> these patches seem fine to me, however I am still seeing an oops
> when using the new ipvsadm. Are we sure that this bug is in generic
> code?
>
> # ipvsadm -A -t 10.4.0.132:80
> # ipvsadm -C
> BUG: unable to handle kernel NULL pointer dereference at 00000028
> IP: [<c021f034>] ip_vs_genl_set_cmd+0x254/0x494
> *pde = 00000000
> Oops: 0000 [#1]
Ah, that made me find a bug where svc wasn't initialized to NULL at the
beginning of ip_vs_genl_set_cmd(). I'm not sure though if that was the
problem or if it still is some general genl issue. I just posted a newer
version version in response to my earlier post, could you give it a try?
Best with this updated ipvsadm:
http://www-user.tu-chemnitz.de/~volz/ipvsadm-1.25-nl-3.tar.gz
(later also at http://sixpak.org/vince/google/ipvsadm/)
Thanks,
Julius
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCHv3 0/2] IPVS: Add Generic Netlink configuration interface
2008-08-08 12:06 ` Julius Volz
@ 2008-08-09 14:23 ` Simon Horman
2008-08-09 19:57 ` Julius Volz
2008-08-13 16:00 ` Julius Volz
0 siblings, 2 replies; 23+ messages in thread
From: Simon Horman @ 2008-08-09 14:23 UTC (permalink / raw)
To: Julius Volz; +Cc: julius.volz, netdev, lvs-devel, kaber, davem, tgraf, vbusam
On Fri, Aug 08, 2008 at 02:06:43PM +0200, Julius Volz wrote:
> On Fri, Aug 08, 2008 at 12:26:09PM +1000, Simon Horman wrote:
> > these patches seem fine to me, however I am still seeing an oops
> > when using the new ipvsadm. Are we sure that this bug is in generic
> > code?
> >
> > # ipvsadm -A -t 10.4.0.132:80
> > # ipvsadm -C
> > BUG: unable to handle kernel NULL pointer dereference at 00000028
> > IP: [<c021f034>] ip_vs_genl_set_cmd+0x254/0x494
> > *pde = 00000000
> > Oops: 0000 [#1]
>
> Ah, that made me find a bug where svc wasn't initialized to NULL at the
> beginning of ip_vs_genl_set_cmd(). I'm not sure though if that was the
> problem or if it still is some general genl issue. I just posted a newer
> version version in response to my earlier post, could you give it a try?
>
> Best with this updated ipvsadm:
> http://www-user.tu-chemnitz.de/~volz/ipvsadm-1.25-nl-3.tar.gz
> (later also at http://sixpak.org/vince/google/ipvsadm/)
Hi Julius,
your latest patch does resolve the problem that I was seeing
with ipvsadm-1.25-nl-2. ipvsadm-1.25-nl-3 also works :-)
I'd like to try and stress it out a bit more.
I will try and get to that tomorrow or on Monday.
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCHv3 0/2] IPVS: Add Generic Netlink configuration interface
2008-08-09 14:23 ` Simon Horman
@ 2008-08-09 19:57 ` Julius Volz
2008-08-13 16:00 ` Julius Volz
1 sibling, 0 replies; 23+ messages in thread
From: Julius Volz @ 2008-08-09 19:57 UTC (permalink / raw)
To: Simon Horman; +Cc: julius.volz, netdev, lvs-devel, kaber, davem, tgraf, vbusam
On Sat, Aug 9, 2008 at 4:23 PM, Simon Horman <horms@verge.net.au> wrote:
> your latest patch does resolve the problem that I was seeing
> with ipvsadm-1.25-nl-2. ipvsadm-1.25-nl-3 also works :-)
Nice!
> I'd like to try and stress it out a bit more.
> I will try and get to that tomorrow or on Monday.
Great, thank you!
Julius
--
Google Switzerland GmbH
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCHv3 0/2] IPVS: Add Generic Netlink configuration interface
2008-08-09 14:23 ` Simon Horman
2008-08-09 19:57 ` Julius Volz
@ 2008-08-13 16:00 ` Julius Volz
2008-08-13 23:09 ` Simon Horman
1 sibling, 1 reply; 23+ messages in thread
From: Julius Volz @ 2008-08-13 16:00 UTC (permalink / raw)
To: Simon Horman; +Cc: julius.volz, netdev, lvs-devel, kaber, davem, tgraf, vbusam
On Sat, Aug 9, 2008 at 4:23 PM, Simon Horman <horms@verge.net.au> wrote:
> Hi Julius,
>
> your latest patch does resolve the problem that I was seeing
> with ipvsadm-1.25-nl-2. ipvsadm-1.25-nl-3 also works :-)
>
> I'd like to try and stress it out a bit more.
> I will try and get to that tomorrow or on Monday.
Hi, you were probably occupied by the other IPVS patches. Is there any
news on this?
Julius
--
Google Switzerland GmbH
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCHv3 2/2] IPVS: Add genetlink interface implementation
2008-08-08 11:29 ` Julius Volz
@ 2008-08-13 21:51 ` Sven Wegener
2008-08-13 21:53 ` Sven Wegener
2008-08-14 9:32 ` Julius Volz
2008-08-14 5:39 ` Sven Wegener
1 sibling, 2 replies; 23+ messages in thread
From: Sven Wegener @ 2008-08-13 21:51 UTC (permalink / raw)
To: Julius Volz; +Cc: netdev, lvs-devel, horms, kaber, davem, tgraf, vbusam
On Fri, 8 Aug 2008, Julius Volz wrote:
> This still had two bugs:
> - policies for IPVS_DEST_ATTR_FWD_METHOD and IPVS_SVC_ATTR_FLAGS
> were swapped
> - svc not initialized to NULL at the beginning of ip_vs_genl_set_cmd()
>
> The version below fixes this:
>
> ----
> Add the implementation of the new Generic Netlink interface to IPVS and
> keep the old set/getsockopt interface for userspace backwards
> compatibility.
>
> Signed-off-by: Julius Volz <juliusv@google.com>
>
> 1 files changed, 880 insertions(+), 0 deletions(-)
>
> diff --git a/net/ipv4/ipvs/ip_vs_ctl.c b/net/ipv4/ipvs/ip_vs_ctl.c
> index 9a5ace0..8038420 100644
> --- a/net/ipv4/ipvs/ip_vs_ctl.c
> +++ b/net/ipv4/ipvs/ip_vs_ctl.c
> @@ -37,6 +37,7 @@
> #include <net/ip.h>
> #include <net/route.h>
> #include <net/sock.h>
> +#include <net/genetlink.h>
>
> #include <asm/uaccess.h>
>
> @@ -2305,6 +2306,877 @@ static struct nf_sockopt_ops ip_vs_sockopts = {
> .owner = THIS_MODULE,
> };
>
> +/*
> + * Generic Netlink interface
> + */
> +
> +/* IPVS genetlink family */
> +static struct genl_family ip_vs_genl_family = {
> + .id = GENL_ID_GENERATE,
> + .hdrsize = 0,
> + .name = IPVS_GENL_NAME,
> + .version = IPVS_GENL_VERSION,
> + .maxattr = IPVS_CMD_MAX,
> +};
> +
> +/* Policy used for first-level command attributes */
> +static const struct nla_policy ip_vs_cmd_policy[IPVS_CMD_ATTR_MAX + 1] = {
> + [IPVS_CMD_ATTR_SERVICE] = { .type = NLA_NESTED },
> + [IPVS_CMD_ATTR_DEST] = { .type = NLA_NESTED },
> + [IPVS_CMD_ATTR_DAEMON] = { .type = NLA_NESTED },
> + [IPVS_CMD_ATTR_TIMEOUT_TCP] = { .type = NLA_U32 },
> + [IPVS_CMD_ATTR_TIMEOUT_TCP_FIN] = { .type = NLA_U32 },
> + [IPVS_CMD_ATTR_TIMEOUT_UDP] = { .type = NLA_U32 },
> +};
> +
> +/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_DAEMON */
> +static const struct nla_policy ip_vs_daemon_policy[IPVS_DAEMON_ATTR_MAX + 1] = {
> + [IPVS_DAEMON_ATTR_STATE] = { .type = NLA_U32 },
> + [IPVS_DAEMON_ATTR_MCAST_IFN] = { .type = NLA_NUL_STRING,
> + .len = IP_VS_IFNAME_MAXLEN },
> + [IPVS_DAEMON_ATTR_SYNC_ID] = { .type = NLA_U32 },
> +};
> +
> +/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_SERVICE */
> +static const struct nla_policy ip_vs_svc_policy[IPVS_SVC_ATTR_MAX + 1] = {
> + [IPVS_SVC_ATTR_AF] = { .type = NLA_U16 },
> + [IPVS_SVC_ATTR_PROTOCOL] = { .type = NLA_U16 },
> + [IPVS_SVC_ATTR_ADDR] = { .type = NLA_BINARY,
> + .len = sizeof(union nf_inet_addr) },
> + [IPVS_SVC_ATTR_PORT] = { .type = NLA_U16 },
> + [IPVS_SVC_ATTR_FWMARK] = { .type = NLA_U32 },
> + [IPVS_SVC_ATTR_SCHED_NAME] = { .type = NLA_NUL_STRING,
> + .len = IP_VS_SCHEDNAME_MAXLEN },
> + [IPVS_SVC_ATTR_FLAGS] = { .type = NLA_BINARY,
> + .len = sizeof(struct ip_vs_flags) },
> + [IPVS_SVC_ATTR_TIMEOUT] = { .type = NLA_U32 },
> + [IPVS_SVC_ATTR_NETMASK] = { .type = NLA_U32 },
> + [IPVS_SVC_ATTR_STATS] = { .type = NLA_NESTED },
> +};
> +
> +/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_DEST */
> +static const struct nla_policy ip_vs_dest_policy[IPVS_DEST_ATTR_MAX + 1] = {
> + [IPVS_DEST_ATTR_ADDR] = { .type = NLA_BINARY,
> + .len = sizeof(union nf_inet_addr) },
> + [IPVS_DEST_ATTR_PORT] = { .type = NLA_U16 },
> + [IPVS_DEST_ATTR_FWD_METHOD] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_WEIGHT] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_U_THRESH] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_L_THRESH] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_ACTIVE_CONNS] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_INACT_CONNS] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_PERSIST_CONNS] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_STATS] = { .type = NLA_NESTED },
> +};
> +
> +static int ip_vs_genl_fill_stats(struct sk_buff *skb, int container_type,
> + struct ip_vs_stats *stats)
> +{
> + struct nlattr *nl_stats = nla_nest_start(skb, container_type);
> + if (!nl_stats)
> + return -EMSGSIZE;
> +
> + spin_lock_bh(&stats->lock);
> +
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_CONNS, stats->conns);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_INPKTS, stats->inpkts);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTPKTS, stats->outpkts);
> + NLA_PUT_U64(skb, IPVS_STATS_ATTR_INBYTES, stats->inbytes);
> + NLA_PUT_U64(skb, IPVS_STATS_ATTR_OUTBYTES, stats->outbytes);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_CPS, stats->cps);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_INPPS, stats->inpps);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTPPS, stats->outpps);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_INBPS, stats->inbps);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTBPS, stats->outbps);
> +
> + spin_unlock_bh(&stats->lock);
> +
> + nla_nest_end(skb, nl_stats);
> +
> + return 0;
> +
> +nla_put_failure:
> + spin_unlock_bh(&stats->lock);
> + nla_nest_cancel(skb, nl_stats);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_fill_service(struct sk_buff *skb,
> + struct ip_vs_service *svc)
> +{
> + struct nlattr *nl_service;
> + struct ip_vs_flags flags = { .flags = svc->flags,
> + .mask = ~0 };
> +
> + nl_service = nla_nest_start(skb, IPVS_CMD_ATTR_SERVICE);
> + if (!nl_service)
> + return -EMSGSIZE;
> +
> + NLA_PUT_U16(skb, IPVS_SVC_ATTR_AF, AF_INET);
> +
> + if (svc->fwmark) {
> + NLA_PUT_U32(skb, IPVS_SVC_ATTR_FWMARK, svc->fwmark);
> + } else {
> + NLA_PUT_U16(skb, IPVS_SVC_ATTR_PROTOCOL, svc->protocol);
> + NLA_PUT(skb, IPVS_SVC_ATTR_ADDR, sizeof(svc->addr), &svc->addr);
> + NLA_PUT_U16(skb, IPVS_SVC_ATTR_PORT, svc->port);
> + }
> +
> + NLA_PUT_STRING(skb, IPVS_SVC_ATTR_SCHED_NAME, svc->scheduler->name);
> + NLA_PUT(skb, IPVS_SVC_ATTR_FLAGS, sizeof(flags), &flags);
> + NLA_PUT_U32(skb, IPVS_SVC_ATTR_TIMEOUT, svc->timeout / HZ);
> + NLA_PUT_U32(skb, IPVS_SVC_ATTR_NETMASK, svc->netmask);
> +
> + if (ip_vs_genl_fill_stats(skb, IPVS_SVC_ATTR_STATS, &svc->stats))
> + goto nla_put_failure;
> +
> + nla_nest_end(skb, nl_service);
> +
> + return 0;
> +
> +nla_put_failure:
> + nla_nest_cancel(skb, nl_service);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_dump_service(struct sk_buff *skb,
> + struct ip_vs_service *svc,
> + struct netlink_callback *cb)
> +{
> + void *hdr;
> +
> + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
> + &ip_vs_genl_family, NLM_F_MULTI,
> + IPVS_CMD_NEW_SERVICE);
> + if (!hdr)
> + return -EMSGSIZE;
> +
> + if (ip_vs_genl_fill_service(skb, svc) < 0)
> + goto nla_put_failure;
> +
> + return genlmsg_end(skb, hdr);
> +
> +nla_put_failure:
> + genlmsg_cancel(skb, hdr);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_dump_services(struct sk_buff *skb,
> + struct netlink_callback *cb)
> +{
> + int idx = 0, i;
> + int start = cb->args[0];
> + struct ip_vs_service *svc;
> +
> + mutex_lock(&__ip_vs_mutex);
> + for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) {
> + list_for_each_entry(svc, &ip_vs_svc_table[i], s_list) {
> + if (++idx <= start)
> + continue;
> + if (ip_vs_genl_dump_service(skb, svc, cb) < 0) {
> + idx--;
> + goto nla_put_failure;
> + }
> + }
> + }
> +
> + for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) {
> + list_for_each_entry(svc, &ip_vs_svc_fwm_table[i], f_list) {
> + if (++idx <= start)
> + continue;
> + if (ip_vs_genl_dump_service(skb, svc, cb) < 0) {
> + idx--;
> + goto nla_put_failure;
> + }
> + }
> + }
> +
> +nla_put_failure:
> + mutex_unlock(&__ip_vs_mutex);
> + cb->args[0] = idx;
> +
> + return skb->len;
> +}
> +
> +static int ip_vs_genl_parse_service(struct ip_vs_service_user *usvc,
> + struct nlattr *nla, int full_entry)
> +{
> + struct nlattr *attrs[IPVS_SVC_ATTR_MAX + 1];
> + struct nlattr *nla_af, *nla_port, *nla_fwmark, *nla_protocol, *nla_addr;
> +
> + /* Parse mandatory identifying service fields first */
> + if (nla == NULL ||
> + nla_parse_nested(attrs, IPVS_SVC_ATTR_MAX, nla, ip_vs_svc_policy))
> + return -EINVAL;
> +
> + nla_af = attrs[IPVS_SVC_ATTR_AF];
> + nla_protocol = attrs[IPVS_SVC_ATTR_PROTOCOL];
> + nla_addr = attrs[IPVS_SVC_ATTR_ADDR];
> + nla_port = attrs[IPVS_SVC_ATTR_PORT];
> + nla_fwmark = attrs[IPVS_SVC_ATTR_FWMARK];
> +
> + if (!(nla_af && (nla_fwmark || (nla_port && nla_protocol && nla_addr))))
> + return -EINVAL;
> +
> + /* For now, only support IPv4 */
> + if (nla_get_u16(nla_af) != AF_INET)
> + return -EAFNOSUPPORT;
> +
> + if (nla_fwmark) {
> + usvc->protocol = IPPROTO_TCP;
> + usvc->fwmark = nla_get_u32(nla_fwmark);
> + } else {
> + usvc->protocol = nla_get_u16(nla_protocol);
> + nla_memcpy(&usvc->addr, nla_addr, sizeof(usvc->addr));
> + usvc->port = nla_get_u16(nla_port);
> + usvc->fwmark = 0;
> + }
> +
> + /* If a full entry was requested, check for the additional fields */
> + if (full_entry) {
> + struct nlattr *nla_sched, *nla_flags, *nla_timeout,
> + *nla_netmask;
> + struct ip_vs_flags flags;
> + struct ip_vs_service *svc;
> +
> + nla_sched = attrs[IPVS_SVC_ATTR_SCHED_NAME];
> + nla_flags = attrs[IPVS_SVC_ATTR_FLAGS];
> + nla_timeout = attrs[IPVS_SVC_ATTR_TIMEOUT];
> + nla_netmask = attrs[IPVS_SVC_ATTR_NETMASK];
> +
> + if (!(nla_sched && nla_flags && nla_timeout && nla_netmask))
> + return -EINVAL;
> +
> + nla_memcpy(&flags, nla_flags, sizeof(flags));
> +
> + /* prefill flags from service if it already exists */
> + if (usvc->fwmark)
> + svc = __ip_vs_svc_fwm_get(usvc->fwmark);
> + else
> + svc = __ip_vs_service_get(usvc->protocol, usvc->addr,
> + usvc->port);
> + if (svc) {
> + usvc->flags = svc->flags;
> + ip_vs_service_put(svc);
> + } else
> + usvc->flags = 0;
> +
> + /* set new flags from userland */
> + usvc->flags = (usvc->flags & ~flags.mask) |
> + (flags.flags & flags.mask);
> +
> + strlcpy(usvc->sched_name, nla_data(nla_sched),
> + sizeof(usvc->sched_name));
> + usvc->timeout = nla_get_u32(nla_timeout);
> + usvc->netmask = nla_get_u32(nla_netmask);
> + }
> +
> + return 0;
> +}
> +
> +static struct ip_vs_service *ip_vs_genl_find_service(struct nlattr *nla)
> +{
> + struct ip_vs_service_user usvc;
> + int ret;
> +
> + ret = ip_vs_genl_parse_service(&usvc, nla, 0);
> + if (ret)
> + return ERR_PTR(ret);
> +
> + if (usvc.fwmark)
> + return __ip_vs_svc_fwm_get(usvc.fwmark);
> + else
> + return __ip_vs_service_get(usvc.protocol, usvc.addr,
> + usvc.port);
> +}
> +
> +static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest)
> +{
> + struct nlattr *nl_dest;
> +
> + nl_dest = nla_nest_start(skb, IPVS_CMD_ATTR_DEST);
> + if (!nl_dest)
> + return -EMSGSIZE;
> +
> + NLA_PUT(skb, IPVS_DEST_ATTR_ADDR, sizeof(dest->addr), &dest->addr);
> + NLA_PUT_U16(skb, IPVS_DEST_ATTR_PORT, dest->port);
> +
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_FWD_METHOD,
> + atomic_read(&dest->conn_flags) & IP_VS_CONN_F_FWD_MASK);
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_WEIGHT, atomic_read(&dest->weight));
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_U_THRESH, dest->u_threshold);
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_L_THRESH, dest->l_threshold);
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_ACTIVE_CONNS,
> + atomic_read(&dest->activeconns));
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_INACT_CONNS,
> + atomic_read(&dest->inactconns));
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_PERSIST_CONNS,
> + atomic_read(&dest->persistconns));
> +
> + if (ip_vs_genl_fill_stats(skb, IPVS_DEST_ATTR_STATS, &dest->stats))
> + goto nla_put_failure;
> +
> + nla_nest_end(skb, nl_dest);
> +
> + return 0;
> +
> +nla_put_failure:
> + nla_nest_cancel(skb, nl_dest);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_dump_dest(struct sk_buff *skb, struct ip_vs_dest *dest,
> + struct netlink_callback *cb)
> +{
> + void *hdr;
> +
> + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
> + &ip_vs_genl_family, NLM_F_MULTI,
> + IPVS_CMD_NEW_DEST);
> + if (!hdr)
> + return -EMSGSIZE;
> +
> + if (ip_vs_genl_fill_dest(skb, dest) < 0)
> + goto nla_put_failure;
> +
> + return genlmsg_end(skb, hdr);
> +
> +nla_put_failure:
> + genlmsg_cancel(skb, hdr);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_dump_dests(struct sk_buff *skb,
> + struct netlink_callback *cb)
> +{
> + int idx = 0;
> + int start = cb->args[0];
> + struct ip_vs_service *svc;
> + struct ip_vs_dest *dest;
> + struct nlattr *attrs[IPVS_CMD_ATTR_MAX + 1];
> +
> + mutex_lock(&__ip_vs_mutex);
> +
> + /* Try to find the service for which to dump destinations */
> + if (nlmsg_parse(cb->nlh, GENL_HDRLEN, attrs,
> + IPVS_CMD_ATTR_MAX, ip_vs_cmd_policy))
> + goto out_err;
> +
> + svc = ip_vs_genl_find_service(attrs[IPVS_CMD_ATTR_SERVICE]);
> + if (IS_ERR(svc) || svc == NULL)
> + goto out_err;
> +
> + /* Dump the destinations */
> + list_for_each_entry(dest, &svc->destinations, n_list) {
> + if (++idx <= start)
> + continue;
> + if (ip_vs_genl_dump_dest(skb, dest, cb) < 0) {
> + idx--;
> + goto nla_put_failure;
> + }
> + }
> +
> +nla_put_failure:
> + cb->args[0] = idx;
> + ip_vs_service_put(svc);
> +
> +out_err:
> + mutex_unlock(&__ip_vs_mutex);
> +
> + return skb->len;
> +}
> +
> +static int ip_vs_genl_parse_dest(struct ip_vs_dest_user *udest,
> + struct nlattr *nla, int full_entry)
> +{
> + struct nlattr *attrs[IPVS_DEST_ATTR_MAX + 1];
> + struct nlattr *nla_addr, *nla_port;
> +
> + /* Parse mandatory identifying destination fields first */
> + if (nla == NULL ||
> + nla_parse_nested(attrs, IPVS_DEST_ATTR_MAX, nla, ip_vs_dest_policy))
> + return -EINVAL;
> +
> + nla_addr = attrs[IPVS_DEST_ATTR_ADDR];
> + nla_port = attrs[IPVS_DEST_ATTR_PORT];
> +
> + if (!(nla_addr && nla_port))
> + return -EINVAL;
> +
> + nla_memcpy(&udest->addr, nla_addr, sizeof(udest->addr));
> + udest->port = nla_get_u16(nla_port);
> +
> + /* If a full entry was requested, check for the additional fields */
> + if (full_entry) {
> + struct nlattr *nla_fwd, *nla_weight, *nla_u_thresh,
> + *nla_l_thresh;
> +
> + nla_fwd = attrs[IPVS_DEST_ATTR_FWD_METHOD];
> + nla_weight = attrs[IPVS_DEST_ATTR_WEIGHT];
> + nla_u_thresh = attrs[IPVS_DEST_ATTR_U_THRESH];
> + nla_l_thresh = attrs[IPVS_DEST_ATTR_L_THRESH];
> +
> + if (!(nla_fwd && nla_weight && nla_u_thresh && nla_l_thresh))
> + return -EINVAL;
> +
> + udest->conn_flags = nla_get_u32(nla_fwd)
> + & IP_VS_CONN_F_FWD_MASK;
> + udest->weight = nla_get_u32(nla_weight);
> + udest->u_threshold = nla_get_u32(nla_u_thresh);
> + udest->l_threshold = nla_get_u32(nla_l_thresh);
> + }
> +
> + return 0;
> +}
> +
> +static int ip_vs_genl_fill_daemon(struct sk_buff *skb, __be32 state,
> + const char *mcast_ifn, __be32 syncid)
> +{
> + struct nlattr *nl_daemon;
> +
> + nl_daemon = nla_nest_start(skb, IPVS_CMD_ATTR_DAEMON);
> + if (!nl_daemon)
> + return -EMSGSIZE;
> +
> + NLA_PUT_U32(skb, IPVS_DAEMON_ATTR_STATE, state);
> + NLA_PUT_STRING(skb, IPVS_DAEMON_ATTR_MCAST_IFN, mcast_ifn);
> + NLA_PUT_U32(skb, IPVS_DAEMON_ATTR_SYNC_ID, syncid);
> +
> + nla_nest_end(skb, nl_daemon);
> +
> + return 0;
> +
> +nla_put_failure:
> + nla_nest_cancel(skb, nl_daemon);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_dump_daemon(struct sk_buff *skb, __be32 state,
> + const char *mcast_ifn, __be32 syncid,
> + struct netlink_callback *cb)
> +{
> + void *hdr;
> + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
> + &ip_vs_genl_family, NLM_F_MULTI,
> + IPVS_CMD_NEW_DAEMON);
> + if (!hdr)
> + return -EMSGSIZE;
> +
> + if (ip_vs_genl_fill_daemon(skb, state, mcast_ifn, syncid))
> + goto nla_put_failure;
> +
> + return genlmsg_end(skb, hdr);
> +
> +nla_put_failure:
> + genlmsg_cancel(skb, hdr);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_dump_daemons(struct sk_buff *skb,
> + struct netlink_callback *cb)
> +{
> + mutex_lock(&__ip_vs_mutex);
> + if ((ip_vs_sync_state & IP_VS_STATE_MASTER) && !cb->args[0]) {
> + if (ip_vs_genl_dump_daemon(skb, IP_VS_STATE_MASTER,
> + ip_vs_master_mcast_ifn,
> + ip_vs_master_syncid, cb) < 0)
> + goto nla_put_failure;
> +
> + cb->args[0] = 1;
> + }
> +
> + if ((ip_vs_sync_state & IP_VS_STATE_BACKUP) && !cb->args[1]) {
> + if (ip_vs_genl_dump_daemon(skb, IP_VS_STATE_BACKUP,
> + ip_vs_backup_mcast_ifn,
> + ip_vs_backup_syncid, cb) < 0)
> + goto nla_put_failure;
> +
> + cb->args[1] = 1;
> + }
> +
> +nla_put_failure:
> + mutex_unlock(&__ip_vs_mutex);
> +
> + return skb->len;
> +}
> +
> +static int ip_vs_genl_new_daemon(struct nlattr **attrs)
> +{
> + if (!(attrs[IPVS_DAEMON_ATTR_STATE] &&
> + attrs[IPVS_DAEMON_ATTR_MCAST_IFN] &&
> + attrs[IPVS_DAEMON_ATTR_SYNC_ID]))
> + return -EINVAL;
> +
> + return start_sync_thread(nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]),
> + nla_data(attrs[IPVS_DAEMON_ATTR_MCAST_IFN]),
> + nla_get_u32(attrs[IPVS_DAEMON_ATTR_SYNC_ID]));
> +}
> +
> +static int ip_vs_genl_del_daemon(struct nlattr **attrs)
> +{
> + if (!attrs[IPVS_DAEMON_ATTR_STATE])
> + return -EINVAL;
> +
> + return stop_sync_thread(nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]));
> +}
> +
> +static int ip_vs_genl_set_config(struct nlattr **attrs)
> +{
> + struct ip_vs_timeout_user t;
> +
> + __ip_vs_get_timeouts(&t);
> +
> + if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP])
> + t.tcp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP]);
> +
> + if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN])
> + t.tcp_fin_timeout =
> + nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN]);
> +
> + if (attrs[IPVS_CMD_ATTR_TIMEOUT_UDP])
> + t.udp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_UDP]);
> +
> + return ip_vs_set_timeout(&t);
> +}
> +
> +static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info)
> +{
> + struct ip_vs_service *svc = NULL;
> + struct ip_vs_service_user usvc;
> + struct ip_vs_dest_user udest;
> + int ret = 0, cmd;
> + int need_full_svc = 0, need_full_dest = 0;
> +
> + cmd = info->genlhdr->cmd;
> +
> + mutex_lock(&__ip_vs_mutex);
> +
> + if (cmd == IPVS_CMD_FLUSH) {
> + ret = ip_vs_flush();
> + goto out;
> + } else if (cmd == IPVS_CMD_SET_CONFIG) {
> + ret = ip_vs_genl_set_config(info->attrs);
> + goto out;
> + } else if (cmd == IPVS_CMD_NEW_DAEMON ||
> + cmd == IPVS_CMD_DEL_DAEMON) {
> +
> + struct nlattr *daemon_attrs[IPVS_DAEMON_ATTR_MAX + 1];
> +
> + if (!info->attrs[IPVS_CMD_ATTR_DAEMON] ||
> + nla_parse_nested(daemon_attrs, IPVS_DAEMON_ATTR_MAX,
> + info->attrs[IPVS_CMD_ATTR_DAEMON],
> + ip_vs_daemon_policy)) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + if (cmd == IPVS_CMD_NEW_DAEMON)
> + ret = ip_vs_genl_new_daemon(daemon_attrs);
> + else
> + ret = ip_vs_genl_del_daemon(daemon_attrs);
> + goto out;
> + } else if (cmd == IPVS_CMD_ZERO &&
> + !info->attrs[IPVS_CMD_ATTR_SERVICE]) {
> + ret = ip_vs_zero_all();
> + goto out;
> + }
> +
> + /* All following commands require a service argument, so check if we
> + * received a valid one. We need a full service specification when
> + * adding / editing a service. Only identifying members otherwise. */
> + if (cmd == IPVS_CMD_NEW_SERVICE || cmd == IPVS_CMD_SET_SERVICE)
> + need_full_svc = 1;
> +
> + ret = ip_vs_genl_parse_service(&usvc,
> + info->attrs[IPVS_CMD_ATTR_SERVICE],
> + need_full_svc);
> + if (ret)
> + goto out;
> +
> + /* Lookup the exact service by <protocol, addr, port> or fwmark */
> + if (usvc.fwmark == 0)
> + svc = __ip_vs_service_get(usvc.protocol, usvc.addr, usvc.port);
> + else
> + svc = __ip_vs_svc_fwm_get(usvc.fwmark);
> +
> + /* Unless we're adding a new service, the service must already exist */
> + if ((cmd != IPVS_CMD_NEW_SERVICE) && (svc == NULL)) {
> + ret = -ESRCH;
> + goto out;
> + }
> +
> + /* Destination commands require a valid destination argument. For
> + * adding / editing a destination, we need a full destination
> + * specification. */
> + if (cmd == IPVS_CMD_NEW_DEST || cmd == IPVS_CMD_SET_DEST ||
> + cmd == IPVS_CMD_DEL_DEST) {
> + if (cmd != IPVS_CMD_DEL_DEST)
> + need_full_dest = 1;
> +
> + ret = ip_vs_genl_parse_dest(&udest,
> + info->attrs[IPVS_CMD_ATTR_DEST],
> + need_full_dest);
> + if (ret)
> + goto out;
> + }
> +
> + switch (cmd) {
> + case IPVS_CMD_NEW_SERVICE:
> + if (svc == NULL)
> + ret = ip_vs_add_service(&usvc, &svc);
> + else
> + ret = -EEXIST;
> + break;
> + case IPVS_CMD_SET_SERVICE:
> + ret = ip_vs_edit_service(svc, &usvc);
> + break;
> + case IPVS_CMD_DEL_SERVICE:
> + ret = ip_vs_del_service(svc);
> + break;
> + case IPVS_CMD_NEW_DEST:
> + ret = ip_vs_add_dest(svc, &udest);
> + break;
> + case IPVS_CMD_SET_DEST:
> + ret = ip_vs_edit_dest(svc, &udest);
> + break;
> + case IPVS_CMD_DEL_DEST:
> + ret = ip_vs_del_dest(svc, &udest);
> + break;
> + case IPVS_CMD_ZERO:
> + ret = ip_vs_zero_service(svc);
> + break;
> + default:
> + ret = -EINVAL;
> + }
> +
> +out:
> + if (svc)
> + ip_vs_service_put(svc);
> + mutex_unlock(&__ip_vs_mutex);
> +
> + return ret;
> +}
> +
> +static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info)
> +{
> + struct sk_buff *msg;
> + void *reply;
> + int ret, cmd, reply_cmd;
> +
> + mutex_lock(&__ip_vs_mutex);
> +
> + cmd = info->genlhdr->cmd;
> +
> + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
> + if (!msg) {
> + ret = -ENOMEM;
> + goto out_err;
Here you want out...
> + }
> +
> + if (cmd == IPVS_CMD_GET_SERVICE)
> + reply_cmd = IPVS_CMD_NEW_SERVICE;
> + else if (cmd == IPVS_CMD_GET_INFO)
> + reply_cmd = IPVS_CMD_SET_INFO;
> + else if (cmd == IPVS_CMD_GET_CONFIG)
> + reply_cmd = IPVS_CMD_SET_CONFIG;
> + else {
> + IP_VS_ERR("unknown Generic Netlink command\n");
> + ret = -EINVAL;
> + goto out;
..and here you want out_error, to not leak msg.
> + }
> +
> + reply = genlmsg_put_reply(msg, info, &ip_vs_genl_family, 0, reply_cmd);
> + if (reply == NULL)
> + goto nla_put_failure;
> +
> + switch (cmd) {
> + case IPVS_CMD_GET_SERVICE:
> + {
> + struct ip_vs_service *svc;
> +
> + svc = ip_vs_genl_find_service(info->attrs[IPVS_CMD_ATTR_SERVICE]);
> + if (IS_ERR(svc)) {
> + ret = PTR_ERR(svc);
> + goto out_err;
> + } else if (svc) {
> + ret = ip_vs_genl_fill_service(msg, svc);
> + ip_vs_service_put(svc);
> + if (ret)
> + goto nla_put_failure;
> + } else {
> + ret = -ESRCH;
> + goto out_err;
> + }
> +
> + break;
> + }
> +
> + case IPVS_CMD_GET_CONFIG:
> + {
> + struct ip_vs_timeout_user t;
> +
> + __ip_vs_get_timeouts(&t);
> +#ifdef CONFIG_IP_VS_PROTO_TCP
> + NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP, t.tcp_timeout);
> + NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP_FIN,
> + t.tcp_fin_timeout);
> +#endif
> +#ifdef CONFIG_IP_VS_PROTO_UDP
> + NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_UDP, t.udp_timeout);
> +#endif
> +
> + break;
> + }
> +
> + case IPVS_CMD_GET_INFO:
> + NLA_PUT_U32(msg, IPVS_INFO_ATTR_VERSION, IP_VS_VERSION_CODE);
> + NLA_PUT_U32(msg, IPVS_INFO_ATTR_CONN_TAB_SIZE,
> + IP_VS_CONN_TAB_SIZE);
> + break;
> + }
> +
> + genlmsg_end(msg, reply);
> + ret = genlmsg_unicast(msg, info->snd_pid);
> + goto out;
> +
> +nla_put_failure:
> + IP_VS_ERR("not enough space in Netlink message\n");
> + ret = -EMSGSIZE;
> +
> +out_err:
> + if (msg)
> + nlmsg_free(msg);
> +out:
> + mutex_unlock(&__ip_vs_mutex);
> +
> + return ret;
> +}
> +
> +
> +static struct genl_ops ip_vs_genl_ops[] __read_mostly = {
> + /* SET commands */
> + {
> + .cmd = IPVS_CMD_NEW_SERVICE,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_SET_SERVICE,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_DEL_SERVICE,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_GET_SERVICE,
> + .flags = GENL_ADMIN_PERM,
> + .doit = ip_vs_genl_get_cmd,
> + .dumpit = ip_vs_genl_dump_services,
> + .policy = ip_vs_cmd_policy,
> + },
> + {
> + .cmd = IPVS_CMD_NEW_DEST,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_SET_DEST,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_DEL_DEST,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_GET_DEST,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .dumpit = ip_vs_genl_dump_dests,
> + },
> + {
> + .cmd = IPVS_CMD_NEW_DAEMON,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_DEL_DAEMON,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_GET_DAEMON,
> + .flags = GENL_ADMIN_PERM,
> + .dumpit = ip_vs_genl_dump_daemons,
> + },
> + {
> + .cmd = IPVS_CMD_SET_CONFIG,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_GET_CONFIG,
> + .flags = GENL_ADMIN_PERM,
> + .doit = ip_vs_genl_get_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_GET_INFO,
> + .flags = GENL_ADMIN_PERM,
> + .doit = ip_vs_genl_get_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_ZERO,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_FLUSH,
> + .flags = GENL_ADMIN_PERM,
> + .doit = ip_vs_genl_set_cmd,
> + },
> +};
> +
> +int ip_vs_genl_register(void)
static int __init
> +{
> + int ret, i;
> +
> + ret = genl_register_family(&ip_vs_genl_family);
> + if (ret)
> + return ret;
> +
> + for (i = 0; i < ARRAY_SIZE(ip_vs_genl_ops); i++) {
> + ret = genl_register_ops(&ip_vs_genl_family, &ip_vs_genl_ops[i]);
> + if (ret)
> + goto err_out;
> + }
> + return 0;
> +
> +err_out:
> + genl_unregister_family(&ip_vs_genl_family);
> + return ret;
> +}
> +
> +void ip_vs_genl_unregister(void)
static void
> +{
> + genl_unregister_family(&ip_vs_genl_family);
> +}
> +
> +/* End of Generic Netlink interface definitions */
> +
>
> int ip_vs_control_init(void)
> {
> @@ -2319,6 +3191,13 @@ int ip_vs_control_init(void)
> return ret;
> }
>
> + ret = ip_vs_genl_register();
> + if (ret) {
> + IP_VS_ERR("cannot register Generic Netlink interface.\n");
> + nf_unregister_sockopt(&ip_vs_sockopts);
> + return ret;
> + }
> +
> proc_net_fops_create(&init_net, "ip_vs", 0, &ip_vs_info_fops);
> proc_net_fops_create(&init_net, "ip_vs_stats",0, &ip_vs_stats_fops);
>
> @@ -2355,6 +3234,7 @@ void ip_vs_control_cleanup(void)
> unregister_sysctl_table(sysctl_header);
> proc_net_remove(&init_net, "ip_vs_stats");
> proc_net_remove(&init_net, "ip_vs");
> + ip_vs_genl_unregister();
> nf_unregister_sockopt(&ip_vs_sockopts);
> LeaveFunction(2);
> }
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCHv3 2/2] IPVS: Add genetlink interface implementation
2008-08-13 21:51 ` Sven Wegener
@ 2008-08-13 21:53 ` Sven Wegener
2008-08-14 9:32 ` Julius Volz
1 sibling, 0 replies; 23+ messages in thread
From: Sven Wegener @ 2008-08-13 21:53 UTC (permalink / raw)
To: Julius Volz; +Cc: netdev, lvs-devel, horms, kaber, davem, tgraf, vbusam
On Wed, 13 Aug 2008, Sven Wegener wrote:
> On Fri, 8 Aug 2008, Julius Volz wrote:
>
> > This still had two bugs:
> > - policies for IPVS_DEST_ATTR_FWD_METHOD and IPVS_SVC_ATTR_FLAGS
> > were swapped
> > - svc not initialized to NULL at the beginning of ip_vs_genl_set_cmd()
> >
> > The version below fixes this:
> >
> > ----
> > Add the implementation of the new Generic Netlink interface to IPVS and
> > keep the old set/getsockopt interface for userspace backwards
> > compatibility.
> >
> > Signed-off-by: Julius Volz <juliusv@google.com>
> >
> > 1 files changed, 880 insertions(+), 0 deletions(-)
> >
> > diff --git a/net/ipv4/ipvs/ip_vs_ctl.c b/net/ipv4/ipvs/ip_vs_ctl.c
> > index 9a5ace0..8038420 100644
> > --- a/net/ipv4/ipvs/ip_vs_ctl.c
> > +++ b/net/ipv4/ipvs/ip_vs_ctl.c
> > @@ -37,6 +37,7 @@
> > #include <net/ip.h>
> > #include <net/route.h>
> > #include <net/sock.h>
> > +#include <net/genetlink.h>
> >
> > #include <asm/uaccess.h>
> >
> > @@ -2305,6 +2306,877 @@ static struct nf_sockopt_ops ip_vs_sockopts = {
> > .owner = THIS_MODULE,
> > };
> >
> > +/*
> > + * Generic Netlink interface
> > + */
> > +
> > +/* IPVS genetlink family */
> > +static struct genl_family ip_vs_genl_family = {
> > + .id = GENL_ID_GENERATE,
> > + .hdrsize = 0,
> > + .name = IPVS_GENL_NAME,
> > + .version = IPVS_GENL_VERSION,
> > + .maxattr = IPVS_CMD_MAX,
> > +};
> > +
> > +/* Policy used for first-level command attributes */
> > +static const struct nla_policy ip_vs_cmd_policy[IPVS_CMD_ATTR_MAX + 1] = {
> > + [IPVS_CMD_ATTR_SERVICE] = { .type = NLA_NESTED },
> > + [IPVS_CMD_ATTR_DEST] = { .type = NLA_NESTED },
> > + [IPVS_CMD_ATTR_DAEMON] = { .type = NLA_NESTED },
> > + [IPVS_CMD_ATTR_TIMEOUT_TCP] = { .type = NLA_U32 },
> > + [IPVS_CMD_ATTR_TIMEOUT_TCP_FIN] = { .type = NLA_U32 },
> > + [IPVS_CMD_ATTR_TIMEOUT_UDP] = { .type = NLA_U32 },
> > +};
> > +
> > +/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_DAEMON */
> > +static const struct nla_policy ip_vs_daemon_policy[IPVS_DAEMON_ATTR_MAX + 1] = {
> > + [IPVS_DAEMON_ATTR_STATE] = { .type = NLA_U32 },
> > + [IPVS_DAEMON_ATTR_MCAST_IFN] = { .type = NLA_NUL_STRING,
> > + .len = IP_VS_IFNAME_MAXLEN },
> > + [IPVS_DAEMON_ATTR_SYNC_ID] = { .type = NLA_U32 },
> > +};
> > +
> > +/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_SERVICE */
> > +static const struct nla_policy ip_vs_svc_policy[IPVS_SVC_ATTR_MAX + 1] = {
> > + [IPVS_SVC_ATTR_AF] = { .type = NLA_U16 },
> > + [IPVS_SVC_ATTR_PROTOCOL] = { .type = NLA_U16 },
> > + [IPVS_SVC_ATTR_ADDR] = { .type = NLA_BINARY,
> > + .len = sizeof(union nf_inet_addr) },
> > + [IPVS_SVC_ATTR_PORT] = { .type = NLA_U16 },
> > + [IPVS_SVC_ATTR_FWMARK] = { .type = NLA_U32 },
> > + [IPVS_SVC_ATTR_SCHED_NAME] = { .type = NLA_NUL_STRING,
> > + .len = IP_VS_SCHEDNAME_MAXLEN },
> > + [IPVS_SVC_ATTR_FLAGS] = { .type = NLA_BINARY,
> > + .len = sizeof(struct ip_vs_flags) },
> > + [IPVS_SVC_ATTR_TIMEOUT] = { .type = NLA_U32 },
> > + [IPVS_SVC_ATTR_NETMASK] = { .type = NLA_U32 },
> > + [IPVS_SVC_ATTR_STATS] = { .type = NLA_NESTED },
> > +};
> > +
> > +/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_DEST */
> > +static const struct nla_policy ip_vs_dest_policy[IPVS_DEST_ATTR_MAX + 1] = {
> > + [IPVS_DEST_ATTR_ADDR] = { .type = NLA_BINARY,
> > + .len = sizeof(union nf_inet_addr) },
> > + [IPVS_DEST_ATTR_PORT] = { .type = NLA_U16 },
> > + [IPVS_DEST_ATTR_FWD_METHOD] = { .type = NLA_U32 },
> > + [IPVS_DEST_ATTR_WEIGHT] = { .type = NLA_U32 },
> > + [IPVS_DEST_ATTR_U_THRESH] = { .type = NLA_U32 },
> > + [IPVS_DEST_ATTR_L_THRESH] = { .type = NLA_U32 },
> > + [IPVS_DEST_ATTR_ACTIVE_CONNS] = { .type = NLA_U32 },
> > + [IPVS_DEST_ATTR_INACT_CONNS] = { .type = NLA_U32 },
> > + [IPVS_DEST_ATTR_PERSIST_CONNS] = { .type = NLA_U32 },
> > + [IPVS_DEST_ATTR_STATS] = { .type = NLA_NESTED },
> > +};
> > +
> > +static int ip_vs_genl_fill_stats(struct sk_buff *skb, int container_type,
> > + struct ip_vs_stats *stats)
> > +{
> > + struct nlattr *nl_stats = nla_nest_start(skb, container_type);
> > + if (!nl_stats)
> > + return -EMSGSIZE;
> > +
> > + spin_lock_bh(&stats->lock);
> > +
> > + NLA_PUT_U32(skb, IPVS_STATS_ATTR_CONNS, stats->conns);
> > + NLA_PUT_U32(skb, IPVS_STATS_ATTR_INPKTS, stats->inpkts);
> > + NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTPKTS, stats->outpkts);
> > + NLA_PUT_U64(skb, IPVS_STATS_ATTR_INBYTES, stats->inbytes);
> > + NLA_PUT_U64(skb, IPVS_STATS_ATTR_OUTBYTES, stats->outbytes);
> > + NLA_PUT_U32(skb, IPVS_STATS_ATTR_CPS, stats->cps);
> > + NLA_PUT_U32(skb, IPVS_STATS_ATTR_INPPS, stats->inpps);
> > + NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTPPS, stats->outpps);
> > + NLA_PUT_U32(skb, IPVS_STATS_ATTR_INBPS, stats->inbps);
> > + NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTBPS, stats->outbps);
> > +
> > + spin_unlock_bh(&stats->lock);
> > +
> > + nla_nest_end(skb, nl_stats);
> > +
> > + return 0;
> > +
> > +nla_put_failure:
> > + spin_unlock_bh(&stats->lock);
> > + nla_nest_cancel(skb, nl_stats);
> > + return -EMSGSIZE;
> > +}
> > +
> > +static int ip_vs_genl_fill_service(struct sk_buff *skb,
> > + struct ip_vs_service *svc)
> > +{
> > + struct nlattr *nl_service;
> > + struct ip_vs_flags flags = { .flags = svc->flags,
> > + .mask = ~0 };
> > +
> > + nl_service = nla_nest_start(skb, IPVS_CMD_ATTR_SERVICE);
> > + if (!nl_service)
> > + return -EMSGSIZE;
> > +
> > + NLA_PUT_U16(skb, IPVS_SVC_ATTR_AF, AF_INET);
> > +
> > + if (svc->fwmark) {
> > + NLA_PUT_U32(skb, IPVS_SVC_ATTR_FWMARK, svc->fwmark);
> > + } else {
> > + NLA_PUT_U16(skb, IPVS_SVC_ATTR_PROTOCOL, svc->protocol);
> > + NLA_PUT(skb, IPVS_SVC_ATTR_ADDR, sizeof(svc->addr), &svc->addr);
> > + NLA_PUT_U16(skb, IPVS_SVC_ATTR_PORT, svc->port);
> > + }
> > +
> > + NLA_PUT_STRING(skb, IPVS_SVC_ATTR_SCHED_NAME, svc->scheduler->name);
> > + NLA_PUT(skb, IPVS_SVC_ATTR_FLAGS, sizeof(flags), &flags);
> > + NLA_PUT_U32(skb, IPVS_SVC_ATTR_TIMEOUT, svc->timeout / HZ);
> > + NLA_PUT_U32(skb, IPVS_SVC_ATTR_NETMASK, svc->netmask);
> > +
> > + if (ip_vs_genl_fill_stats(skb, IPVS_SVC_ATTR_STATS, &svc->stats))
> > + goto nla_put_failure;
> > +
> > + nla_nest_end(skb, nl_service);
> > +
> > + return 0;
> > +
> > +nla_put_failure:
> > + nla_nest_cancel(skb, nl_service);
> > + return -EMSGSIZE;
> > +}
> > +
> > +static int ip_vs_genl_dump_service(struct sk_buff *skb,
> > + struct ip_vs_service *svc,
> > + struct netlink_callback *cb)
> > +{
> > + void *hdr;
> > +
> > + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
> > + &ip_vs_genl_family, NLM_F_MULTI,
> > + IPVS_CMD_NEW_SERVICE);
> > + if (!hdr)
> > + return -EMSGSIZE;
> > +
> > + if (ip_vs_genl_fill_service(skb, svc) < 0)
> > + goto nla_put_failure;
> > +
> > + return genlmsg_end(skb, hdr);
> > +
> > +nla_put_failure:
> > + genlmsg_cancel(skb, hdr);
> > + return -EMSGSIZE;
> > +}
> > +
> > +static int ip_vs_genl_dump_services(struct sk_buff *skb,
> > + struct netlink_callback *cb)
> > +{
> > + int idx = 0, i;
> > + int start = cb->args[0];
> > + struct ip_vs_service *svc;
> > +
> > + mutex_lock(&__ip_vs_mutex);
> > + for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) {
> > + list_for_each_entry(svc, &ip_vs_svc_table[i], s_list) {
> > + if (++idx <= start)
> > + continue;
> > + if (ip_vs_genl_dump_service(skb, svc, cb) < 0) {
> > + idx--;
> > + goto nla_put_failure;
> > + }
> > + }
> > + }
> > +
> > + for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) {
> > + list_for_each_entry(svc, &ip_vs_svc_fwm_table[i], f_list) {
> > + if (++idx <= start)
> > + continue;
> > + if (ip_vs_genl_dump_service(skb, svc, cb) < 0) {
> > + idx--;
> > + goto nla_put_failure;
> > + }
> > + }
> > + }
> > +
> > +nla_put_failure:
> > + mutex_unlock(&__ip_vs_mutex);
> > + cb->args[0] = idx;
> > +
> > + return skb->len;
> > +}
> > +
> > +static int ip_vs_genl_parse_service(struct ip_vs_service_user *usvc,
> > + struct nlattr *nla, int full_entry)
> > +{
> > + struct nlattr *attrs[IPVS_SVC_ATTR_MAX + 1];
> > + struct nlattr *nla_af, *nla_port, *nla_fwmark, *nla_protocol, *nla_addr;
> > +
> > + /* Parse mandatory identifying service fields first */
> > + if (nla == NULL ||
> > + nla_parse_nested(attrs, IPVS_SVC_ATTR_MAX, nla, ip_vs_svc_policy))
> > + return -EINVAL;
> > +
> > + nla_af = attrs[IPVS_SVC_ATTR_AF];
> > + nla_protocol = attrs[IPVS_SVC_ATTR_PROTOCOL];
> > + nla_addr = attrs[IPVS_SVC_ATTR_ADDR];
> > + nla_port = attrs[IPVS_SVC_ATTR_PORT];
> > + nla_fwmark = attrs[IPVS_SVC_ATTR_FWMARK];
> > +
> > + if (!(nla_af && (nla_fwmark || (nla_port && nla_protocol && nla_addr))))
> > + return -EINVAL;
> > +
> > + /* For now, only support IPv4 */
> > + if (nla_get_u16(nla_af) != AF_INET)
> > + return -EAFNOSUPPORT;
> > +
> > + if (nla_fwmark) {
> > + usvc->protocol = IPPROTO_TCP;
> > + usvc->fwmark = nla_get_u32(nla_fwmark);
> > + } else {
> > + usvc->protocol = nla_get_u16(nla_protocol);
> > + nla_memcpy(&usvc->addr, nla_addr, sizeof(usvc->addr));
> > + usvc->port = nla_get_u16(nla_port);
> > + usvc->fwmark = 0;
> > + }
> > +
> > + /* If a full entry was requested, check for the additional fields */
> > + if (full_entry) {
> > + struct nlattr *nla_sched, *nla_flags, *nla_timeout,
> > + *nla_netmask;
> > + struct ip_vs_flags flags;
> > + struct ip_vs_service *svc;
> > +
> > + nla_sched = attrs[IPVS_SVC_ATTR_SCHED_NAME];
> > + nla_flags = attrs[IPVS_SVC_ATTR_FLAGS];
> > + nla_timeout = attrs[IPVS_SVC_ATTR_TIMEOUT];
> > + nla_netmask = attrs[IPVS_SVC_ATTR_NETMASK];
> > +
> > + if (!(nla_sched && nla_flags && nla_timeout && nla_netmask))
> > + return -EINVAL;
> > +
> > + nla_memcpy(&flags, nla_flags, sizeof(flags));
> > +
> > + /* prefill flags from service if it already exists */
> > + if (usvc->fwmark)
> > + svc = __ip_vs_svc_fwm_get(usvc->fwmark);
> > + else
> > + svc = __ip_vs_service_get(usvc->protocol, usvc->addr,
> > + usvc->port);
> > + if (svc) {
> > + usvc->flags = svc->flags;
> > + ip_vs_service_put(svc);
> > + } else
> > + usvc->flags = 0;
> > +
> > + /* set new flags from userland */
> > + usvc->flags = (usvc->flags & ~flags.mask) |
> > + (flags.flags & flags.mask);
> > +
> > + strlcpy(usvc->sched_name, nla_data(nla_sched),
> > + sizeof(usvc->sched_name));
> > + usvc->timeout = nla_get_u32(nla_timeout);
> > + usvc->netmask = nla_get_u32(nla_netmask);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static struct ip_vs_service *ip_vs_genl_find_service(struct nlattr *nla)
> > +{
> > + struct ip_vs_service_user usvc;
> > + int ret;
> > +
> > + ret = ip_vs_genl_parse_service(&usvc, nla, 0);
> > + if (ret)
> > + return ERR_PTR(ret);
> > +
> > + if (usvc.fwmark)
> > + return __ip_vs_svc_fwm_get(usvc.fwmark);
> > + else
> > + return __ip_vs_service_get(usvc.protocol, usvc.addr,
> > + usvc.port);
> > +}
> > +
> > +static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest)
> > +{
> > + struct nlattr *nl_dest;
> > +
> > + nl_dest = nla_nest_start(skb, IPVS_CMD_ATTR_DEST);
> > + if (!nl_dest)
> > + return -EMSGSIZE;
> > +
> > + NLA_PUT(skb, IPVS_DEST_ATTR_ADDR, sizeof(dest->addr), &dest->addr);
> > + NLA_PUT_U16(skb, IPVS_DEST_ATTR_PORT, dest->port);
> > +
> > + NLA_PUT_U32(skb, IPVS_DEST_ATTR_FWD_METHOD,
> > + atomic_read(&dest->conn_flags) & IP_VS_CONN_F_FWD_MASK);
> > + NLA_PUT_U32(skb, IPVS_DEST_ATTR_WEIGHT, atomic_read(&dest->weight));
> > + NLA_PUT_U32(skb, IPVS_DEST_ATTR_U_THRESH, dest->u_threshold);
> > + NLA_PUT_U32(skb, IPVS_DEST_ATTR_L_THRESH, dest->l_threshold);
> > + NLA_PUT_U32(skb, IPVS_DEST_ATTR_ACTIVE_CONNS,
> > + atomic_read(&dest->activeconns));
> > + NLA_PUT_U32(skb, IPVS_DEST_ATTR_INACT_CONNS,
> > + atomic_read(&dest->inactconns));
> > + NLA_PUT_U32(skb, IPVS_DEST_ATTR_PERSIST_CONNS,
> > + atomic_read(&dest->persistconns));
> > +
> > + if (ip_vs_genl_fill_stats(skb, IPVS_DEST_ATTR_STATS, &dest->stats))
> > + goto nla_put_failure;
> > +
> > + nla_nest_end(skb, nl_dest);
> > +
> > + return 0;
> > +
> > +nla_put_failure:
> > + nla_nest_cancel(skb, nl_dest);
> > + return -EMSGSIZE;
> > +}
> > +
> > +static int ip_vs_genl_dump_dest(struct sk_buff *skb, struct ip_vs_dest *dest,
> > + struct netlink_callback *cb)
> > +{
> > + void *hdr;
> > +
> > + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
> > + &ip_vs_genl_family, NLM_F_MULTI,
> > + IPVS_CMD_NEW_DEST);
> > + if (!hdr)
> > + return -EMSGSIZE;
> > +
> > + if (ip_vs_genl_fill_dest(skb, dest) < 0)
> > + goto nla_put_failure;
> > +
> > + return genlmsg_end(skb, hdr);
> > +
> > +nla_put_failure:
> > + genlmsg_cancel(skb, hdr);
> > + return -EMSGSIZE;
> > +}
> > +
> > +static int ip_vs_genl_dump_dests(struct sk_buff *skb,
> > + struct netlink_callback *cb)
> > +{
> > + int idx = 0;
> > + int start = cb->args[0];
> > + struct ip_vs_service *svc;
> > + struct ip_vs_dest *dest;
> > + struct nlattr *attrs[IPVS_CMD_ATTR_MAX + 1];
> > +
> > + mutex_lock(&__ip_vs_mutex);
> > +
> > + /* Try to find the service for which to dump destinations */
> > + if (nlmsg_parse(cb->nlh, GENL_HDRLEN, attrs,
> > + IPVS_CMD_ATTR_MAX, ip_vs_cmd_policy))
> > + goto out_err;
> > +
> > + svc = ip_vs_genl_find_service(attrs[IPVS_CMD_ATTR_SERVICE]);
> > + if (IS_ERR(svc) || svc == NULL)
> > + goto out_err;
> > +
> > + /* Dump the destinations */
> > + list_for_each_entry(dest, &svc->destinations, n_list) {
> > + if (++idx <= start)
> > + continue;
> > + if (ip_vs_genl_dump_dest(skb, dest, cb) < 0) {
> > + idx--;
> > + goto nla_put_failure;
> > + }
> > + }
> > +
> > +nla_put_failure:
> > + cb->args[0] = idx;
> > + ip_vs_service_put(svc);
> > +
> > +out_err:
> > + mutex_unlock(&__ip_vs_mutex);
> > +
> > + return skb->len;
> > +}
> > +
> > +static int ip_vs_genl_parse_dest(struct ip_vs_dest_user *udest,
> > + struct nlattr *nla, int full_entry)
> > +{
> > + struct nlattr *attrs[IPVS_DEST_ATTR_MAX + 1];
> > + struct nlattr *nla_addr, *nla_port;
> > +
> > + /* Parse mandatory identifying destination fields first */
> > + if (nla == NULL ||
> > + nla_parse_nested(attrs, IPVS_DEST_ATTR_MAX, nla, ip_vs_dest_policy))
> > + return -EINVAL;
> > +
> > + nla_addr = attrs[IPVS_DEST_ATTR_ADDR];
> > + nla_port = attrs[IPVS_DEST_ATTR_PORT];
> > +
> > + if (!(nla_addr && nla_port))
> > + return -EINVAL;
> > +
> > + nla_memcpy(&udest->addr, nla_addr, sizeof(udest->addr));
> > + udest->port = nla_get_u16(nla_port);
> > +
> > + /* If a full entry was requested, check for the additional fields */
> > + if (full_entry) {
> > + struct nlattr *nla_fwd, *nla_weight, *nla_u_thresh,
> > + *nla_l_thresh;
> > +
> > + nla_fwd = attrs[IPVS_DEST_ATTR_FWD_METHOD];
> > + nla_weight = attrs[IPVS_DEST_ATTR_WEIGHT];
> > + nla_u_thresh = attrs[IPVS_DEST_ATTR_U_THRESH];
> > + nla_l_thresh = attrs[IPVS_DEST_ATTR_L_THRESH];
> > +
> > + if (!(nla_fwd && nla_weight && nla_u_thresh && nla_l_thresh))
> > + return -EINVAL;
> > +
> > + udest->conn_flags = nla_get_u32(nla_fwd)
> > + & IP_VS_CONN_F_FWD_MASK;
> > + udest->weight = nla_get_u32(nla_weight);
> > + udest->u_threshold = nla_get_u32(nla_u_thresh);
> > + udest->l_threshold = nla_get_u32(nla_l_thresh);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int ip_vs_genl_fill_daemon(struct sk_buff *skb, __be32 state,
> > + const char *mcast_ifn, __be32 syncid)
> > +{
> > + struct nlattr *nl_daemon;
> > +
> > + nl_daemon = nla_nest_start(skb, IPVS_CMD_ATTR_DAEMON);
> > + if (!nl_daemon)
> > + return -EMSGSIZE;
> > +
> > + NLA_PUT_U32(skb, IPVS_DAEMON_ATTR_STATE, state);
> > + NLA_PUT_STRING(skb, IPVS_DAEMON_ATTR_MCAST_IFN, mcast_ifn);
> > + NLA_PUT_U32(skb, IPVS_DAEMON_ATTR_SYNC_ID, syncid);
> > +
> > + nla_nest_end(skb, nl_daemon);
> > +
> > + return 0;
> > +
> > +nla_put_failure:
> > + nla_nest_cancel(skb, nl_daemon);
> > + return -EMSGSIZE;
> > +}
> > +
> > +static int ip_vs_genl_dump_daemon(struct sk_buff *skb, __be32 state,
> > + const char *mcast_ifn, __be32 syncid,
> > + struct netlink_callback *cb)
> > +{
> > + void *hdr;
> > + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
> > + &ip_vs_genl_family, NLM_F_MULTI,
> > + IPVS_CMD_NEW_DAEMON);
> > + if (!hdr)
> > + return -EMSGSIZE;
> > +
> > + if (ip_vs_genl_fill_daemon(skb, state, mcast_ifn, syncid))
> > + goto nla_put_failure;
> > +
> > + return genlmsg_end(skb, hdr);
> > +
> > +nla_put_failure:
> > + genlmsg_cancel(skb, hdr);
> > + return -EMSGSIZE;
> > +}
> > +
> > +static int ip_vs_genl_dump_daemons(struct sk_buff *skb,
> > + struct netlink_callback *cb)
> > +{
> > + mutex_lock(&__ip_vs_mutex);
> > + if ((ip_vs_sync_state & IP_VS_STATE_MASTER) && !cb->args[0]) {
> > + if (ip_vs_genl_dump_daemon(skb, IP_VS_STATE_MASTER,
> > + ip_vs_master_mcast_ifn,
> > + ip_vs_master_syncid, cb) < 0)
> > + goto nla_put_failure;
> > +
> > + cb->args[0] = 1;
> > + }
> > +
> > + if ((ip_vs_sync_state & IP_VS_STATE_BACKUP) && !cb->args[1]) {
> > + if (ip_vs_genl_dump_daemon(skb, IP_VS_STATE_BACKUP,
> > + ip_vs_backup_mcast_ifn,
> > + ip_vs_backup_syncid, cb) < 0)
> > + goto nla_put_failure;
> > +
> > + cb->args[1] = 1;
> > + }
> > +
> > +nla_put_failure:
> > + mutex_unlock(&__ip_vs_mutex);
> > +
> > + return skb->len;
> > +}
> > +
> > +static int ip_vs_genl_new_daemon(struct nlattr **attrs)
> > +{
> > + if (!(attrs[IPVS_DAEMON_ATTR_STATE] &&
> > + attrs[IPVS_DAEMON_ATTR_MCAST_IFN] &&
> > + attrs[IPVS_DAEMON_ATTR_SYNC_ID]))
> > + return -EINVAL;
> > +
> > + return start_sync_thread(nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]),
> > + nla_data(attrs[IPVS_DAEMON_ATTR_MCAST_IFN]),
> > + nla_get_u32(attrs[IPVS_DAEMON_ATTR_SYNC_ID]));
> > +}
> > +
> > +static int ip_vs_genl_del_daemon(struct nlattr **attrs)
> > +{
> > + if (!attrs[IPVS_DAEMON_ATTR_STATE])
> > + return -EINVAL;
> > +
> > + return stop_sync_thread(nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]));
> > +}
> > +
> > +static int ip_vs_genl_set_config(struct nlattr **attrs)
> > +{
> > + struct ip_vs_timeout_user t;
> > +
> > + __ip_vs_get_timeouts(&t);
> > +
> > + if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP])
> > + t.tcp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP]);
> > +
> > + if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN])
> > + t.tcp_fin_timeout =
> > + nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN]);
> > +
> > + if (attrs[IPVS_CMD_ATTR_TIMEOUT_UDP])
> > + t.udp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_UDP]);
> > +
> > + return ip_vs_set_timeout(&t);
> > +}
> > +
> > +static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info)
> > +{
> > + struct ip_vs_service *svc = NULL;
> > + struct ip_vs_service_user usvc;
> > + struct ip_vs_dest_user udest;
> > + int ret = 0, cmd;
> > + int need_full_svc = 0, need_full_dest = 0;
> > +
> > + cmd = info->genlhdr->cmd;
> > +
> > + mutex_lock(&__ip_vs_mutex);
> > +
> > + if (cmd == IPVS_CMD_FLUSH) {
> > + ret = ip_vs_flush();
> > + goto out;
> > + } else if (cmd == IPVS_CMD_SET_CONFIG) {
> > + ret = ip_vs_genl_set_config(info->attrs);
> > + goto out;
> > + } else if (cmd == IPVS_CMD_NEW_DAEMON ||
> > + cmd == IPVS_CMD_DEL_DAEMON) {
> > +
> > + struct nlattr *daemon_attrs[IPVS_DAEMON_ATTR_MAX + 1];
> > +
> > + if (!info->attrs[IPVS_CMD_ATTR_DAEMON] ||
> > + nla_parse_nested(daemon_attrs, IPVS_DAEMON_ATTR_MAX,
> > + info->attrs[IPVS_CMD_ATTR_DAEMON],
> > + ip_vs_daemon_policy)) {
> > + ret = -EINVAL;
> > + goto out;
> > + }
> > +
> > + if (cmd == IPVS_CMD_NEW_DAEMON)
> > + ret = ip_vs_genl_new_daemon(daemon_attrs);
> > + else
> > + ret = ip_vs_genl_del_daemon(daemon_attrs);
> > + goto out;
> > + } else if (cmd == IPVS_CMD_ZERO &&
> > + !info->attrs[IPVS_CMD_ATTR_SERVICE]) {
> > + ret = ip_vs_zero_all();
> > + goto out;
> > + }
> > +
> > + /* All following commands require a service argument, so check if we
> > + * received a valid one. We need a full service specification when
> > + * adding / editing a service. Only identifying members otherwise. */
> > + if (cmd == IPVS_CMD_NEW_SERVICE || cmd == IPVS_CMD_SET_SERVICE)
> > + need_full_svc = 1;
> > +
> > + ret = ip_vs_genl_parse_service(&usvc,
> > + info->attrs[IPVS_CMD_ATTR_SERVICE],
> > + need_full_svc);
> > + if (ret)
> > + goto out;
> > +
> > + /* Lookup the exact service by <protocol, addr, port> or fwmark */
> > + if (usvc.fwmark == 0)
> > + svc = __ip_vs_service_get(usvc.protocol, usvc.addr, usvc.port);
> > + else
> > + svc = __ip_vs_svc_fwm_get(usvc.fwmark);
> > +
> > + /* Unless we're adding a new service, the service must already exist */
> > + if ((cmd != IPVS_CMD_NEW_SERVICE) && (svc == NULL)) {
> > + ret = -ESRCH;
> > + goto out;
> > + }
> > +
> > + /* Destination commands require a valid destination argument. For
> > + * adding / editing a destination, we need a full destination
> > + * specification. */
> > + if (cmd == IPVS_CMD_NEW_DEST || cmd == IPVS_CMD_SET_DEST ||
> > + cmd == IPVS_CMD_DEL_DEST) {
> > + if (cmd != IPVS_CMD_DEL_DEST)
> > + need_full_dest = 1;
> > +
> > + ret = ip_vs_genl_parse_dest(&udest,
> > + info->attrs[IPVS_CMD_ATTR_DEST],
> > + need_full_dest);
> > + if (ret)
> > + goto out;
> > + }
> > +
> > + switch (cmd) {
> > + case IPVS_CMD_NEW_SERVICE:
> > + if (svc == NULL)
> > + ret = ip_vs_add_service(&usvc, &svc);
> > + else
> > + ret = -EEXIST;
> > + break;
> > + case IPVS_CMD_SET_SERVICE:
> > + ret = ip_vs_edit_service(svc, &usvc);
> > + break;
> > + case IPVS_CMD_DEL_SERVICE:
> > + ret = ip_vs_del_service(svc);
> > + break;
> > + case IPVS_CMD_NEW_DEST:
> > + ret = ip_vs_add_dest(svc, &udest);
> > + break;
> > + case IPVS_CMD_SET_DEST:
> > + ret = ip_vs_edit_dest(svc, &udest);
> > + break;
> > + case IPVS_CMD_DEL_DEST:
> > + ret = ip_vs_del_dest(svc, &udest);
> > + break;
> > + case IPVS_CMD_ZERO:
> > + ret = ip_vs_zero_service(svc);
> > + break;
> > + default:
> > + ret = -EINVAL;
> > + }
> > +
> > +out:
> > + if (svc)
> > + ip_vs_service_put(svc);
> > + mutex_unlock(&__ip_vs_mutex);
> > +
> > + return ret;
> > +}
> > +
> > +static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info)
> > +{
> > + struct sk_buff *msg;
> > + void *reply;
> > + int ret, cmd, reply_cmd;
> > +
> > + mutex_lock(&__ip_vs_mutex);
> > +
> > + cmd = info->genlhdr->cmd;
> > +
> > + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
> > + if (!msg) {
> > + ret = -ENOMEM;
> > + goto out_err;
>
> Here you want out...
>
> > + }
> > +
> > + if (cmd == IPVS_CMD_GET_SERVICE)
> > + reply_cmd = IPVS_CMD_NEW_SERVICE;
> > + else if (cmd == IPVS_CMD_GET_INFO)
> > + reply_cmd = IPVS_CMD_SET_INFO;
> > + else if (cmd == IPVS_CMD_GET_CONFIG)
> > + reply_cmd = IPVS_CMD_SET_CONFIG;
> > + else {
> > + IP_VS_ERR("unknown Generic Netlink command\n");
> > + ret = -EINVAL;
> > + goto out;
>
> ..and here you want out_error, to not leak msg.
Actually, exchange the two code blocks and jump to out in both.
> > + }
> > +
> > + reply = genlmsg_put_reply(msg, info, &ip_vs_genl_family, 0, reply_cmd);
> > + if (reply == NULL)
> > + goto nla_put_failure;
> > +
> > + switch (cmd) {
> > + case IPVS_CMD_GET_SERVICE:
> > + {
> > + struct ip_vs_service *svc;
> > +
> > + svc = ip_vs_genl_find_service(info->attrs[IPVS_CMD_ATTR_SERVICE]);
> > + if (IS_ERR(svc)) {
> > + ret = PTR_ERR(svc);
> > + goto out_err;
> > + } else if (svc) {
> > + ret = ip_vs_genl_fill_service(msg, svc);
> > + ip_vs_service_put(svc);
> > + if (ret)
> > + goto nla_put_failure;
> > + } else {
> > + ret = -ESRCH;
> > + goto out_err;
> > + }
> > +
> > + break;
> > + }
> > +
> > + case IPVS_CMD_GET_CONFIG:
> > + {
> > + struct ip_vs_timeout_user t;
> > +
> > + __ip_vs_get_timeouts(&t);
> > +#ifdef CONFIG_IP_VS_PROTO_TCP
> > + NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP, t.tcp_timeout);
> > + NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP_FIN,
> > + t.tcp_fin_timeout);
> > +#endif
> > +#ifdef CONFIG_IP_VS_PROTO_UDP
> > + NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_UDP, t.udp_timeout);
> > +#endif
> > +
> > + break;
> > + }
> > +
> > + case IPVS_CMD_GET_INFO:
> > + NLA_PUT_U32(msg, IPVS_INFO_ATTR_VERSION, IP_VS_VERSION_CODE);
> > + NLA_PUT_U32(msg, IPVS_INFO_ATTR_CONN_TAB_SIZE,
> > + IP_VS_CONN_TAB_SIZE);
> > + break;
> > + }
> > +
> > + genlmsg_end(msg, reply);
> > + ret = genlmsg_unicast(msg, info->snd_pid);
> > + goto out;
> > +
> > +nla_put_failure:
> > + IP_VS_ERR("not enough space in Netlink message\n");
> > + ret = -EMSGSIZE;
> > +
> > +out_err:
> > + if (msg)
> > + nlmsg_free(msg);
> > +out:
> > + mutex_unlock(&__ip_vs_mutex);
> > +
> > + return ret;
> > +}
> > +
> > +
> > +static struct genl_ops ip_vs_genl_ops[] __read_mostly = {
> > + /* SET commands */
> > + {
> > + .cmd = IPVS_CMD_NEW_SERVICE,
> > + .flags = GENL_ADMIN_PERM,
> > + .policy = ip_vs_cmd_policy,
> > + .doit = ip_vs_genl_set_cmd,
> > + },
> > + {
> > + .cmd = IPVS_CMD_SET_SERVICE,
> > + .flags = GENL_ADMIN_PERM,
> > + .policy = ip_vs_cmd_policy,
> > + .doit = ip_vs_genl_set_cmd,
> > + },
> > + {
> > + .cmd = IPVS_CMD_DEL_SERVICE,
> > + .flags = GENL_ADMIN_PERM,
> > + .policy = ip_vs_cmd_policy,
> > + .doit = ip_vs_genl_set_cmd,
> > + },
> > + {
> > + .cmd = IPVS_CMD_GET_SERVICE,
> > + .flags = GENL_ADMIN_PERM,
> > + .doit = ip_vs_genl_get_cmd,
> > + .dumpit = ip_vs_genl_dump_services,
> > + .policy = ip_vs_cmd_policy,
> > + },
> > + {
> > + .cmd = IPVS_CMD_NEW_DEST,
> > + .flags = GENL_ADMIN_PERM,
> > + .policy = ip_vs_cmd_policy,
> > + .doit = ip_vs_genl_set_cmd,
> > + },
> > + {
> > + .cmd = IPVS_CMD_SET_DEST,
> > + .flags = GENL_ADMIN_PERM,
> > + .policy = ip_vs_cmd_policy,
> > + .doit = ip_vs_genl_set_cmd,
> > + },
> > + {
> > + .cmd = IPVS_CMD_DEL_DEST,
> > + .flags = GENL_ADMIN_PERM,
> > + .policy = ip_vs_cmd_policy,
> > + .doit = ip_vs_genl_set_cmd,
> > + },
> > + {
> > + .cmd = IPVS_CMD_GET_DEST,
> > + .flags = GENL_ADMIN_PERM,
> > + .policy = ip_vs_cmd_policy,
> > + .dumpit = ip_vs_genl_dump_dests,
> > + },
> > + {
> > + .cmd = IPVS_CMD_NEW_DAEMON,
> > + .flags = GENL_ADMIN_PERM,
> > + .policy = ip_vs_cmd_policy,
> > + .doit = ip_vs_genl_set_cmd,
> > + },
> > + {
> > + .cmd = IPVS_CMD_DEL_DAEMON,
> > + .flags = GENL_ADMIN_PERM,
> > + .policy = ip_vs_cmd_policy,
> > + .doit = ip_vs_genl_set_cmd,
> > + },
> > + {
> > + .cmd = IPVS_CMD_GET_DAEMON,
> > + .flags = GENL_ADMIN_PERM,
> > + .dumpit = ip_vs_genl_dump_daemons,
> > + },
> > + {
> > + .cmd = IPVS_CMD_SET_CONFIG,
> > + .flags = GENL_ADMIN_PERM,
> > + .policy = ip_vs_cmd_policy,
> > + .doit = ip_vs_genl_set_cmd,
> > + },
> > + {
> > + .cmd = IPVS_CMD_GET_CONFIG,
> > + .flags = GENL_ADMIN_PERM,
> > + .doit = ip_vs_genl_get_cmd,
> > + },
> > + {
> > + .cmd = IPVS_CMD_GET_INFO,
> > + .flags = GENL_ADMIN_PERM,
> > + .doit = ip_vs_genl_get_cmd,
> > + },
> > + {
> > + .cmd = IPVS_CMD_ZERO,
> > + .flags = GENL_ADMIN_PERM,
> > + .policy = ip_vs_cmd_policy,
> > + .doit = ip_vs_genl_set_cmd,
> > + },
> > + {
> > + .cmd = IPVS_CMD_FLUSH,
> > + .flags = GENL_ADMIN_PERM,
> > + .doit = ip_vs_genl_set_cmd,
> > + },
> > +};
> > +
> > +int ip_vs_genl_register(void)
>
> static int __init
>
> > +{
> > + int ret, i;
> > +
> > + ret = genl_register_family(&ip_vs_genl_family);
> > + if (ret)
> > + return ret;
> > +
> > + for (i = 0; i < ARRAY_SIZE(ip_vs_genl_ops); i++) {
> > + ret = genl_register_ops(&ip_vs_genl_family, &ip_vs_genl_ops[i]);
> > + if (ret)
> > + goto err_out;
> > + }
> > + return 0;
> > +
> > +err_out:
> > + genl_unregister_family(&ip_vs_genl_family);
> > + return ret;
> > +}
> > +
> > +void ip_vs_genl_unregister(void)
>
> static void
>
> > +{
> > + genl_unregister_family(&ip_vs_genl_family);
> > +}
> > +
> > +/* End of Generic Netlink interface definitions */
> > +
> >
> > int ip_vs_control_init(void)
> > {
> > @@ -2319,6 +3191,13 @@ int ip_vs_control_init(void)
> > return ret;
> > }
> >
> > + ret = ip_vs_genl_register();
> > + if (ret) {
> > + IP_VS_ERR("cannot register Generic Netlink interface.\n");
> > + nf_unregister_sockopt(&ip_vs_sockopts);
> > + return ret;
> > + }
> > +
> > proc_net_fops_create(&init_net, "ip_vs", 0, &ip_vs_info_fops);
> > proc_net_fops_create(&init_net, "ip_vs_stats",0, &ip_vs_stats_fops);
> >
> > @@ -2355,6 +3234,7 @@ void ip_vs_control_cleanup(void)
> > unregister_sysctl_table(sysctl_header);
> > proc_net_remove(&init_net, "ip_vs_stats");
> > proc_net_remove(&init_net, "ip_vs");
> > + ip_vs_genl_unregister();
> > nf_unregister_sockopt(&ip_vs_sockopts);
> > LeaveFunction(2);
> > }
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCHv3 0/2] IPVS: Add Generic Netlink configuration interface
2008-08-13 16:00 ` Julius Volz
@ 2008-08-13 23:09 ` Simon Horman
2008-08-14 4:12 ` Simon Horman
0 siblings, 1 reply; 23+ messages in thread
From: Simon Horman @ 2008-08-13 23:09 UTC (permalink / raw)
To: Julius Volz; +Cc: julius.volz, netdev, lvs-devel, kaber, davem, tgraf, vbusam
On Wed, Aug 13, 2008 at 06:00:41PM +0200, Julius Volz wrote:
> On Sat, Aug 9, 2008 at 4:23 PM, Simon Horman <horms@verge.net.au> wrote:
> > Hi Julius,
> >
> > your latest patch does resolve the problem that I was seeing
> > with ipvsadm-1.25-nl-2. ipvsadm-1.25-nl-3 also works :-)
> >
> > I'd like to try and stress it out a bit more.
> > I will try and get to that tomorrow or on Monday.
>
> Hi, you were probably occupied by the other IPVS patches. Is there any
> news on this?
Sorry Julius, I got side-tracked. I'll get onto this today, one way or another.
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCHv3 0/2] IPVS: Add Generic Netlink configuration interface
2008-08-13 23:09 ` Simon Horman
@ 2008-08-14 4:12 ` Simon Horman
2008-08-14 9:34 ` Julius Volz
0 siblings, 1 reply; 23+ messages in thread
From: Simon Horman @ 2008-08-14 4:12 UTC (permalink / raw)
To: Julius Volz; +Cc: julius.volz, netdev, lvs-devel, kaber, davem, tgraf, vbusam
On Thu, Aug 14, 2008 at 09:09:29AM +1000, Simon Horman wrote:
> On Wed, Aug 13, 2008 at 06:00:41PM +0200, Julius Volz wrote:
> > On Sat, Aug 9, 2008 at 4:23 PM, Simon Horman <horms@verge.net.au> wrote:
> > > Hi Julius,
> > >
> > > your latest patch does resolve the problem that I was seeing
> > > with ipvsadm-1.25-nl-2. ipvsadm-1.25-nl-3 also works :-)
> > >
> > > I'd like to try and stress it out a bit more.
> > > I will try and get to that tomorrow or on Monday.
> >
> > Hi, you were probably occupied by the other IPVS patches. Is there any
> > news on this?
>
> Sorry Julius, I got side-tracked. I'll get onto this today, one way or another.
Hi Julius,
I tried to stress this code a bit more and nothing fell off.
I'm pretty happy with them, though Sven's comment about error checking
needs attention.
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCHv3 2/2] IPVS: Add genetlink interface implementation
2008-08-08 11:29 ` Julius Volz
2008-08-13 21:51 ` Sven Wegener
@ 2008-08-14 5:39 ` Sven Wegener
1 sibling, 0 replies; 23+ messages in thread
From: Sven Wegener @ 2008-08-14 5:39 UTC (permalink / raw)
To: Julius Volz; +Cc: netdev, lvs-devel, horms, kaber, davem, tgraf, vbusam
On Fri, 8 Aug 2008, Julius Volz wrote:
> This still had two bugs:
> - policies for IPVS_DEST_ATTR_FWD_METHOD and IPVS_SVC_ATTR_FLAGS
> were swapped
> - svc not initialized to NULL at the beginning of ip_vs_genl_set_cmd()
>
> The version below fixes this:
>
> ----
> Add the implementation of the new Generic Netlink interface to IPVS and
> keep the old set/getsockopt interface for userspace backwards
> compatibility.
>
> Signed-off-by: Julius Volz <juliusv@google.com>
>
> 1 files changed, 880 insertions(+), 0 deletions(-)
>
> diff --git a/net/ipv4/ipvs/ip_vs_ctl.c b/net/ipv4/ipvs/ip_vs_ctl.c
> index 9a5ace0..8038420 100644
> --- a/net/ipv4/ipvs/ip_vs_ctl.c
> +++ b/net/ipv4/ipvs/ip_vs_ctl.c
> @@ -37,6 +37,7 @@
> #include <net/ip.h>
> #include <net/route.h>
> #include <net/sock.h>
> +#include <net/genetlink.h>
>
> #include <asm/uaccess.h>
>
> @@ -2305,6 +2306,877 @@ static struct nf_sockopt_ops ip_vs_sockopts = {
> .owner = THIS_MODULE,
> };
>
> +/*
> + * Generic Netlink interface
> + */
> +
> +/* IPVS genetlink family */
> +static struct genl_family ip_vs_genl_family = {
> + .id = GENL_ID_GENERATE,
> + .hdrsize = 0,
> + .name = IPVS_GENL_NAME,
> + .version = IPVS_GENL_VERSION,
> + .maxattr = IPVS_CMD_MAX,
> +};
> +
> +/* Policy used for first-level command attributes */
> +static const struct nla_policy ip_vs_cmd_policy[IPVS_CMD_ATTR_MAX + 1] = {
> + [IPVS_CMD_ATTR_SERVICE] = { .type = NLA_NESTED },
> + [IPVS_CMD_ATTR_DEST] = { .type = NLA_NESTED },
> + [IPVS_CMD_ATTR_DAEMON] = { .type = NLA_NESTED },
> + [IPVS_CMD_ATTR_TIMEOUT_TCP] = { .type = NLA_U32 },
> + [IPVS_CMD_ATTR_TIMEOUT_TCP_FIN] = { .type = NLA_U32 },
> + [IPVS_CMD_ATTR_TIMEOUT_UDP] = { .type = NLA_U32 },
> +};
> +
> +/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_DAEMON */
> +static const struct nla_policy ip_vs_daemon_policy[IPVS_DAEMON_ATTR_MAX + 1] = {
> + [IPVS_DAEMON_ATTR_STATE] = { .type = NLA_U32 },
> + [IPVS_DAEMON_ATTR_MCAST_IFN] = { .type = NLA_NUL_STRING,
> + .len = IP_VS_IFNAME_MAXLEN },
> + [IPVS_DAEMON_ATTR_SYNC_ID] = { .type = NLA_U32 },
> +};
> +
> +/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_SERVICE */
> +static const struct nla_policy ip_vs_svc_policy[IPVS_SVC_ATTR_MAX + 1] = {
> + [IPVS_SVC_ATTR_AF] = { .type = NLA_U16 },
> + [IPVS_SVC_ATTR_PROTOCOL] = { .type = NLA_U16 },
> + [IPVS_SVC_ATTR_ADDR] = { .type = NLA_BINARY,
> + .len = sizeof(union nf_inet_addr) },
> + [IPVS_SVC_ATTR_PORT] = { .type = NLA_U16 },
> + [IPVS_SVC_ATTR_FWMARK] = { .type = NLA_U32 },
> + [IPVS_SVC_ATTR_SCHED_NAME] = { .type = NLA_NUL_STRING,
> + .len = IP_VS_SCHEDNAME_MAXLEN },
> + [IPVS_SVC_ATTR_FLAGS] = { .type = NLA_BINARY,
> + .len = sizeof(struct ip_vs_flags) },
> + [IPVS_SVC_ATTR_TIMEOUT] = { .type = NLA_U32 },
> + [IPVS_SVC_ATTR_NETMASK] = { .type = NLA_U32 },
> + [IPVS_SVC_ATTR_STATS] = { .type = NLA_NESTED },
> +};
> +
> +/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_DEST */
> +static const struct nla_policy ip_vs_dest_policy[IPVS_DEST_ATTR_MAX + 1] = {
> + [IPVS_DEST_ATTR_ADDR] = { .type = NLA_BINARY,
> + .len = sizeof(union nf_inet_addr) },
> + [IPVS_DEST_ATTR_PORT] = { .type = NLA_U16 },
> + [IPVS_DEST_ATTR_FWD_METHOD] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_WEIGHT] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_U_THRESH] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_L_THRESH] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_ACTIVE_CONNS] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_INACT_CONNS] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_PERSIST_CONNS] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_STATS] = { .type = NLA_NESTED },
> +};
> +
> +static int ip_vs_genl_fill_stats(struct sk_buff *skb, int container_type,
> + struct ip_vs_stats *stats)
> +{
> + struct nlattr *nl_stats = nla_nest_start(skb, container_type);
> + if (!nl_stats)
> + return -EMSGSIZE;
> +
> + spin_lock_bh(&stats->lock);
> +
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_CONNS, stats->conns);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_INPKTS, stats->inpkts);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTPKTS, stats->outpkts);
> + NLA_PUT_U64(skb, IPVS_STATS_ATTR_INBYTES, stats->inbytes);
> + NLA_PUT_U64(skb, IPVS_STATS_ATTR_OUTBYTES, stats->outbytes);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_CPS, stats->cps);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_INPPS, stats->inpps);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTPPS, stats->outpps);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_INBPS, stats->inbps);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTBPS, stats->outbps);
> +
> + spin_unlock_bh(&stats->lock);
> +
> + nla_nest_end(skb, nl_stats);
> +
> + return 0;
> +
> +nla_put_failure:
> + spin_unlock_bh(&stats->lock);
> + nla_nest_cancel(skb, nl_stats);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_fill_service(struct sk_buff *skb,
> + struct ip_vs_service *svc)
> +{
> + struct nlattr *nl_service;
> + struct ip_vs_flags flags = { .flags = svc->flags,
> + .mask = ~0 };
> +
> + nl_service = nla_nest_start(skb, IPVS_CMD_ATTR_SERVICE);
> + if (!nl_service)
> + return -EMSGSIZE;
> +
> + NLA_PUT_U16(skb, IPVS_SVC_ATTR_AF, AF_INET);
> +
> + if (svc->fwmark) {
> + NLA_PUT_U32(skb, IPVS_SVC_ATTR_FWMARK, svc->fwmark);
> + } else {
> + NLA_PUT_U16(skb, IPVS_SVC_ATTR_PROTOCOL, svc->protocol);
> + NLA_PUT(skb, IPVS_SVC_ATTR_ADDR, sizeof(svc->addr), &svc->addr);
> + NLA_PUT_U16(skb, IPVS_SVC_ATTR_PORT, svc->port);
> + }
> +
> + NLA_PUT_STRING(skb, IPVS_SVC_ATTR_SCHED_NAME, svc->scheduler->name);
> + NLA_PUT(skb, IPVS_SVC_ATTR_FLAGS, sizeof(flags), &flags);
> + NLA_PUT_U32(skb, IPVS_SVC_ATTR_TIMEOUT, svc->timeout / HZ);
> + NLA_PUT_U32(skb, IPVS_SVC_ATTR_NETMASK, svc->netmask);
> +
> + if (ip_vs_genl_fill_stats(skb, IPVS_SVC_ATTR_STATS, &svc->stats))
> + goto nla_put_failure;
> +
> + nla_nest_end(skb, nl_service);
> +
> + return 0;
> +
> +nla_put_failure:
> + nla_nest_cancel(skb, nl_service);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_dump_service(struct sk_buff *skb,
> + struct ip_vs_service *svc,
> + struct netlink_callback *cb)
> +{
> + void *hdr;
> +
> + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
> + &ip_vs_genl_family, NLM_F_MULTI,
> + IPVS_CMD_NEW_SERVICE);
> + if (!hdr)
> + return -EMSGSIZE;
> +
> + if (ip_vs_genl_fill_service(skb, svc) < 0)
> + goto nla_put_failure;
> +
> + return genlmsg_end(skb, hdr);
> +
> +nla_put_failure:
> + genlmsg_cancel(skb, hdr);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_dump_services(struct sk_buff *skb,
> + struct netlink_callback *cb)
> +{
> + int idx = 0, i;
> + int start = cb->args[0];
> + struct ip_vs_service *svc;
> +
> + mutex_lock(&__ip_vs_mutex);
> + for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) {
> + list_for_each_entry(svc, &ip_vs_svc_table[i], s_list) {
> + if (++idx <= start)
> + continue;
> + if (ip_vs_genl_dump_service(skb, svc, cb) < 0) {
> + idx--;
> + goto nla_put_failure;
> + }
> + }
> + }
> +
> + for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) {
> + list_for_each_entry(svc, &ip_vs_svc_fwm_table[i], f_list) {
> + if (++idx <= start)
> + continue;
> + if (ip_vs_genl_dump_service(skb, svc, cb) < 0) {
> + idx--;
> + goto nla_put_failure;
> + }
> + }
> + }
> +
> +nla_put_failure:
> + mutex_unlock(&__ip_vs_mutex);
> + cb->args[0] = idx;
> +
> + return skb->len;
> +}
> +
> +static int ip_vs_genl_parse_service(struct ip_vs_service_user *usvc,
> + struct nlattr *nla, int full_entry)
> +{
> + struct nlattr *attrs[IPVS_SVC_ATTR_MAX + 1];
> + struct nlattr *nla_af, *nla_port, *nla_fwmark, *nla_protocol, *nla_addr;
> +
> + /* Parse mandatory identifying service fields first */
> + if (nla == NULL ||
> + nla_parse_nested(attrs, IPVS_SVC_ATTR_MAX, nla, ip_vs_svc_policy))
> + return -EINVAL;
> +
> + nla_af = attrs[IPVS_SVC_ATTR_AF];
> + nla_protocol = attrs[IPVS_SVC_ATTR_PROTOCOL];
> + nla_addr = attrs[IPVS_SVC_ATTR_ADDR];
> + nla_port = attrs[IPVS_SVC_ATTR_PORT];
> + nla_fwmark = attrs[IPVS_SVC_ATTR_FWMARK];
> +
> + if (!(nla_af && (nla_fwmark || (nla_port && nla_protocol && nla_addr))))
> + return -EINVAL;
> +
> + /* For now, only support IPv4 */
> + if (nla_get_u16(nla_af) != AF_INET)
> + return -EAFNOSUPPORT;
> +
> + if (nla_fwmark) {
> + usvc->protocol = IPPROTO_TCP;
> + usvc->fwmark = nla_get_u32(nla_fwmark);
> + } else {
> + usvc->protocol = nla_get_u16(nla_protocol);
> + nla_memcpy(&usvc->addr, nla_addr, sizeof(usvc->addr));
> + usvc->port = nla_get_u16(nla_port);
> + usvc->fwmark = 0;
> + }
> +
> + /* If a full entry was requested, check for the additional fields */
> + if (full_entry) {
> + struct nlattr *nla_sched, *nla_flags, *nla_timeout,
> + *nla_netmask;
> + struct ip_vs_flags flags;
> + struct ip_vs_service *svc;
> +
> + nla_sched = attrs[IPVS_SVC_ATTR_SCHED_NAME];
> + nla_flags = attrs[IPVS_SVC_ATTR_FLAGS];
> + nla_timeout = attrs[IPVS_SVC_ATTR_TIMEOUT];
> + nla_netmask = attrs[IPVS_SVC_ATTR_NETMASK];
> +
> + if (!(nla_sched && nla_flags && nla_timeout && nla_netmask))
> + return -EINVAL;
> +
> + nla_memcpy(&flags, nla_flags, sizeof(flags));
> +
> + /* prefill flags from service if it already exists */
> + if (usvc->fwmark)
> + svc = __ip_vs_svc_fwm_get(usvc->fwmark);
> + else
> + svc = __ip_vs_service_get(usvc->protocol, usvc->addr,
> + usvc->port);
> + if (svc) {
> + usvc->flags = svc->flags;
> + ip_vs_service_put(svc);
> + } else
> + usvc->flags = 0;
> +
> + /* set new flags from userland */
> + usvc->flags = (usvc->flags & ~flags.mask) |
> + (flags.flags & flags.mask);
> +
> + strlcpy(usvc->sched_name, nla_data(nla_sched),
> + sizeof(usvc->sched_name));
> + usvc->timeout = nla_get_u32(nla_timeout);
> + usvc->netmask = nla_get_u32(nla_netmask);
> + }
> +
> + return 0;
> +}
> +
> +static struct ip_vs_service *ip_vs_genl_find_service(struct nlattr *nla)
> +{
> + struct ip_vs_service_user usvc;
> + int ret;
> +
> + ret = ip_vs_genl_parse_service(&usvc, nla, 0);
> + if (ret)
> + return ERR_PTR(ret);
> +
> + if (usvc.fwmark)
> + return __ip_vs_svc_fwm_get(usvc.fwmark);
> + else
> + return __ip_vs_service_get(usvc.protocol, usvc.addr,
> + usvc.port);
> +}
> +
> +static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest)
> +{
> + struct nlattr *nl_dest;
> +
> + nl_dest = nla_nest_start(skb, IPVS_CMD_ATTR_DEST);
> + if (!nl_dest)
> + return -EMSGSIZE;
> +
> + NLA_PUT(skb, IPVS_DEST_ATTR_ADDR, sizeof(dest->addr), &dest->addr);
> + NLA_PUT_U16(skb, IPVS_DEST_ATTR_PORT, dest->port);
> +
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_FWD_METHOD,
> + atomic_read(&dest->conn_flags) & IP_VS_CONN_F_FWD_MASK);
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_WEIGHT, atomic_read(&dest->weight));
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_U_THRESH, dest->u_threshold);
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_L_THRESH, dest->l_threshold);
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_ACTIVE_CONNS,
> + atomic_read(&dest->activeconns));
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_INACT_CONNS,
> + atomic_read(&dest->inactconns));
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_PERSIST_CONNS,
> + atomic_read(&dest->persistconns));
> +
> + if (ip_vs_genl_fill_stats(skb, IPVS_DEST_ATTR_STATS, &dest->stats))
> + goto nla_put_failure;
> +
> + nla_nest_end(skb, nl_dest);
> +
> + return 0;
> +
> +nla_put_failure:
> + nla_nest_cancel(skb, nl_dest);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_dump_dest(struct sk_buff *skb, struct ip_vs_dest *dest,
> + struct netlink_callback *cb)
> +{
> + void *hdr;
> +
> + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
> + &ip_vs_genl_family, NLM_F_MULTI,
> + IPVS_CMD_NEW_DEST);
> + if (!hdr)
> + return -EMSGSIZE;
> +
> + if (ip_vs_genl_fill_dest(skb, dest) < 0)
> + goto nla_put_failure;
> +
> + return genlmsg_end(skb, hdr);
> +
> +nla_put_failure:
> + genlmsg_cancel(skb, hdr);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_dump_dests(struct sk_buff *skb,
> + struct netlink_callback *cb)
> +{
> + int idx = 0;
> + int start = cb->args[0];
> + struct ip_vs_service *svc;
> + struct ip_vs_dest *dest;
> + struct nlattr *attrs[IPVS_CMD_ATTR_MAX + 1];
> +
> + mutex_lock(&__ip_vs_mutex);
> +
> + /* Try to find the service for which to dump destinations */
> + if (nlmsg_parse(cb->nlh, GENL_HDRLEN, attrs,
> + IPVS_CMD_ATTR_MAX, ip_vs_cmd_policy))
> + goto out_err;
> +
> + svc = ip_vs_genl_find_service(attrs[IPVS_CMD_ATTR_SERVICE]);
> + if (IS_ERR(svc) || svc == NULL)
> + goto out_err;
> +
> + /* Dump the destinations */
> + list_for_each_entry(dest, &svc->destinations, n_list) {
> + if (++idx <= start)
> + continue;
> + if (ip_vs_genl_dump_dest(skb, dest, cb) < 0) {
> + idx--;
> + goto nla_put_failure;
> + }
> + }
> +
> +nla_put_failure:
> + cb->args[0] = idx;
> + ip_vs_service_put(svc);
> +
> +out_err:
> + mutex_unlock(&__ip_vs_mutex);
> +
> + return skb->len;
> +}
> +
> +static int ip_vs_genl_parse_dest(struct ip_vs_dest_user *udest,
> + struct nlattr *nla, int full_entry)
> +{
> + struct nlattr *attrs[IPVS_DEST_ATTR_MAX + 1];
> + struct nlattr *nla_addr, *nla_port;
> +
> + /* Parse mandatory identifying destination fields first */
> + if (nla == NULL ||
> + nla_parse_nested(attrs, IPVS_DEST_ATTR_MAX, nla, ip_vs_dest_policy))
> + return -EINVAL;
> +
> + nla_addr = attrs[IPVS_DEST_ATTR_ADDR];
> + nla_port = attrs[IPVS_DEST_ATTR_PORT];
> +
> + if (!(nla_addr && nla_port))
> + return -EINVAL;
> +
> + nla_memcpy(&udest->addr, nla_addr, sizeof(udest->addr));
> + udest->port = nla_get_u16(nla_port);
> +
> + /* If a full entry was requested, check for the additional fields */
> + if (full_entry) {
> + struct nlattr *nla_fwd, *nla_weight, *nla_u_thresh,
> + *nla_l_thresh;
> +
> + nla_fwd = attrs[IPVS_DEST_ATTR_FWD_METHOD];
> + nla_weight = attrs[IPVS_DEST_ATTR_WEIGHT];
> + nla_u_thresh = attrs[IPVS_DEST_ATTR_U_THRESH];
> + nla_l_thresh = attrs[IPVS_DEST_ATTR_L_THRESH];
> +
> + if (!(nla_fwd && nla_weight && nla_u_thresh && nla_l_thresh))
> + return -EINVAL;
> +
> + udest->conn_flags = nla_get_u32(nla_fwd)
> + & IP_VS_CONN_F_FWD_MASK;
> + udest->weight = nla_get_u32(nla_weight);
> + udest->u_threshold = nla_get_u32(nla_u_thresh);
> + udest->l_threshold = nla_get_u32(nla_l_thresh);
> + }
> +
> + return 0;
> +}
> +
> +static int ip_vs_genl_fill_daemon(struct sk_buff *skb, __be32 state,
> + const char *mcast_ifn, __be32 syncid)
> +{
> + struct nlattr *nl_daemon;
> +
> + nl_daemon = nla_nest_start(skb, IPVS_CMD_ATTR_DAEMON);
> + if (!nl_daemon)
> + return -EMSGSIZE;
> +
> + NLA_PUT_U32(skb, IPVS_DAEMON_ATTR_STATE, state);
> + NLA_PUT_STRING(skb, IPVS_DAEMON_ATTR_MCAST_IFN, mcast_ifn);
> + NLA_PUT_U32(skb, IPVS_DAEMON_ATTR_SYNC_ID, syncid);
> +
> + nla_nest_end(skb, nl_daemon);
> +
> + return 0;
> +
> +nla_put_failure:
> + nla_nest_cancel(skb, nl_daemon);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_dump_daemon(struct sk_buff *skb, __be32 state,
> + const char *mcast_ifn, __be32 syncid,
> + struct netlink_callback *cb)
> +{
> + void *hdr;
> + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
> + &ip_vs_genl_family, NLM_F_MULTI,
> + IPVS_CMD_NEW_DAEMON);
> + if (!hdr)
> + return -EMSGSIZE;
> +
> + if (ip_vs_genl_fill_daemon(skb, state, mcast_ifn, syncid))
> + goto nla_put_failure;
> +
> + return genlmsg_end(skb, hdr);
> +
> +nla_put_failure:
> + genlmsg_cancel(skb, hdr);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_dump_daemons(struct sk_buff *skb,
> + struct netlink_callback *cb)
> +{
> + mutex_lock(&__ip_vs_mutex);
> + if ((ip_vs_sync_state & IP_VS_STATE_MASTER) && !cb->args[0]) {
> + if (ip_vs_genl_dump_daemon(skb, IP_VS_STATE_MASTER,
> + ip_vs_master_mcast_ifn,
> + ip_vs_master_syncid, cb) < 0)
> + goto nla_put_failure;
> +
> + cb->args[0] = 1;
> + }
> +
> + if ((ip_vs_sync_state & IP_VS_STATE_BACKUP) && !cb->args[1]) {
> + if (ip_vs_genl_dump_daemon(skb, IP_VS_STATE_BACKUP,
> + ip_vs_backup_mcast_ifn,
> + ip_vs_backup_syncid, cb) < 0)
> + goto nla_put_failure;
> +
> + cb->args[1] = 1;
> + }
> +
> +nla_put_failure:
> + mutex_unlock(&__ip_vs_mutex);
> +
> + return skb->len;
> +}
> +
> +static int ip_vs_genl_new_daemon(struct nlattr **attrs)
> +{
> + if (!(attrs[IPVS_DAEMON_ATTR_STATE] &&
> + attrs[IPVS_DAEMON_ATTR_MCAST_IFN] &&
> + attrs[IPVS_DAEMON_ATTR_SYNC_ID]))
> + return -EINVAL;
> +
> + return start_sync_thread(nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]),
> + nla_data(attrs[IPVS_DAEMON_ATTR_MCAST_IFN]),
> + nla_get_u32(attrs[IPVS_DAEMON_ATTR_SYNC_ID]));
> +}
> +
> +static int ip_vs_genl_del_daemon(struct nlattr **attrs)
> +{
> + if (!attrs[IPVS_DAEMON_ATTR_STATE])
> + return -EINVAL;
> +
> + return stop_sync_thread(nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]));
> +}
> +
> +static int ip_vs_genl_set_config(struct nlattr **attrs)
> +{
> + struct ip_vs_timeout_user t;
> +
> + __ip_vs_get_timeouts(&t);
> +
> + if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP])
> + t.tcp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP]);
> +
> + if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN])
> + t.tcp_fin_timeout =
> + nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN]);
> +
> + if (attrs[IPVS_CMD_ATTR_TIMEOUT_UDP])
> + t.udp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_UDP]);
> +
> + return ip_vs_set_timeout(&t);
> +}
> +
> +static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info)
> +{
> + struct ip_vs_service *svc = NULL;
> + struct ip_vs_service_user usvc;
> + struct ip_vs_dest_user udest;
> + int ret = 0, cmd;
> + int need_full_svc = 0, need_full_dest = 0;
> +
> + cmd = info->genlhdr->cmd;
> +
> + mutex_lock(&__ip_vs_mutex);
> +
> + if (cmd == IPVS_CMD_FLUSH) {
> + ret = ip_vs_flush();
> + goto out;
> + } else if (cmd == IPVS_CMD_SET_CONFIG) {
> + ret = ip_vs_genl_set_config(info->attrs);
> + goto out;
> + } else if (cmd == IPVS_CMD_NEW_DAEMON ||
> + cmd == IPVS_CMD_DEL_DAEMON) {
> +
> + struct nlattr *daemon_attrs[IPVS_DAEMON_ATTR_MAX + 1];
> +
> + if (!info->attrs[IPVS_CMD_ATTR_DAEMON] ||
> + nla_parse_nested(daemon_attrs, IPVS_DAEMON_ATTR_MAX,
> + info->attrs[IPVS_CMD_ATTR_DAEMON],
> + ip_vs_daemon_policy)) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + if (cmd == IPVS_CMD_NEW_DAEMON)
> + ret = ip_vs_genl_new_daemon(daemon_attrs);
> + else
> + ret = ip_vs_genl_del_daemon(daemon_attrs);
> + goto out;
> + } else if (cmd == IPVS_CMD_ZERO &&
> + !info->attrs[IPVS_CMD_ATTR_SERVICE]) {
> + ret = ip_vs_zero_all();
> + goto out;
> + }
> +
> + /* All following commands require a service argument, so check if we
> + * received a valid one. We need a full service specification when
> + * adding / editing a service. Only identifying members otherwise. */
> + if (cmd == IPVS_CMD_NEW_SERVICE || cmd == IPVS_CMD_SET_SERVICE)
> + need_full_svc = 1;
> +
> + ret = ip_vs_genl_parse_service(&usvc,
> + info->attrs[IPVS_CMD_ATTR_SERVICE],
> + need_full_svc);
> + if (ret)
> + goto out;
> +
> + /* Lookup the exact service by <protocol, addr, port> or fwmark */
> + if (usvc.fwmark == 0)
> + svc = __ip_vs_service_get(usvc.protocol, usvc.addr, usvc.port);
> + else
> + svc = __ip_vs_svc_fwm_get(usvc.fwmark);
> +
> + /* Unless we're adding a new service, the service must already exist */
> + if ((cmd != IPVS_CMD_NEW_SERVICE) && (svc == NULL)) {
> + ret = -ESRCH;
> + goto out;
> + }
> +
> + /* Destination commands require a valid destination argument. For
> + * adding / editing a destination, we need a full destination
> + * specification. */
> + if (cmd == IPVS_CMD_NEW_DEST || cmd == IPVS_CMD_SET_DEST ||
> + cmd == IPVS_CMD_DEL_DEST) {
> + if (cmd != IPVS_CMD_DEL_DEST)
> + need_full_dest = 1;
> +
> + ret = ip_vs_genl_parse_dest(&udest,
> + info->attrs[IPVS_CMD_ATTR_DEST],
> + need_full_dest);
> + if (ret)
> + goto out;
> + }
> +
> + switch (cmd) {
> + case IPVS_CMD_NEW_SERVICE:
> + if (svc == NULL)
> + ret = ip_vs_add_service(&usvc, &svc);
> + else
> + ret = -EEXIST;
> + break;
> + case IPVS_CMD_SET_SERVICE:
> + ret = ip_vs_edit_service(svc, &usvc);
> + break;
> + case IPVS_CMD_DEL_SERVICE:
> + ret = ip_vs_del_service(svc);
> + break;
> + case IPVS_CMD_NEW_DEST:
> + ret = ip_vs_add_dest(svc, &udest);
> + break;
> + case IPVS_CMD_SET_DEST:
> + ret = ip_vs_edit_dest(svc, &udest);
> + break;
> + case IPVS_CMD_DEL_DEST:
> + ret = ip_vs_del_dest(svc, &udest);
> + break;
> + case IPVS_CMD_ZERO:
> + ret = ip_vs_zero_service(svc);
> + break;
> + default:
> + ret = -EINVAL;
> + }
> +
> +out:
> + if (svc)
> + ip_vs_service_put(svc);
> + mutex_unlock(&__ip_vs_mutex);
> +
> + return ret;
> +}
> +
> +static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info)
> +{
> + struct sk_buff *msg;
> + void *reply;
> + int ret, cmd, reply_cmd;
> +
> + mutex_lock(&__ip_vs_mutex);
> +
> + cmd = info->genlhdr->cmd;
> +
> + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
> + if (!msg) {
> + ret = -ENOMEM;
> + goto out_err;
> + }
> +
> + if (cmd == IPVS_CMD_GET_SERVICE)
> + reply_cmd = IPVS_CMD_NEW_SERVICE;
> + else if (cmd == IPVS_CMD_GET_INFO)
> + reply_cmd = IPVS_CMD_SET_INFO;
> + else if (cmd == IPVS_CMD_GET_CONFIG)
> + reply_cmd = IPVS_CMD_SET_CONFIG;
> + else {
> + IP_VS_ERR("unknown Generic Netlink command\n");
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + reply = genlmsg_put_reply(msg, info, &ip_vs_genl_family, 0, reply_cmd);
> + if (reply == NULL)
> + goto nla_put_failure;
And for what it's worth, the code above doesn't require __ip_vs_mutex, you
can defer the locking until here for concurrency improvement.
> +
> + switch (cmd) {
> + case IPVS_CMD_GET_SERVICE:
> + {
> + struct ip_vs_service *svc;
> +
> + svc = ip_vs_genl_find_service(info->attrs[IPVS_CMD_ATTR_SERVICE]);
> + if (IS_ERR(svc)) {
> + ret = PTR_ERR(svc);
> + goto out_err;
> + } else if (svc) {
> + ret = ip_vs_genl_fill_service(msg, svc);
> + ip_vs_service_put(svc);
> + if (ret)
> + goto nla_put_failure;
> + } else {
> + ret = -ESRCH;
> + goto out_err;
> + }
> +
> + break;
> + }
> +
> + case IPVS_CMD_GET_CONFIG:
> + {
> + struct ip_vs_timeout_user t;
> +
> + __ip_vs_get_timeouts(&t);
> +#ifdef CONFIG_IP_VS_PROTO_TCP
> + NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP, t.tcp_timeout);
> + NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP_FIN,
> + t.tcp_fin_timeout);
> +#endif
> +#ifdef CONFIG_IP_VS_PROTO_UDP
> + NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_UDP, t.udp_timeout);
> +#endif
> +
> + break;
> + }
> +
> + case IPVS_CMD_GET_INFO:
> + NLA_PUT_U32(msg, IPVS_INFO_ATTR_VERSION, IP_VS_VERSION_CODE);
> + NLA_PUT_U32(msg, IPVS_INFO_ATTR_CONN_TAB_SIZE,
> + IP_VS_CONN_TAB_SIZE);
> + break;
> + }
> +
> + genlmsg_end(msg, reply);
> + ret = genlmsg_unicast(msg, info->snd_pid);
> + goto out;
> +
> +nla_put_failure:
> + IP_VS_ERR("not enough space in Netlink message\n");
> + ret = -EMSGSIZE;
> +
> +out_err:
> + if (msg)
> + nlmsg_free(msg);
> +out:
> + mutex_unlock(&__ip_vs_mutex);
> +
> + return ret;
> +}
> +
> +
> +static struct genl_ops ip_vs_genl_ops[] __read_mostly = {
> + /* SET commands */
> + {
> + .cmd = IPVS_CMD_NEW_SERVICE,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_SET_SERVICE,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_DEL_SERVICE,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_GET_SERVICE,
> + .flags = GENL_ADMIN_PERM,
> + .doit = ip_vs_genl_get_cmd,
> + .dumpit = ip_vs_genl_dump_services,
> + .policy = ip_vs_cmd_policy,
> + },
> + {
> + .cmd = IPVS_CMD_NEW_DEST,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_SET_DEST,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_DEL_DEST,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_GET_DEST,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .dumpit = ip_vs_genl_dump_dests,
> + },
> + {
> + .cmd = IPVS_CMD_NEW_DAEMON,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_DEL_DAEMON,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_GET_DAEMON,
> + .flags = GENL_ADMIN_PERM,
> + .dumpit = ip_vs_genl_dump_daemons,
> + },
> + {
> + .cmd = IPVS_CMD_SET_CONFIG,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_GET_CONFIG,
> + .flags = GENL_ADMIN_PERM,
> + .doit = ip_vs_genl_get_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_GET_INFO,
> + .flags = GENL_ADMIN_PERM,
> + .doit = ip_vs_genl_get_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_ZERO,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_FLUSH,
> + .flags = GENL_ADMIN_PERM,
> + .doit = ip_vs_genl_set_cmd,
> + },
> +};
> +
> +int ip_vs_genl_register(void)
> +{
> + int ret, i;
> +
> + ret = genl_register_family(&ip_vs_genl_family);
> + if (ret)
> + return ret;
> +
> + for (i = 0; i < ARRAY_SIZE(ip_vs_genl_ops); i++) {
> + ret = genl_register_ops(&ip_vs_genl_family, &ip_vs_genl_ops[i]);
> + if (ret)
> + goto err_out;
> + }
> + return 0;
> +
> +err_out:
> + genl_unregister_family(&ip_vs_genl_family);
> + return ret;
> +}
> +
> +void ip_vs_genl_unregister(void)
> +{
> + genl_unregister_family(&ip_vs_genl_family);
> +}
> +
> +/* End of Generic Netlink interface definitions */
> +
>
> int ip_vs_control_init(void)
> {
> @@ -2319,6 +3191,13 @@ int ip_vs_control_init(void)
> return ret;
> }
>
> + ret = ip_vs_genl_register();
> + if (ret) {
> + IP_VS_ERR("cannot register Generic Netlink interface.\n");
> + nf_unregister_sockopt(&ip_vs_sockopts);
> + return ret;
> + }
> +
> proc_net_fops_create(&init_net, "ip_vs", 0, &ip_vs_info_fops);
> proc_net_fops_create(&init_net, "ip_vs_stats",0, &ip_vs_stats_fops);
>
> @@ -2355,6 +3234,7 @@ void ip_vs_control_cleanup(void)
> unregister_sysctl_table(sysctl_header);
> proc_net_remove(&init_net, "ip_vs_stats");
> proc_net_remove(&init_net, "ip_vs");
> + ip_vs_genl_unregister();
> nf_unregister_sockopt(&ip_vs_sockopts);
> LeaveFunction(2);
> }
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCHv3 2/2] IPVS: Add genetlink interface implementation
2008-08-13 21:51 ` Sven Wegener
2008-08-13 21:53 ` Sven Wegener
@ 2008-08-14 9:32 ` Julius Volz
2008-08-14 9:52 ` Simon Horman
2008-08-14 10:04 ` Sven Wegener
1 sibling, 2 replies; 23+ messages in thread
From: Julius Volz @ 2008-08-14 9:32 UTC (permalink / raw)
To: Sven Wegener; +Cc: netdev, lvs-devel, horms, kaber, davem, tgraf, vbusam
Hi Sven,
On Wed, Aug 13, 2008 at 11:51:06PM +0200, Sven Wegener wrote:
> On Fri, 8 Aug 2008, Julius Volz wrote:
> > +static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info)
> > +{
> > + struct sk_buff *msg;
> > + void *reply;
> > + int ret, cmd, reply_cmd;
> > +
> > + mutex_lock(&__ip_vs_mutex);
> > +
> > + cmd = info->genlhdr->cmd;
> > +
> > + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
> > + if (!msg) {
> > + ret = -ENOMEM;
> > + goto out_err;
>
> Here you want out...
>
> > + }
> > +
> > + if (cmd == IPVS_CMD_GET_SERVICE)
> > + reply_cmd = IPVS_CMD_NEW_SERVICE;
> > + else if (cmd == IPVS_CMD_GET_INFO)
> > + reply_cmd = IPVS_CMD_SET_INFO;
> > + else if (cmd == IPVS_CMD_GET_CONFIG)
> > + reply_cmd = IPVS_CMD_SET_CONFIG;
> > + else {
> > + IP_VS_ERR("unknown Generic Netlink command\n");
> > + ret = -EINVAL;
> > + goto out;
>
> ..and here you want out_error, to not leak msg.
Ouch, thanks! Fixed this and locked the mutex later. I also removed the
"if (msg)" from out_err, as it becomes unneeded now. Here's the updated
patch:
-------------
Add the implementation of the new Generic Netlink interface to IPVS and
keep the old set/getsockopt interface for userspace backwards
compatibility.
Signed-off-by: Julius Volz <juliusv@google.com>
1 files changed, 878 insertions(+), 0 deletions(-)
diff --git a/net/ipv4/ipvs/ip_vs_ctl.c b/net/ipv4/ipvs/ip_vs_ctl.c
index 6379705..63a1cbb 100644
--- a/net/ipv4/ipvs/ip_vs_ctl.c
+++ b/net/ipv4/ipvs/ip_vs_ctl.c
@@ -37,6 +37,7 @@
#include <net/ip.h>
#include <net/route.h>
#include <net/sock.h>
+#include <net/genetlink.h>
#include <asm/uaccess.h>
@@ -2320,6 +2321,875 @@ static struct nf_sockopt_ops ip_vs_sockopts = {
.owner = THIS_MODULE,
};
+/*
+ * Generic Netlink interface
+ */
+
+/* IPVS genetlink family */
+static struct genl_family ip_vs_genl_family = {
+ .id = GENL_ID_GENERATE,
+ .hdrsize = 0,
+ .name = IPVS_GENL_NAME,
+ .version = IPVS_GENL_VERSION,
+ .maxattr = IPVS_CMD_MAX,
+};
+
+/* Policy used for first-level command attributes */
+static const struct nla_policy ip_vs_cmd_policy[IPVS_CMD_ATTR_MAX + 1] = {
+ [IPVS_CMD_ATTR_SERVICE] = { .type = NLA_NESTED },
+ [IPVS_CMD_ATTR_DEST] = { .type = NLA_NESTED },
+ [IPVS_CMD_ATTR_DAEMON] = { .type = NLA_NESTED },
+ [IPVS_CMD_ATTR_TIMEOUT_TCP] = { .type = NLA_U32 },
+ [IPVS_CMD_ATTR_TIMEOUT_TCP_FIN] = { .type = NLA_U32 },
+ [IPVS_CMD_ATTR_TIMEOUT_UDP] = { .type = NLA_U32 },
+};
+
+/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_DAEMON */
+static const struct nla_policy ip_vs_daemon_policy[IPVS_DAEMON_ATTR_MAX + 1] = {
+ [IPVS_DAEMON_ATTR_STATE] = { .type = NLA_U32 },
+ [IPVS_DAEMON_ATTR_MCAST_IFN] = { .type = NLA_NUL_STRING,
+ .len = IP_VS_IFNAME_MAXLEN },
+ [IPVS_DAEMON_ATTR_SYNC_ID] = { .type = NLA_U32 },
+};
+
+/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_SERVICE */
+static const struct nla_policy ip_vs_svc_policy[IPVS_SVC_ATTR_MAX + 1] = {
+ [IPVS_SVC_ATTR_AF] = { .type = NLA_U16 },
+ [IPVS_SVC_ATTR_PROTOCOL] = { .type = NLA_U16 },
+ [IPVS_SVC_ATTR_ADDR] = { .type = NLA_BINARY,
+ .len = sizeof(union nf_inet_addr) },
+ [IPVS_SVC_ATTR_PORT] = { .type = NLA_U16 },
+ [IPVS_SVC_ATTR_FWMARK] = { .type = NLA_U32 },
+ [IPVS_SVC_ATTR_SCHED_NAME] = { .type = NLA_NUL_STRING,
+ .len = IP_VS_SCHEDNAME_MAXLEN },
+ [IPVS_SVC_ATTR_FLAGS] = { .type = NLA_BINARY,
+ .len = sizeof(struct ip_vs_flags) },
+ [IPVS_SVC_ATTR_TIMEOUT] = { .type = NLA_U32 },
+ [IPVS_SVC_ATTR_NETMASK] = { .type = NLA_U32 },
+ [IPVS_SVC_ATTR_STATS] = { .type = NLA_NESTED },
+};
+
+/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_DEST */
+static const struct nla_policy ip_vs_dest_policy[IPVS_DEST_ATTR_MAX + 1] = {
+ [IPVS_DEST_ATTR_ADDR] = { .type = NLA_BINARY,
+ .len = sizeof(union nf_inet_addr) },
+ [IPVS_DEST_ATTR_PORT] = { .type = NLA_U16 },
+ [IPVS_DEST_ATTR_FWD_METHOD] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_WEIGHT] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_U_THRESH] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_L_THRESH] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_ACTIVE_CONNS] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_INACT_CONNS] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_PERSIST_CONNS] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_STATS] = { .type = NLA_NESTED },
+};
+
+static int ip_vs_genl_fill_stats(struct sk_buff *skb, int container_type,
+ struct ip_vs_stats *stats)
+{
+ struct nlattr *nl_stats = nla_nest_start(skb, container_type);
+ if (!nl_stats)
+ return -EMSGSIZE;
+
+ spin_lock_bh(&stats->lock);
+
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_CONNS, stats->conns);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_INPKTS, stats->inpkts);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTPKTS, stats->outpkts);
+ NLA_PUT_U64(skb, IPVS_STATS_ATTR_INBYTES, stats->inbytes);
+ NLA_PUT_U64(skb, IPVS_STATS_ATTR_OUTBYTES, stats->outbytes);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_CPS, stats->cps);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_INPPS, stats->inpps);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTPPS, stats->outpps);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_INBPS, stats->inbps);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTBPS, stats->outbps);
+
+ spin_unlock_bh(&stats->lock);
+
+ nla_nest_end(skb, nl_stats);
+
+ return 0;
+
+nla_put_failure:
+ spin_unlock_bh(&stats->lock);
+ nla_nest_cancel(skb, nl_stats);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_fill_service(struct sk_buff *skb,
+ struct ip_vs_service *svc)
+{
+ struct nlattr *nl_service;
+ struct ip_vs_flags flags = { .flags = svc->flags,
+ .mask = ~0 };
+
+ nl_service = nla_nest_start(skb, IPVS_CMD_ATTR_SERVICE);
+ if (!nl_service)
+ return -EMSGSIZE;
+
+ NLA_PUT_U16(skb, IPVS_SVC_ATTR_AF, AF_INET);
+
+ if (svc->fwmark) {
+ NLA_PUT_U32(skb, IPVS_SVC_ATTR_FWMARK, svc->fwmark);
+ } else {
+ NLA_PUT_U16(skb, IPVS_SVC_ATTR_PROTOCOL, svc->protocol);
+ NLA_PUT(skb, IPVS_SVC_ATTR_ADDR, sizeof(svc->addr), &svc->addr);
+ NLA_PUT_U16(skb, IPVS_SVC_ATTR_PORT, svc->port);
+ }
+
+ NLA_PUT_STRING(skb, IPVS_SVC_ATTR_SCHED_NAME, svc->scheduler->name);
+ NLA_PUT(skb, IPVS_SVC_ATTR_FLAGS, sizeof(flags), &flags);
+ NLA_PUT_U32(skb, IPVS_SVC_ATTR_TIMEOUT, svc->timeout / HZ);
+ NLA_PUT_U32(skb, IPVS_SVC_ATTR_NETMASK, svc->netmask);
+
+ if (ip_vs_genl_fill_stats(skb, IPVS_SVC_ATTR_STATS, &svc->stats))
+ goto nla_put_failure;
+
+ nla_nest_end(skb, nl_service);
+
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(skb, nl_service);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_dump_service(struct sk_buff *skb,
+ struct ip_vs_service *svc,
+ struct netlink_callback *cb)
+{
+ void *hdr;
+
+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
+ &ip_vs_genl_family, NLM_F_MULTI,
+ IPVS_CMD_NEW_SERVICE);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ if (ip_vs_genl_fill_service(skb, svc) < 0)
+ goto nla_put_failure;
+
+ return genlmsg_end(skb, hdr);
+
+nla_put_failure:
+ genlmsg_cancel(skb, hdr);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_dump_services(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ int idx = 0, i;
+ int start = cb->args[0];
+ struct ip_vs_service *svc;
+
+ mutex_lock(&__ip_vs_mutex);
+ for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) {
+ list_for_each_entry(svc, &ip_vs_svc_table[i], s_list) {
+ if (++idx <= start)
+ continue;
+ if (ip_vs_genl_dump_service(skb, svc, cb) < 0) {
+ idx--;
+ goto nla_put_failure;
+ }
+ }
+ }
+
+ for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) {
+ list_for_each_entry(svc, &ip_vs_svc_fwm_table[i], f_list) {
+ if (++idx <= start)
+ continue;
+ if (ip_vs_genl_dump_service(skb, svc, cb) < 0) {
+ idx--;
+ goto nla_put_failure;
+ }
+ }
+ }
+
+nla_put_failure:
+ mutex_unlock(&__ip_vs_mutex);
+ cb->args[0] = idx;
+
+ return skb->len;
+}
+
+static int ip_vs_genl_parse_service(struct ip_vs_service_user *usvc,
+ struct nlattr *nla, int full_entry)
+{
+ struct nlattr *attrs[IPVS_SVC_ATTR_MAX + 1];
+ struct nlattr *nla_af, *nla_port, *nla_fwmark, *nla_protocol, *nla_addr;
+
+ /* Parse mandatory identifying service fields first */
+ if (nla == NULL ||
+ nla_parse_nested(attrs, IPVS_SVC_ATTR_MAX, nla, ip_vs_svc_policy))
+ return -EINVAL;
+
+ nla_af = attrs[IPVS_SVC_ATTR_AF];
+ nla_protocol = attrs[IPVS_SVC_ATTR_PROTOCOL];
+ nla_addr = attrs[IPVS_SVC_ATTR_ADDR];
+ nla_port = attrs[IPVS_SVC_ATTR_PORT];
+ nla_fwmark = attrs[IPVS_SVC_ATTR_FWMARK];
+
+ if (!(nla_af && (nla_fwmark || (nla_port && nla_protocol && nla_addr))))
+ return -EINVAL;
+
+ /* For now, only support IPv4 */
+ if (nla_get_u16(nla_af) != AF_INET)
+ return -EAFNOSUPPORT;
+
+ if (nla_fwmark) {
+ usvc->protocol = IPPROTO_TCP;
+ usvc->fwmark = nla_get_u32(nla_fwmark);
+ } else {
+ usvc->protocol = nla_get_u16(nla_protocol);
+ nla_memcpy(&usvc->addr, nla_addr, sizeof(usvc->addr));
+ usvc->port = nla_get_u16(nla_port);
+ usvc->fwmark = 0;
+ }
+
+ /* If a full entry was requested, check for the additional fields */
+ if (full_entry) {
+ struct nlattr *nla_sched, *nla_flags, *nla_timeout,
+ *nla_netmask;
+ struct ip_vs_flags flags;
+ struct ip_vs_service *svc;
+
+ nla_sched = attrs[IPVS_SVC_ATTR_SCHED_NAME];
+ nla_flags = attrs[IPVS_SVC_ATTR_FLAGS];
+ nla_timeout = attrs[IPVS_SVC_ATTR_TIMEOUT];
+ nla_netmask = attrs[IPVS_SVC_ATTR_NETMASK];
+
+ if (!(nla_sched && nla_flags && nla_timeout && nla_netmask))
+ return -EINVAL;
+
+ nla_memcpy(&flags, nla_flags, sizeof(flags));
+
+ /* prefill flags from service if it already exists */
+ if (usvc->fwmark)
+ svc = __ip_vs_svc_fwm_get(usvc->fwmark);
+ else
+ svc = __ip_vs_service_get(usvc->protocol, usvc->addr,
+ usvc->port);
+ if (svc) {
+ usvc->flags = svc->flags;
+ ip_vs_service_put(svc);
+ } else
+ usvc->flags = 0;
+
+ /* set new flags from userland */
+ usvc->flags = (usvc->flags & ~flags.mask) |
+ (flags.flags & flags.mask);
+
+ strlcpy(usvc->sched_name, nla_data(nla_sched),
+ sizeof(usvc->sched_name));
+ usvc->timeout = nla_get_u32(nla_timeout);
+ usvc->netmask = nla_get_u32(nla_netmask);
+ }
+
+ return 0;
+}
+
+static struct ip_vs_service *ip_vs_genl_find_service(struct nlattr *nla)
+{
+ struct ip_vs_service_user usvc;
+ int ret;
+
+ ret = ip_vs_genl_parse_service(&usvc, nla, 0);
+ if (ret)
+ return ERR_PTR(ret);
+
+ if (usvc.fwmark)
+ return __ip_vs_svc_fwm_get(usvc.fwmark);
+ else
+ return __ip_vs_service_get(usvc.protocol, usvc.addr,
+ usvc.port);
+}
+
+static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest)
+{
+ struct nlattr *nl_dest;
+
+ nl_dest = nla_nest_start(skb, IPVS_CMD_ATTR_DEST);
+ if (!nl_dest)
+ return -EMSGSIZE;
+
+ NLA_PUT(skb, IPVS_DEST_ATTR_ADDR, sizeof(dest->addr), &dest->addr);
+ NLA_PUT_U16(skb, IPVS_DEST_ATTR_PORT, dest->port);
+
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_FWD_METHOD,
+ atomic_read(&dest->conn_flags) & IP_VS_CONN_F_FWD_MASK);
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_WEIGHT, atomic_read(&dest->weight));
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_U_THRESH, dest->u_threshold);
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_L_THRESH, dest->l_threshold);
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_ACTIVE_CONNS,
+ atomic_read(&dest->activeconns));
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_INACT_CONNS,
+ atomic_read(&dest->inactconns));
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_PERSIST_CONNS,
+ atomic_read(&dest->persistconns));
+
+ if (ip_vs_genl_fill_stats(skb, IPVS_DEST_ATTR_STATS, &dest->stats))
+ goto nla_put_failure;
+
+ nla_nest_end(skb, nl_dest);
+
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(skb, nl_dest);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_dump_dest(struct sk_buff *skb, struct ip_vs_dest *dest,
+ struct netlink_callback *cb)
+{
+ void *hdr;
+
+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
+ &ip_vs_genl_family, NLM_F_MULTI,
+ IPVS_CMD_NEW_DEST);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ if (ip_vs_genl_fill_dest(skb, dest) < 0)
+ goto nla_put_failure;
+
+ return genlmsg_end(skb, hdr);
+
+nla_put_failure:
+ genlmsg_cancel(skb, hdr);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_dump_dests(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ int idx = 0;
+ int start = cb->args[0];
+ struct ip_vs_service *svc;
+ struct ip_vs_dest *dest;
+ struct nlattr *attrs[IPVS_CMD_ATTR_MAX + 1];
+
+ mutex_lock(&__ip_vs_mutex);
+
+ /* Try to find the service for which to dump destinations */
+ if (nlmsg_parse(cb->nlh, GENL_HDRLEN, attrs,
+ IPVS_CMD_ATTR_MAX, ip_vs_cmd_policy))
+ goto out_err;
+
+ svc = ip_vs_genl_find_service(attrs[IPVS_CMD_ATTR_SERVICE]);
+ if (IS_ERR(svc) || svc == NULL)
+ goto out_err;
+
+ /* Dump the destinations */
+ list_for_each_entry(dest, &svc->destinations, n_list) {
+ if (++idx <= start)
+ continue;
+ if (ip_vs_genl_dump_dest(skb, dest, cb) < 0) {
+ idx--;
+ goto nla_put_failure;
+ }
+ }
+
+nla_put_failure:
+ cb->args[0] = idx;
+ ip_vs_service_put(svc);
+
+out_err:
+ mutex_unlock(&__ip_vs_mutex);
+
+ return skb->len;
+}
+
+static int ip_vs_genl_parse_dest(struct ip_vs_dest_user *udest,
+ struct nlattr *nla, int full_entry)
+{
+ struct nlattr *attrs[IPVS_DEST_ATTR_MAX + 1];
+ struct nlattr *nla_addr, *nla_port;
+
+ /* Parse mandatory identifying destination fields first */
+ if (nla == NULL ||
+ nla_parse_nested(attrs, IPVS_DEST_ATTR_MAX, nla, ip_vs_dest_policy))
+ return -EINVAL;
+
+ nla_addr = attrs[IPVS_DEST_ATTR_ADDR];
+ nla_port = attrs[IPVS_DEST_ATTR_PORT];
+
+ if (!(nla_addr && nla_port))
+ return -EINVAL;
+
+ nla_memcpy(&udest->addr, nla_addr, sizeof(udest->addr));
+ udest->port = nla_get_u16(nla_port);
+
+ /* If a full entry was requested, check for the additional fields */
+ if (full_entry) {
+ struct nlattr *nla_fwd, *nla_weight, *nla_u_thresh,
+ *nla_l_thresh;
+
+ nla_fwd = attrs[IPVS_DEST_ATTR_FWD_METHOD];
+ nla_weight = attrs[IPVS_DEST_ATTR_WEIGHT];
+ nla_u_thresh = attrs[IPVS_DEST_ATTR_U_THRESH];
+ nla_l_thresh = attrs[IPVS_DEST_ATTR_L_THRESH];
+
+ if (!(nla_fwd && nla_weight && nla_u_thresh && nla_l_thresh))
+ return -EINVAL;
+
+ udest->conn_flags = nla_get_u32(nla_fwd)
+ & IP_VS_CONN_F_FWD_MASK;
+ udest->weight = nla_get_u32(nla_weight);
+ udest->u_threshold = nla_get_u32(nla_u_thresh);
+ udest->l_threshold = nla_get_u32(nla_l_thresh);
+ }
+
+ return 0;
+}
+
+static int ip_vs_genl_fill_daemon(struct sk_buff *skb, __be32 state,
+ const char *mcast_ifn, __be32 syncid)
+{
+ struct nlattr *nl_daemon;
+
+ nl_daemon = nla_nest_start(skb, IPVS_CMD_ATTR_DAEMON);
+ if (!nl_daemon)
+ return -EMSGSIZE;
+
+ NLA_PUT_U32(skb, IPVS_DAEMON_ATTR_STATE, state);
+ NLA_PUT_STRING(skb, IPVS_DAEMON_ATTR_MCAST_IFN, mcast_ifn);
+ NLA_PUT_U32(skb, IPVS_DAEMON_ATTR_SYNC_ID, syncid);
+
+ nla_nest_end(skb, nl_daemon);
+
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(skb, nl_daemon);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_dump_daemon(struct sk_buff *skb, __be32 state,
+ const char *mcast_ifn, __be32 syncid,
+ struct netlink_callback *cb)
+{
+ void *hdr;
+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
+ &ip_vs_genl_family, NLM_F_MULTI,
+ IPVS_CMD_NEW_DAEMON);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ if (ip_vs_genl_fill_daemon(skb, state, mcast_ifn, syncid))
+ goto nla_put_failure;
+
+ return genlmsg_end(skb, hdr);
+
+nla_put_failure:
+ genlmsg_cancel(skb, hdr);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_dump_daemons(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ mutex_lock(&__ip_vs_mutex);
+ if ((ip_vs_sync_state & IP_VS_STATE_MASTER) && !cb->args[0]) {
+ if (ip_vs_genl_dump_daemon(skb, IP_VS_STATE_MASTER,
+ ip_vs_master_mcast_ifn,
+ ip_vs_master_syncid, cb) < 0)
+ goto nla_put_failure;
+
+ cb->args[0] = 1;
+ }
+
+ if ((ip_vs_sync_state & IP_VS_STATE_BACKUP) && !cb->args[1]) {
+ if (ip_vs_genl_dump_daemon(skb, IP_VS_STATE_BACKUP,
+ ip_vs_backup_mcast_ifn,
+ ip_vs_backup_syncid, cb) < 0)
+ goto nla_put_failure;
+
+ cb->args[1] = 1;
+ }
+
+nla_put_failure:
+ mutex_unlock(&__ip_vs_mutex);
+
+ return skb->len;
+}
+
+static int ip_vs_genl_new_daemon(struct nlattr **attrs)
+{
+ if (!(attrs[IPVS_DAEMON_ATTR_STATE] &&
+ attrs[IPVS_DAEMON_ATTR_MCAST_IFN] &&
+ attrs[IPVS_DAEMON_ATTR_SYNC_ID]))
+ return -EINVAL;
+
+ return start_sync_thread(nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]),
+ nla_data(attrs[IPVS_DAEMON_ATTR_MCAST_IFN]),
+ nla_get_u32(attrs[IPVS_DAEMON_ATTR_SYNC_ID]));
+}
+
+static int ip_vs_genl_del_daemon(struct nlattr **attrs)
+{
+ if (!attrs[IPVS_DAEMON_ATTR_STATE])
+ return -EINVAL;
+
+ return stop_sync_thread(nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]));
+}
+
+static int ip_vs_genl_set_config(struct nlattr **attrs)
+{
+ struct ip_vs_timeout_user t;
+
+ __ip_vs_get_timeouts(&t);
+
+ if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP])
+ t.tcp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP]);
+
+ if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN])
+ t.tcp_fin_timeout =
+ nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN]);
+
+ if (attrs[IPVS_CMD_ATTR_TIMEOUT_UDP])
+ t.udp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_UDP]);
+
+ return ip_vs_set_timeout(&t);
+}
+
+static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info)
+{
+ struct ip_vs_service *svc = NULL;
+ struct ip_vs_service_user usvc;
+ struct ip_vs_dest_user udest;
+ int ret = 0, cmd;
+ int need_full_svc = 0, need_full_dest = 0;
+
+ cmd = info->genlhdr->cmd;
+
+ mutex_lock(&__ip_vs_mutex);
+
+ if (cmd == IPVS_CMD_FLUSH) {
+ ret = ip_vs_flush();
+ goto out;
+ } else if (cmd == IPVS_CMD_SET_CONFIG) {
+ ret = ip_vs_genl_set_config(info->attrs);
+ goto out;
+ } else if (cmd == IPVS_CMD_NEW_DAEMON ||
+ cmd == IPVS_CMD_DEL_DAEMON) {
+
+ struct nlattr *daemon_attrs[IPVS_DAEMON_ATTR_MAX + 1];
+
+ if (!info->attrs[IPVS_CMD_ATTR_DAEMON] ||
+ nla_parse_nested(daemon_attrs, IPVS_DAEMON_ATTR_MAX,
+ info->attrs[IPVS_CMD_ATTR_DAEMON],
+ ip_vs_daemon_policy)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (cmd == IPVS_CMD_NEW_DAEMON)
+ ret = ip_vs_genl_new_daemon(daemon_attrs);
+ else
+ ret = ip_vs_genl_del_daemon(daemon_attrs);
+ goto out;
+ } else if (cmd == IPVS_CMD_ZERO &&
+ !info->attrs[IPVS_CMD_ATTR_SERVICE]) {
+ ret = ip_vs_zero_all();
+ goto out;
+ }
+
+ /* All following commands require a service argument, so check if we
+ * received a valid one. We need a full service specification when
+ * adding / editing a service. Only identifying members otherwise. */
+ if (cmd == IPVS_CMD_NEW_SERVICE || cmd == IPVS_CMD_SET_SERVICE)
+ need_full_svc = 1;
+
+ ret = ip_vs_genl_parse_service(&usvc,
+ info->attrs[IPVS_CMD_ATTR_SERVICE],
+ need_full_svc);
+ if (ret)
+ goto out;
+
+ /* Lookup the exact service by <protocol, addr, port> or fwmark */
+ if (usvc.fwmark == 0)
+ svc = __ip_vs_service_get(usvc.protocol, usvc.addr, usvc.port);
+ else
+ svc = __ip_vs_svc_fwm_get(usvc.fwmark);
+
+ /* Unless we're adding a new service, the service must already exist */
+ if ((cmd != IPVS_CMD_NEW_SERVICE) && (svc == NULL)) {
+ ret = -ESRCH;
+ goto out;
+ }
+
+ /* Destination commands require a valid destination argument. For
+ * adding / editing a destination, we need a full destination
+ * specification. */
+ if (cmd == IPVS_CMD_NEW_DEST || cmd == IPVS_CMD_SET_DEST ||
+ cmd == IPVS_CMD_DEL_DEST) {
+ if (cmd != IPVS_CMD_DEL_DEST)
+ need_full_dest = 1;
+
+ ret = ip_vs_genl_parse_dest(&udest,
+ info->attrs[IPVS_CMD_ATTR_DEST],
+ need_full_dest);
+ if (ret)
+ goto out;
+ }
+
+ switch (cmd) {
+ case IPVS_CMD_NEW_SERVICE:
+ if (svc == NULL)
+ ret = ip_vs_add_service(&usvc, &svc);
+ else
+ ret = -EEXIST;
+ break;
+ case IPVS_CMD_SET_SERVICE:
+ ret = ip_vs_edit_service(svc, &usvc);
+ break;
+ case IPVS_CMD_DEL_SERVICE:
+ ret = ip_vs_del_service(svc);
+ break;
+ case IPVS_CMD_NEW_DEST:
+ ret = ip_vs_add_dest(svc, &udest);
+ break;
+ case IPVS_CMD_SET_DEST:
+ ret = ip_vs_edit_dest(svc, &udest);
+ break;
+ case IPVS_CMD_DEL_DEST:
+ ret = ip_vs_del_dest(svc, &udest);
+ break;
+ case IPVS_CMD_ZERO:
+ ret = ip_vs_zero_service(svc);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+out:
+ if (svc)
+ ip_vs_service_put(svc);
+ mutex_unlock(&__ip_vs_mutex);
+
+ return ret;
+}
+
+static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info)
+{
+ struct sk_buff *msg;
+ void *reply;
+ int ret, cmd, reply_cmd;
+
+ cmd = info->genlhdr->cmd;
+
+ if (cmd == IPVS_CMD_GET_SERVICE)
+ reply_cmd = IPVS_CMD_NEW_SERVICE;
+ else if (cmd == IPVS_CMD_GET_INFO)
+ reply_cmd = IPVS_CMD_SET_INFO;
+ else if (cmd == IPVS_CMD_GET_CONFIG)
+ reply_cmd = IPVS_CMD_SET_CONFIG;
+ else {
+ IP_VS_ERR("unknown Generic Netlink command\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ reply = genlmsg_put_reply(msg, info, &ip_vs_genl_family, 0, reply_cmd);
+ if (reply == NULL)
+ goto nla_put_failure;
+
+ mutex_lock(&__ip_vs_mutex);
+
+ switch (cmd) {
+ case IPVS_CMD_GET_SERVICE:
+ {
+ struct ip_vs_service *svc;
+
+ svc = ip_vs_genl_find_service(info->attrs[IPVS_CMD_ATTR_SERVICE]);
+ if (IS_ERR(svc)) {
+ ret = PTR_ERR(svc);
+ goto out_err;
+ } else if (svc) {
+ ret = ip_vs_genl_fill_service(msg, svc);
+ ip_vs_service_put(svc);
+ if (ret)
+ goto nla_put_failure;
+ } else {
+ ret = -ESRCH;
+ goto out_err;
+ }
+
+ break;
+ }
+
+ case IPVS_CMD_GET_CONFIG:
+ {
+ struct ip_vs_timeout_user t;
+
+ __ip_vs_get_timeouts(&t);
+#ifdef CONFIG_IP_VS_PROTO_TCP
+ NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP, t.tcp_timeout);
+ NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP_FIN,
+ t.tcp_fin_timeout);
+#endif
+#ifdef CONFIG_IP_VS_PROTO_UDP
+ NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_UDP, t.udp_timeout);
+#endif
+
+ break;
+ }
+
+ case IPVS_CMD_GET_INFO:
+ NLA_PUT_U32(msg, IPVS_INFO_ATTR_VERSION, IP_VS_VERSION_CODE);
+ NLA_PUT_U32(msg, IPVS_INFO_ATTR_CONN_TAB_SIZE,
+ IP_VS_CONN_TAB_SIZE);
+ break;
+ }
+
+ genlmsg_end(msg, reply);
+ ret = genlmsg_unicast(msg, info->snd_pid);
+ goto out;
+
+nla_put_failure:
+ IP_VS_ERR("not enough space in Netlink message\n");
+ ret = -EMSGSIZE;
+
+out_err:
+ nlmsg_free(msg);
+out:
+ mutex_unlock(&__ip_vs_mutex);
+
+ return ret;
+}
+
+
+static struct genl_ops ip_vs_genl_ops[] __read_mostly = {
+ {
+ .cmd = IPVS_CMD_NEW_SERVICE,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_SET_SERVICE,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_DEL_SERVICE,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_GET_SERVICE,
+ .flags = GENL_ADMIN_PERM,
+ .doit = ip_vs_genl_get_cmd,
+ .dumpit = ip_vs_genl_dump_services,
+ .policy = ip_vs_cmd_policy,
+ },
+ {
+ .cmd = IPVS_CMD_NEW_DEST,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_SET_DEST,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_DEL_DEST,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_GET_DEST,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .dumpit = ip_vs_genl_dump_dests,
+ },
+ {
+ .cmd = IPVS_CMD_NEW_DAEMON,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_DEL_DAEMON,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_GET_DAEMON,
+ .flags = GENL_ADMIN_PERM,
+ .dumpit = ip_vs_genl_dump_daemons,
+ },
+ {
+ .cmd = IPVS_CMD_SET_CONFIG,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_GET_CONFIG,
+ .flags = GENL_ADMIN_PERM,
+ .doit = ip_vs_genl_get_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_GET_INFO,
+ .flags = GENL_ADMIN_PERM,
+ .doit = ip_vs_genl_get_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_ZERO,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_FLUSH,
+ .flags = GENL_ADMIN_PERM,
+ .doit = ip_vs_genl_set_cmd,
+ },
+};
+
+int ip_vs_genl_register(void)
+{
+ int ret, i;
+
+ ret = genl_register_family(&ip_vs_genl_family);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(ip_vs_genl_ops); i++) {
+ ret = genl_register_ops(&ip_vs_genl_family, &ip_vs_genl_ops[i]);
+ if (ret)
+ goto err_out;
+ }
+ return 0;
+
+err_out:
+ genl_unregister_family(&ip_vs_genl_family);
+ return ret;
+}
+
+void ip_vs_genl_unregister(void)
+{
+ genl_unregister_family(&ip_vs_genl_family);
+}
+
+/* End of Generic Netlink interface definitions */
+
int __init ip_vs_control_init(void)
{
@@ -2334,6 +3204,13 @@ int __init ip_vs_control_init(void)
return ret;
}
+ ret = ip_vs_genl_register();
+ if (ret) {
+ IP_VS_ERR("cannot register Generic Netlink interface.\n");
+ nf_unregister_sockopt(&ip_vs_sockopts);
+ return ret;
+ }
+
proc_net_fops_create(&init_net, "ip_vs", 0, &ip_vs_info_fops);
proc_net_fops_create(&init_net, "ip_vs_stats",0, &ip_vs_stats_fops);
@@ -2368,6 +3245,7 @@ void ip_vs_control_cleanup(void)
unregister_sysctl_table(sysctl_header);
proc_net_remove(&init_net, "ip_vs_stats");
proc_net_remove(&init_net, "ip_vs");
+ ip_vs_genl_unregister();
nf_unregister_sockopt(&ip_vs_sockopts);
LeaveFunction(2);
}
--
1.5.4.5
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCHv3 0/2] IPVS: Add Generic Netlink configuration interface
2008-08-14 4:12 ` Simon Horman
@ 2008-08-14 9:34 ` Julius Volz
0 siblings, 0 replies; 23+ messages in thread
From: Julius Volz @ 2008-08-14 9:34 UTC (permalink / raw)
To: Simon Horman; +Cc: julius.volz, netdev, lvs-devel, kaber, davem, tgraf, vbusam
On Thu, Aug 14, 2008 at 6:12 AM, Simon Horman <horms@verge.net.au> wrote:
> I tried to stress this code a bit more and nothing fell off.
Good, thanks!
> I'm pretty happy with them, though Sven's comment about error checking
> needs attention.
Yes, I just posted a fixed version.
Julius
--
Google Switzerland GmbH
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCHv3 2/2] IPVS: Add genetlink interface implementation
2008-08-14 9:32 ` Julius Volz
@ 2008-08-14 9:52 ` Simon Horman
2008-08-14 10:04 ` Sven Wegener
1 sibling, 0 replies; 23+ messages in thread
From: Simon Horman @ 2008-08-14 9:52 UTC (permalink / raw)
To: Julius Volz; +Cc: Sven Wegener, netdev, lvs-devel, kaber, davem, tgraf, vbusam
On Thu, Aug 14, 2008 at 11:32:02AM +0200, Julius Volz wrote:
> Hi Sven,
>
> On Wed, Aug 13, 2008 at 11:51:06PM +0200, Sven Wegener wrote:
> > On Fri, 8 Aug 2008, Julius Volz wrote:
> > > +static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info)
> > > +{
> > > + struct sk_buff *msg;
> > > + void *reply;
> > > + int ret, cmd, reply_cmd;
> > > +
> > > + mutex_lock(&__ip_vs_mutex);
> > > +
> > > + cmd = info->genlhdr->cmd;
> > > +
> > > + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
> > > + if (!msg) {
> > > + ret = -ENOMEM;
> > > + goto out_err;
> >
> > Here you want out...
> >
> > > + }
> > > +
> > > + if (cmd == IPVS_CMD_GET_SERVICE)
> > > + reply_cmd = IPVS_CMD_NEW_SERVICE;
> > > + else if (cmd == IPVS_CMD_GET_INFO)
> > > + reply_cmd = IPVS_CMD_SET_INFO;
> > > + else if (cmd == IPVS_CMD_GET_CONFIG)
> > > + reply_cmd = IPVS_CMD_SET_CONFIG;
> > > + else {
> > > + IP_VS_ERR("unknown Generic Netlink command\n");
> > > + ret = -EINVAL;
> > > + goto out;
> >
> > ..and here you want out_error, to not leak msg.
>
> Ouch, thanks! Fixed this and locked the mutex later. I also removed the
> "if (msg)" from out_err, as it becomes unneeded now. Here's the updated
> patch:
>
> -------------
> Add the implementation of the new Generic Netlink interface to IPVS and
> keep the old set/getsockopt interface for userspace backwards
> compatibility.
>
> Signed-off-by: Julius Volz <juliusv@google.com>
Acked-by: Simon Horman <horms@verge.net.au>
(ditto for the other patch in the series)
> 1 files changed, 878 insertions(+), 0 deletions(-)
>
> diff --git a/net/ipv4/ipvs/ip_vs_ctl.c b/net/ipv4/ipvs/ip_vs_ctl.c
> index 6379705..63a1cbb 100644
> --- a/net/ipv4/ipvs/ip_vs_ctl.c
> +++ b/net/ipv4/ipvs/ip_vs_ctl.c
> @@ -37,6 +37,7 @@
> #include <net/ip.h>
> #include <net/route.h>
> #include <net/sock.h>
> +#include <net/genetlink.h>
>
> #include <asm/uaccess.h>
>
> @@ -2320,6 +2321,875 @@ static struct nf_sockopt_ops ip_vs_sockopts = {
> .owner = THIS_MODULE,
> };
>
> +/*
> + * Generic Netlink interface
> + */
> +
> +/* IPVS genetlink family */
> +static struct genl_family ip_vs_genl_family = {
> + .id = GENL_ID_GENERATE,
> + .hdrsize = 0,
> + .name = IPVS_GENL_NAME,
> + .version = IPVS_GENL_VERSION,
> + .maxattr = IPVS_CMD_MAX,
> +};
> +
> +/* Policy used for first-level command attributes */
> +static const struct nla_policy ip_vs_cmd_policy[IPVS_CMD_ATTR_MAX + 1] = {
> + [IPVS_CMD_ATTR_SERVICE] = { .type = NLA_NESTED },
> + [IPVS_CMD_ATTR_DEST] = { .type = NLA_NESTED },
> + [IPVS_CMD_ATTR_DAEMON] = { .type = NLA_NESTED },
> + [IPVS_CMD_ATTR_TIMEOUT_TCP] = { .type = NLA_U32 },
> + [IPVS_CMD_ATTR_TIMEOUT_TCP_FIN] = { .type = NLA_U32 },
> + [IPVS_CMD_ATTR_TIMEOUT_UDP] = { .type = NLA_U32 },
> +};
> +
> +/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_DAEMON */
> +static const struct nla_policy ip_vs_daemon_policy[IPVS_DAEMON_ATTR_MAX + 1] = {
> + [IPVS_DAEMON_ATTR_STATE] = { .type = NLA_U32 },
> + [IPVS_DAEMON_ATTR_MCAST_IFN] = { .type = NLA_NUL_STRING,
> + .len = IP_VS_IFNAME_MAXLEN },
> + [IPVS_DAEMON_ATTR_SYNC_ID] = { .type = NLA_U32 },
> +};
> +
> +/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_SERVICE */
> +static const struct nla_policy ip_vs_svc_policy[IPVS_SVC_ATTR_MAX + 1] = {
> + [IPVS_SVC_ATTR_AF] = { .type = NLA_U16 },
> + [IPVS_SVC_ATTR_PROTOCOL] = { .type = NLA_U16 },
> + [IPVS_SVC_ATTR_ADDR] = { .type = NLA_BINARY,
> + .len = sizeof(union nf_inet_addr) },
> + [IPVS_SVC_ATTR_PORT] = { .type = NLA_U16 },
> + [IPVS_SVC_ATTR_FWMARK] = { .type = NLA_U32 },
> + [IPVS_SVC_ATTR_SCHED_NAME] = { .type = NLA_NUL_STRING,
> + .len = IP_VS_SCHEDNAME_MAXLEN },
> + [IPVS_SVC_ATTR_FLAGS] = { .type = NLA_BINARY,
> + .len = sizeof(struct ip_vs_flags) },
> + [IPVS_SVC_ATTR_TIMEOUT] = { .type = NLA_U32 },
> + [IPVS_SVC_ATTR_NETMASK] = { .type = NLA_U32 },
> + [IPVS_SVC_ATTR_STATS] = { .type = NLA_NESTED },
> +};
> +
> +/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_DEST */
> +static const struct nla_policy ip_vs_dest_policy[IPVS_DEST_ATTR_MAX + 1] = {
> + [IPVS_DEST_ATTR_ADDR] = { .type = NLA_BINARY,
> + .len = sizeof(union nf_inet_addr) },
> + [IPVS_DEST_ATTR_PORT] = { .type = NLA_U16 },
> + [IPVS_DEST_ATTR_FWD_METHOD] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_WEIGHT] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_U_THRESH] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_L_THRESH] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_ACTIVE_CONNS] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_INACT_CONNS] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_PERSIST_CONNS] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_STATS] = { .type = NLA_NESTED },
> +};
> +
> +static int ip_vs_genl_fill_stats(struct sk_buff *skb, int container_type,
> + struct ip_vs_stats *stats)
> +{
> + struct nlattr *nl_stats = nla_nest_start(skb, container_type);
> + if (!nl_stats)
> + return -EMSGSIZE;
> +
> + spin_lock_bh(&stats->lock);
> +
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_CONNS, stats->conns);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_INPKTS, stats->inpkts);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTPKTS, stats->outpkts);
> + NLA_PUT_U64(skb, IPVS_STATS_ATTR_INBYTES, stats->inbytes);
> + NLA_PUT_U64(skb, IPVS_STATS_ATTR_OUTBYTES, stats->outbytes);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_CPS, stats->cps);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_INPPS, stats->inpps);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTPPS, stats->outpps);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_INBPS, stats->inbps);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTBPS, stats->outbps);
> +
> + spin_unlock_bh(&stats->lock);
> +
> + nla_nest_end(skb, nl_stats);
> +
> + return 0;
> +
> +nla_put_failure:
> + spin_unlock_bh(&stats->lock);
> + nla_nest_cancel(skb, nl_stats);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_fill_service(struct sk_buff *skb,
> + struct ip_vs_service *svc)
> +{
> + struct nlattr *nl_service;
> + struct ip_vs_flags flags = { .flags = svc->flags,
> + .mask = ~0 };
> +
> + nl_service = nla_nest_start(skb, IPVS_CMD_ATTR_SERVICE);
> + if (!nl_service)
> + return -EMSGSIZE;
> +
> + NLA_PUT_U16(skb, IPVS_SVC_ATTR_AF, AF_INET);
> +
> + if (svc->fwmark) {
> + NLA_PUT_U32(skb, IPVS_SVC_ATTR_FWMARK, svc->fwmark);
> + } else {
> + NLA_PUT_U16(skb, IPVS_SVC_ATTR_PROTOCOL, svc->protocol);
> + NLA_PUT(skb, IPVS_SVC_ATTR_ADDR, sizeof(svc->addr), &svc->addr);
> + NLA_PUT_U16(skb, IPVS_SVC_ATTR_PORT, svc->port);
> + }
> +
> + NLA_PUT_STRING(skb, IPVS_SVC_ATTR_SCHED_NAME, svc->scheduler->name);
> + NLA_PUT(skb, IPVS_SVC_ATTR_FLAGS, sizeof(flags), &flags);
> + NLA_PUT_U32(skb, IPVS_SVC_ATTR_TIMEOUT, svc->timeout / HZ);
> + NLA_PUT_U32(skb, IPVS_SVC_ATTR_NETMASK, svc->netmask);
> +
> + if (ip_vs_genl_fill_stats(skb, IPVS_SVC_ATTR_STATS, &svc->stats))
> + goto nla_put_failure;
> +
> + nla_nest_end(skb, nl_service);
> +
> + return 0;
> +
> +nla_put_failure:
> + nla_nest_cancel(skb, nl_service);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_dump_service(struct sk_buff *skb,
> + struct ip_vs_service *svc,
> + struct netlink_callback *cb)
> +{
> + void *hdr;
> +
> + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
> + &ip_vs_genl_family, NLM_F_MULTI,
> + IPVS_CMD_NEW_SERVICE);
> + if (!hdr)
> + return -EMSGSIZE;
> +
> + if (ip_vs_genl_fill_service(skb, svc) < 0)
> + goto nla_put_failure;
> +
> + return genlmsg_end(skb, hdr);
> +
> +nla_put_failure:
> + genlmsg_cancel(skb, hdr);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_dump_services(struct sk_buff *skb,
> + struct netlink_callback *cb)
> +{
> + int idx = 0, i;
> + int start = cb->args[0];
> + struct ip_vs_service *svc;
> +
> + mutex_lock(&__ip_vs_mutex);
> + for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) {
> + list_for_each_entry(svc, &ip_vs_svc_table[i], s_list) {
> + if (++idx <= start)
> + continue;
> + if (ip_vs_genl_dump_service(skb, svc, cb) < 0) {
> + idx--;
> + goto nla_put_failure;
> + }
> + }
> + }
> +
> + for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) {
> + list_for_each_entry(svc, &ip_vs_svc_fwm_table[i], f_list) {
> + if (++idx <= start)
> + continue;
> + if (ip_vs_genl_dump_service(skb, svc, cb) < 0) {
> + idx--;
> + goto nla_put_failure;
> + }
> + }
> + }
> +
> +nla_put_failure:
> + mutex_unlock(&__ip_vs_mutex);
> + cb->args[0] = idx;
> +
> + return skb->len;
> +}
> +
> +static int ip_vs_genl_parse_service(struct ip_vs_service_user *usvc,
> + struct nlattr *nla, int full_entry)
> +{
> + struct nlattr *attrs[IPVS_SVC_ATTR_MAX + 1];
> + struct nlattr *nla_af, *nla_port, *nla_fwmark, *nla_protocol, *nla_addr;
> +
> + /* Parse mandatory identifying service fields first */
> + if (nla == NULL ||
> + nla_parse_nested(attrs, IPVS_SVC_ATTR_MAX, nla, ip_vs_svc_policy))
> + return -EINVAL;
> +
> + nla_af = attrs[IPVS_SVC_ATTR_AF];
> + nla_protocol = attrs[IPVS_SVC_ATTR_PROTOCOL];
> + nla_addr = attrs[IPVS_SVC_ATTR_ADDR];
> + nla_port = attrs[IPVS_SVC_ATTR_PORT];
> + nla_fwmark = attrs[IPVS_SVC_ATTR_FWMARK];
> +
> + if (!(nla_af && (nla_fwmark || (nla_port && nla_protocol && nla_addr))))
> + return -EINVAL;
> +
> + /* For now, only support IPv4 */
> + if (nla_get_u16(nla_af) != AF_INET)
> + return -EAFNOSUPPORT;
> +
> + if (nla_fwmark) {
> + usvc->protocol = IPPROTO_TCP;
> + usvc->fwmark = nla_get_u32(nla_fwmark);
> + } else {
> + usvc->protocol = nla_get_u16(nla_protocol);
> + nla_memcpy(&usvc->addr, nla_addr, sizeof(usvc->addr));
> + usvc->port = nla_get_u16(nla_port);
> + usvc->fwmark = 0;
> + }
> +
> + /* If a full entry was requested, check for the additional fields */
> + if (full_entry) {
> + struct nlattr *nla_sched, *nla_flags, *nla_timeout,
> + *nla_netmask;
> + struct ip_vs_flags flags;
> + struct ip_vs_service *svc;
> +
> + nla_sched = attrs[IPVS_SVC_ATTR_SCHED_NAME];
> + nla_flags = attrs[IPVS_SVC_ATTR_FLAGS];
> + nla_timeout = attrs[IPVS_SVC_ATTR_TIMEOUT];
> + nla_netmask = attrs[IPVS_SVC_ATTR_NETMASK];
> +
> + if (!(nla_sched && nla_flags && nla_timeout && nla_netmask))
> + return -EINVAL;
> +
> + nla_memcpy(&flags, nla_flags, sizeof(flags));
> +
> + /* prefill flags from service if it already exists */
> + if (usvc->fwmark)
> + svc = __ip_vs_svc_fwm_get(usvc->fwmark);
> + else
> + svc = __ip_vs_service_get(usvc->protocol, usvc->addr,
> + usvc->port);
> + if (svc) {
> + usvc->flags = svc->flags;
> + ip_vs_service_put(svc);
> + } else
> + usvc->flags = 0;
> +
> + /* set new flags from userland */
> + usvc->flags = (usvc->flags & ~flags.mask) |
> + (flags.flags & flags.mask);
> +
> + strlcpy(usvc->sched_name, nla_data(nla_sched),
> + sizeof(usvc->sched_name));
> + usvc->timeout = nla_get_u32(nla_timeout);
> + usvc->netmask = nla_get_u32(nla_netmask);
> + }
> +
> + return 0;
> +}
> +
> +static struct ip_vs_service *ip_vs_genl_find_service(struct nlattr *nla)
> +{
> + struct ip_vs_service_user usvc;
> + int ret;
> +
> + ret = ip_vs_genl_parse_service(&usvc, nla, 0);
> + if (ret)
> + return ERR_PTR(ret);
> +
> + if (usvc.fwmark)
> + return __ip_vs_svc_fwm_get(usvc.fwmark);
> + else
> + return __ip_vs_service_get(usvc.protocol, usvc.addr,
> + usvc.port);
> +}
> +
> +static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest)
> +{
> + struct nlattr *nl_dest;
> +
> + nl_dest = nla_nest_start(skb, IPVS_CMD_ATTR_DEST);
> + if (!nl_dest)
> + return -EMSGSIZE;
> +
> + NLA_PUT(skb, IPVS_DEST_ATTR_ADDR, sizeof(dest->addr), &dest->addr);
> + NLA_PUT_U16(skb, IPVS_DEST_ATTR_PORT, dest->port);
> +
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_FWD_METHOD,
> + atomic_read(&dest->conn_flags) & IP_VS_CONN_F_FWD_MASK);
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_WEIGHT, atomic_read(&dest->weight));
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_U_THRESH, dest->u_threshold);
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_L_THRESH, dest->l_threshold);
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_ACTIVE_CONNS,
> + atomic_read(&dest->activeconns));
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_INACT_CONNS,
> + atomic_read(&dest->inactconns));
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_PERSIST_CONNS,
> + atomic_read(&dest->persistconns));
> +
> + if (ip_vs_genl_fill_stats(skb, IPVS_DEST_ATTR_STATS, &dest->stats))
> + goto nla_put_failure;
> +
> + nla_nest_end(skb, nl_dest);
> +
> + return 0;
> +
> +nla_put_failure:
> + nla_nest_cancel(skb, nl_dest);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_dump_dest(struct sk_buff *skb, struct ip_vs_dest *dest,
> + struct netlink_callback *cb)
> +{
> + void *hdr;
> +
> + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
> + &ip_vs_genl_family, NLM_F_MULTI,
> + IPVS_CMD_NEW_DEST);
> + if (!hdr)
> + return -EMSGSIZE;
> +
> + if (ip_vs_genl_fill_dest(skb, dest) < 0)
> + goto nla_put_failure;
> +
> + return genlmsg_end(skb, hdr);
> +
> +nla_put_failure:
> + genlmsg_cancel(skb, hdr);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_dump_dests(struct sk_buff *skb,
> + struct netlink_callback *cb)
> +{
> + int idx = 0;
> + int start = cb->args[0];
> + struct ip_vs_service *svc;
> + struct ip_vs_dest *dest;
> + struct nlattr *attrs[IPVS_CMD_ATTR_MAX + 1];
> +
> + mutex_lock(&__ip_vs_mutex);
> +
> + /* Try to find the service for which to dump destinations */
> + if (nlmsg_parse(cb->nlh, GENL_HDRLEN, attrs,
> + IPVS_CMD_ATTR_MAX, ip_vs_cmd_policy))
> + goto out_err;
> +
> + svc = ip_vs_genl_find_service(attrs[IPVS_CMD_ATTR_SERVICE]);
> + if (IS_ERR(svc) || svc == NULL)
> + goto out_err;
> +
> + /* Dump the destinations */
> + list_for_each_entry(dest, &svc->destinations, n_list) {
> + if (++idx <= start)
> + continue;
> + if (ip_vs_genl_dump_dest(skb, dest, cb) < 0) {
> + idx--;
> + goto nla_put_failure;
> + }
> + }
> +
> +nla_put_failure:
> + cb->args[0] = idx;
> + ip_vs_service_put(svc);
> +
> +out_err:
> + mutex_unlock(&__ip_vs_mutex);
> +
> + return skb->len;
> +}
> +
> +static int ip_vs_genl_parse_dest(struct ip_vs_dest_user *udest,
> + struct nlattr *nla, int full_entry)
> +{
> + struct nlattr *attrs[IPVS_DEST_ATTR_MAX + 1];
> + struct nlattr *nla_addr, *nla_port;
> +
> + /* Parse mandatory identifying destination fields first */
> + if (nla == NULL ||
> + nla_parse_nested(attrs, IPVS_DEST_ATTR_MAX, nla, ip_vs_dest_policy))
> + return -EINVAL;
> +
> + nla_addr = attrs[IPVS_DEST_ATTR_ADDR];
> + nla_port = attrs[IPVS_DEST_ATTR_PORT];
> +
> + if (!(nla_addr && nla_port))
> + return -EINVAL;
> +
> + nla_memcpy(&udest->addr, nla_addr, sizeof(udest->addr));
> + udest->port = nla_get_u16(nla_port);
> +
> + /* If a full entry was requested, check for the additional fields */
> + if (full_entry) {
> + struct nlattr *nla_fwd, *nla_weight, *nla_u_thresh,
> + *nla_l_thresh;
> +
> + nla_fwd = attrs[IPVS_DEST_ATTR_FWD_METHOD];
> + nla_weight = attrs[IPVS_DEST_ATTR_WEIGHT];
> + nla_u_thresh = attrs[IPVS_DEST_ATTR_U_THRESH];
> + nla_l_thresh = attrs[IPVS_DEST_ATTR_L_THRESH];
> +
> + if (!(nla_fwd && nla_weight && nla_u_thresh && nla_l_thresh))
> + return -EINVAL;
> +
> + udest->conn_flags = nla_get_u32(nla_fwd)
> + & IP_VS_CONN_F_FWD_MASK;
> + udest->weight = nla_get_u32(nla_weight);
> + udest->u_threshold = nla_get_u32(nla_u_thresh);
> + udest->l_threshold = nla_get_u32(nla_l_thresh);
> + }
> +
> + return 0;
> +}
> +
> +static int ip_vs_genl_fill_daemon(struct sk_buff *skb, __be32 state,
> + const char *mcast_ifn, __be32 syncid)
> +{
> + struct nlattr *nl_daemon;
> +
> + nl_daemon = nla_nest_start(skb, IPVS_CMD_ATTR_DAEMON);
> + if (!nl_daemon)
> + return -EMSGSIZE;
> +
> + NLA_PUT_U32(skb, IPVS_DAEMON_ATTR_STATE, state);
> + NLA_PUT_STRING(skb, IPVS_DAEMON_ATTR_MCAST_IFN, mcast_ifn);
> + NLA_PUT_U32(skb, IPVS_DAEMON_ATTR_SYNC_ID, syncid);
> +
> + nla_nest_end(skb, nl_daemon);
> +
> + return 0;
> +
> +nla_put_failure:
> + nla_nest_cancel(skb, nl_daemon);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_dump_daemon(struct sk_buff *skb, __be32 state,
> + const char *mcast_ifn, __be32 syncid,
> + struct netlink_callback *cb)
> +{
> + void *hdr;
> + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
> + &ip_vs_genl_family, NLM_F_MULTI,
> + IPVS_CMD_NEW_DAEMON);
> + if (!hdr)
> + return -EMSGSIZE;
> +
> + if (ip_vs_genl_fill_daemon(skb, state, mcast_ifn, syncid))
> + goto nla_put_failure;
> +
> + return genlmsg_end(skb, hdr);
> +
> +nla_put_failure:
> + genlmsg_cancel(skb, hdr);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_dump_daemons(struct sk_buff *skb,
> + struct netlink_callback *cb)
> +{
> + mutex_lock(&__ip_vs_mutex);
> + if ((ip_vs_sync_state & IP_VS_STATE_MASTER) && !cb->args[0]) {
> + if (ip_vs_genl_dump_daemon(skb, IP_VS_STATE_MASTER,
> + ip_vs_master_mcast_ifn,
> + ip_vs_master_syncid, cb) < 0)
> + goto nla_put_failure;
> +
> + cb->args[0] = 1;
> + }
> +
> + if ((ip_vs_sync_state & IP_VS_STATE_BACKUP) && !cb->args[1]) {
> + if (ip_vs_genl_dump_daemon(skb, IP_VS_STATE_BACKUP,
> + ip_vs_backup_mcast_ifn,
> + ip_vs_backup_syncid, cb) < 0)
> + goto nla_put_failure;
> +
> + cb->args[1] = 1;
> + }
> +
> +nla_put_failure:
> + mutex_unlock(&__ip_vs_mutex);
> +
> + return skb->len;
> +}
> +
> +static int ip_vs_genl_new_daemon(struct nlattr **attrs)
> +{
> + if (!(attrs[IPVS_DAEMON_ATTR_STATE] &&
> + attrs[IPVS_DAEMON_ATTR_MCAST_IFN] &&
> + attrs[IPVS_DAEMON_ATTR_SYNC_ID]))
> + return -EINVAL;
> +
> + return start_sync_thread(nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]),
> + nla_data(attrs[IPVS_DAEMON_ATTR_MCAST_IFN]),
> + nla_get_u32(attrs[IPVS_DAEMON_ATTR_SYNC_ID]));
> +}
> +
> +static int ip_vs_genl_del_daemon(struct nlattr **attrs)
> +{
> + if (!attrs[IPVS_DAEMON_ATTR_STATE])
> + return -EINVAL;
> +
> + return stop_sync_thread(nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]));
> +}
> +
> +static int ip_vs_genl_set_config(struct nlattr **attrs)
> +{
> + struct ip_vs_timeout_user t;
> +
> + __ip_vs_get_timeouts(&t);
> +
> + if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP])
> + t.tcp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP]);
> +
> + if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN])
> + t.tcp_fin_timeout =
> + nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN]);
> +
> + if (attrs[IPVS_CMD_ATTR_TIMEOUT_UDP])
> + t.udp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_UDP]);
> +
> + return ip_vs_set_timeout(&t);
> +}
> +
> +static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info)
> +{
> + struct ip_vs_service *svc = NULL;
> + struct ip_vs_service_user usvc;
> + struct ip_vs_dest_user udest;
> + int ret = 0, cmd;
> + int need_full_svc = 0, need_full_dest = 0;
> +
> + cmd = info->genlhdr->cmd;
> +
> + mutex_lock(&__ip_vs_mutex);
> +
> + if (cmd == IPVS_CMD_FLUSH) {
> + ret = ip_vs_flush();
> + goto out;
> + } else if (cmd == IPVS_CMD_SET_CONFIG) {
> + ret = ip_vs_genl_set_config(info->attrs);
> + goto out;
> + } else if (cmd == IPVS_CMD_NEW_DAEMON ||
> + cmd == IPVS_CMD_DEL_DAEMON) {
> +
> + struct nlattr *daemon_attrs[IPVS_DAEMON_ATTR_MAX + 1];
> +
> + if (!info->attrs[IPVS_CMD_ATTR_DAEMON] ||
> + nla_parse_nested(daemon_attrs, IPVS_DAEMON_ATTR_MAX,
> + info->attrs[IPVS_CMD_ATTR_DAEMON],
> + ip_vs_daemon_policy)) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + if (cmd == IPVS_CMD_NEW_DAEMON)
> + ret = ip_vs_genl_new_daemon(daemon_attrs);
> + else
> + ret = ip_vs_genl_del_daemon(daemon_attrs);
> + goto out;
> + } else if (cmd == IPVS_CMD_ZERO &&
> + !info->attrs[IPVS_CMD_ATTR_SERVICE]) {
> + ret = ip_vs_zero_all();
> + goto out;
> + }
> +
> + /* All following commands require a service argument, so check if we
> + * received a valid one. We need a full service specification when
> + * adding / editing a service. Only identifying members otherwise. */
> + if (cmd == IPVS_CMD_NEW_SERVICE || cmd == IPVS_CMD_SET_SERVICE)
> + need_full_svc = 1;
> +
> + ret = ip_vs_genl_parse_service(&usvc,
> + info->attrs[IPVS_CMD_ATTR_SERVICE],
> + need_full_svc);
> + if (ret)
> + goto out;
> +
> + /* Lookup the exact service by <protocol, addr, port> or fwmark */
> + if (usvc.fwmark == 0)
> + svc = __ip_vs_service_get(usvc.protocol, usvc.addr, usvc.port);
> + else
> + svc = __ip_vs_svc_fwm_get(usvc.fwmark);
> +
> + /* Unless we're adding a new service, the service must already exist */
> + if ((cmd != IPVS_CMD_NEW_SERVICE) && (svc == NULL)) {
> + ret = -ESRCH;
> + goto out;
> + }
> +
> + /* Destination commands require a valid destination argument. For
> + * adding / editing a destination, we need a full destination
> + * specification. */
> + if (cmd == IPVS_CMD_NEW_DEST || cmd == IPVS_CMD_SET_DEST ||
> + cmd == IPVS_CMD_DEL_DEST) {
> + if (cmd != IPVS_CMD_DEL_DEST)
> + need_full_dest = 1;
> +
> + ret = ip_vs_genl_parse_dest(&udest,
> + info->attrs[IPVS_CMD_ATTR_DEST],
> + need_full_dest);
> + if (ret)
> + goto out;
> + }
> +
> + switch (cmd) {
> + case IPVS_CMD_NEW_SERVICE:
> + if (svc == NULL)
> + ret = ip_vs_add_service(&usvc, &svc);
> + else
> + ret = -EEXIST;
> + break;
> + case IPVS_CMD_SET_SERVICE:
> + ret = ip_vs_edit_service(svc, &usvc);
> + break;
> + case IPVS_CMD_DEL_SERVICE:
> + ret = ip_vs_del_service(svc);
> + break;
> + case IPVS_CMD_NEW_DEST:
> + ret = ip_vs_add_dest(svc, &udest);
> + break;
> + case IPVS_CMD_SET_DEST:
> + ret = ip_vs_edit_dest(svc, &udest);
> + break;
> + case IPVS_CMD_DEL_DEST:
> + ret = ip_vs_del_dest(svc, &udest);
> + break;
> + case IPVS_CMD_ZERO:
> + ret = ip_vs_zero_service(svc);
> + break;
> + default:
> + ret = -EINVAL;
> + }
> +
> +out:
> + if (svc)
> + ip_vs_service_put(svc);
> + mutex_unlock(&__ip_vs_mutex);
> +
> + return ret;
> +}
> +
> +static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info)
> +{
> + struct sk_buff *msg;
> + void *reply;
> + int ret, cmd, reply_cmd;
> +
> + cmd = info->genlhdr->cmd;
> +
> + if (cmd == IPVS_CMD_GET_SERVICE)
> + reply_cmd = IPVS_CMD_NEW_SERVICE;
> + else if (cmd == IPVS_CMD_GET_INFO)
> + reply_cmd = IPVS_CMD_SET_INFO;
> + else if (cmd == IPVS_CMD_GET_CONFIG)
> + reply_cmd = IPVS_CMD_SET_CONFIG;
> + else {
> + IP_VS_ERR("unknown Generic Netlink command\n");
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
> + if (!msg) {
> + ret = -ENOMEM;
> + goto out;
> + }
> +
> + reply = genlmsg_put_reply(msg, info, &ip_vs_genl_family, 0, reply_cmd);
> + if (reply == NULL)
> + goto nla_put_failure;
> +
> + mutex_lock(&__ip_vs_mutex);
> +
> + switch (cmd) {
> + case IPVS_CMD_GET_SERVICE:
> + {
> + struct ip_vs_service *svc;
> +
> + svc = ip_vs_genl_find_service(info->attrs[IPVS_CMD_ATTR_SERVICE]);
> + if (IS_ERR(svc)) {
> + ret = PTR_ERR(svc);
> + goto out_err;
> + } else if (svc) {
> + ret = ip_vs_genl_fill_service(msg, svc);
> + ip_vs_service_put(svc);
> + if (ret)
> + goto nla_put_failure;
> + } else {
> + ret = -ESRCH;
> + goto out_err;
> + }
> +
> + break;
> + }
> +
> + case IPVS_CMD_GET_CONFIG:
> + {
> + struct ip_vs_timeout_user t;
> +
> + __ip_vs_get_timeouts(&t);
> +#ifdef CONFIG_IP_VS_PROTO_TCP
> + NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP, t.tcp_timeout);
> + NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP_FIN,
> + t.tcp_fin_timeout);
> +#endif
> +#ifdef CONFIG_IP_VS_PROTO_UDP
> + NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_UDP, t.udp_timeout);
> +#endif
> +
> + break;
> + }
> +
> + case IPVS_CMD_GET_INFO:
> + NLA_PUT_U32(msg, IPVS_INFO_ATTR_VERSION, IP_VS_VERSION_CODE);
> + NLA_PUT_U32(msg, IPVS_INFO_ATTR_CONN_TAB_SIZE,
> + IP_VS_CONN_TAB_SIZE);
> + break;
> + }
> +
> + genlmsg_end(msg, reply);
> + ret = genlmsg_unicast(msg, info->snd_pid);
> + goto out;
> +
> +nla_put_failure:
> + IP_VS_ERR("not enough space in Netlink message\n");
> + ret = -EMSGSIZE;
> +
> +out_err:
> + nlmsg_free(msg);
> +out:
> + mutex_unlock(&__ip_vs_mutex);
> +
> + return ret;
> +}
> +
> +
> +static struct genl_ops ip_vs_genl_ops[] __read_mostly = {
> + {
> + .cmd = IPVS_CMD_NEW_SERVICE,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_SET_SERVICE,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_DEL_SERVICE,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_GET_SERVICE,
> + .flags = GENL_ADMIN_PERM,
> + .doit = ip_vs_genl_get_cmd,
> + .dumpit = ip_vs_genl_dump_services,
> + .policy = ip_vs_cmd_policy,
> + },
> + {
> + .cmd = IPVS_CMD_NEW_DEST,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_SET_DEST,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_DEL_DEST,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_GET_DEST,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .dumpit = ip_vs_genl_dump_dests,
> + },
> + {
> + .cmd = IPVS_CMD_NEW_DAEMON,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_DEL_DAEMON,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_GET_DAEMON,
> + .flags = GENL_ADMIN_PERM,
> + .dumpit = ip_vs_genl_dump_daemons,
> + },
> + {
> + .cmd = IPVS_CMD_SET_CONFIG,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_GET_CONFIG,
> + .flags = GENL_ADMIN_PERM,
> + .doit = ip_vs_genl_get_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_GET_INFO,
> + .flags = GENL_ADMIN_PERM,
> + .doit = ip_vs_genl_get_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_ZERO,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_FLUSH,
> + .flags = GENL_ADMIN_PERM,
> + .doit = ip_vs_genl_set_cmd,
> + },
> +};
> +
> +int ip_vs_genl_register(void)
> +{
> + int ret, i;
> +
> + ret = genl_register_family(&ip_vs_genl_family);
> + if (ret)
> + return ret;
> +
> + for (i = 0; i < ARRAY_SIZE(ip_vs_genl_ops); i++) {
> + ret = genl_register_ops(&ip_vs_genl_family, &ip_vs_genl_ops[i]);
> + if (ret)
> + goto err_out;
> + }
> + return 0;
> +
> +err_out:
> + genl_unregister_family(&ip_vs_genl_family);
> + return ret;
> +}
> +
> +void ip_vs_genl_unregister(void)
> +{
> + genl_unregister_family(&ip_vs_genl_family);
> +}
> +
> +/* End of Generic Netlink interface definitions */
> +
>
> int __init ip_vs_control_init(void)
> {
> @@ -2334,6 +3204,13 @@ int __init ip_vs_control_init(void)
> return ret;
> }
>
> + ret = ip_vs_genl_register();
> + if (ret) {
> + IP_VS_ERR("cannot register Generic Netlink interface.\n");
> + nf_unregister_sockopt(&ip_vs_sockopts);
> + return ret;
> + }
> +
> proc_net_fops_create(&init_net, "ip_vs", 0, &ip_vs_info_fops);
> proc_net_fops_create(&init_net, "ip_vs_stats",0, &ip_vs_stats_fops);
>
> @@ -2368,6 +3245,7 @@ void ip_vs_control_cleanup(void)
> unregister_sysctl_table(sysctl_header);
> proc_net_remove(&init_net, "ip_vs_stats");
> proc_net_remove(&init_net, "ip_vs");
> + ip_vs_genl_unregister();
> nf_unregister_sockopt(&ip_vs_sockopts);
> LeaveFunction(2);
> }
> --
> 1.5.4.5
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCHv3 2/2] IPVS: Add genetlink interface implementation
2008-08-14 9:32 ` Julius Volz
2008-08-14 9:52 ` Simon Horman
@ 2008-08-14 10:04 ` Sven Wegener
2008-08-14 10:27 ` Julius Volz
1 sibling, 1 reply; 23+ messages in thread
From: Sven Wegener @ 2008-08-14 10:04 UTC (permalink / raw)
To: Julius Volz; +Cc: netdev, lvs-devel, horms, kaber, davem, tgraf, vbusam
On Thu, 14 Aug 2008, Julius Volz wrote:
> On Wed, Aug 13, 2008 at 11:51:06PM +0200, Sven Wegener wrote:
> > On Fri, 8 Aug 2008, Julius Volz wrote:
> > > +static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info)
> > > +{
> > > + struct sk_buff *msg;
> > > + void *reply;
> > > + int ret, cmd, reply_cmd;
> > > +
> > > + mutex_lock(&__ip_vs_mutex);
> > > +
> > > + cmd = info->genlhdr->cmd;
> > > +
> > > + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
> > > + if (!msg) {
> > > + ret = -ENOMEM;
> > > + goto out_err;
> >
> > Here you want out...
> >
> > > + }
> > > +
> > > + if (cmd == IPVS_CMD_GET_SERVICE)
> > > + reply_cmd = IPVS_CMD_NEW_SERVICE;
> > > + else if (cmd == IPVS_CMD_GET_INFO)
> > > + reply_cmd = IPVS_CMD_SET_INFO;
> > > + else if (cmd == IPVS_CMD_GET_CONFIG)
> > > + reply_cmd = IPVS_CMD_SET_CONFIG;
> > > + else {
> > > + IP_VS_ERR("unknown Generic Netlink command\n");
> > > + ret = -EINVAL;
> > > + goto out;
> >
> > ..and here you want out_error, to not leak msg.
>
> Ouch, thanks! Fixed this and locked the mutex later. I also removed the
> "if (msg)" from out_err, as it becomes unneeded now. Here's the updated
> patch:
You missed the static on the register and unregister functions down at the
bottom. :) Also see my comments to your change regarding the above issue
down here.
> -------------
> Add the implementation of the new Generic Netlink interface to IPVS and
> keep the old set/getsockopt interface for userspace backwards
> compatibility.
>
> Signed-off-by: Julius Volz <juliusv@google.com>
>
> 1 files changed, 878 insertions(+), 0 deletions(-)
>
> diff --git a/net/ipv4/ipvs/ip_vs_ctl.c b/net/ipv4/ipvs/ip_vs_ctl.c
> index 6379705..63a1cbb 100644
> --- a/net/ipv4/ipvs/ip_vs_ctl.c
> +++ b/net/ipv4/ipvs/ip_vs_ctl.c
> @@ -37,6 +37,7 @@
> #include <net/ip.h>
> #include <net/route.h>
> #include <net/sock.h>
> +#include <net/genetlink.h>
>
> #include <asm/uaccess.h>
>
> @@ -2320,6 +2321,875 @@ static struct nf_sockopt_ops ip_vs_sockopts = {
> .owner = THIS_MODULE,
> };
>
> +/*
> + * Generic Netlink interface
> + */
> +
> +/* IPVS genetlink family */
> +static struct genl_family ip_vs_genl_family = {
> + .id = GENL_ID_GENERATE,
> + .hdrsize = 0,
> + .name = IPVS_GENL_NAME,
> + .version = IPVS_GENL_VERSION,
> + .maxattr = IPVS_CMD_MAX,
> +};
> +
> +/* Policy used for first-level command attributes */
> +static const struct nla_policy ip_vs_cmd_policy[IPVS_CMD_ATTR_MAX + 1] = {
> + [IPVS_CMD_ATTR_SERVICE] = { .type = NLA_NESTED },
> + [IPVS_CMD_ATTR_DEST] = { .type = NLA_NESTED },
> + [IPVS_CMD_ATTR_DAEMON] = { .type = NLA_NESTED },
> + [IPVS_CMD_ATTR_TIMEOUT_TCP] = { .type = NLA_U32 },
> + [IPVS_CMD_ATTR_TIMEOUT_TCP_FIN] = { .type = NLA_U32 },
> + [IPVS_CMD_ATTR_TIMEOUT_UDP] = { .type = NLA_U32 },
> +};
> +
> +/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_DAEMON */
> +static const struct nla_policy ip_vs_daemon_policy[IPVS_DAEMON_ATTR_MAX + 1] = {
> + [IPVS_DAEMON_ATTR_STATE] = { .type = NLA_U32 },
> + [IPVS_DAEMON_ATTR_MCAST_IFN] = { .type = NLA_NUL_STRING,
> + .len = IP_VS_IFNAME_MAXLEN },
> + [IPVS_DAEMON_ATTR_SYNC_ID] = { .type = NLA_U32 },
> +};
> +
> +/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_SERVICE */
> +static const struct nla_policy ip_vs_svc_policy[IPVS_SVC_ATTR_MAX + 1] = {
> + [IPVS_SVC_ATTR_AF] = { .type = NLA_U16 },
> + [IPVS_SVC_ATTR_PROTOCOL] = { .type = NLA_U16 },
> + [IPVS_SVC_ATTR_ADDR] = { .type = NLA_BINARY,
> + .len = sizeof(union nf_inet_addr) },
> + [IPVS_SVC_ATTR_PORT] = { .type = NLA_U16 },
> + [IPVS_SVC_ATTR_FWMARK] = { .type = NLA_U32 },
> + [IPVS_SVC_ATTR_SCHED_NAME] = { .type = NLA_NUL_STRING,
> + .len = IP_VS_SCHEDNAME_MAXLEN },
> + [IPVS_SVC_ATTR_FLAGS] = { .type = NLA_BINARY,
> + .len = sizeof(struct ip_vs_flags) },
> + [IPVS_SVC_ATTR_TIMEOUT] = { .type = NLA_U32 },
> + [IPVS_SVC_ATTR_NETMASK] = { .type = NLA_U32 },
> + [IPVS_SVC_ATTR_STATS] = { .type = NLA_NESTED },
> +};
> +
> +/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_DEST */
> +static const struct nla_policy ip_vs_dest_policy[IPVS_DEST_ATTR_MAX + 1] = {
> + [IPVS_DEST_ATTR_ADDR] = { .type = NLA_BINARY,
> + .len = sizeof(union nf_inet_addr) },
> + [IPVS_DEST_ATTR_PORT] = { .type = NLA_U16 },
> + [IPVS_DEST_ATTR_FWD_METHOD] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_WEIGHT] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_U_THRESH] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_L_THRESH] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_ACTIVE_CONNS] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_INACT_CONNS] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_PERSIST_CONNS] = { .type = NLA_U32 },
> + [IPVS_DEST_ATTR_STATS] = { .type = NLA_NESTED },
> +};
> +
> +static int ip_vs_genl_fill_stats(struct sk_buff *skb, int container_type,
> + struct ip_vs_stats *stats)
> +{
> + struct nlattr *nl_stats = nla_nest_start(skb, container_type);
> + if (!nl_stats)
> + return -EMSGSIZE;
> +
> + spin_lock_bh(&stats->lock);
> +
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_CONNS, stats->conns);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_INPKTS, stats->inpkts);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTPKTS, stats->outpkts);
> + NLA_PUT_U64(skb, IPVS_STATS_ATTR_INBYTES, stats->inbytes);
> + NLA_PUT_U64(skb, IPVS_STATS_ATTR_OUTBYTES, stats->outbytes);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_CPS, stats->cps);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_INPPS, stats->inpps);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTPPS, stats->outpps);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_INBPS, stats->inbps);
> + NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTBPS, stats->outbps);
> +
> + spin_unlock_bh(&stats->lock);
> +
> + nla_nest_end(skb, nl_stats);
> +
> + return 0;
> +
> +nla_put_failure:
> + spin_unlock_bh(&stats->lock);
> + nla_nest_cancel(skb, nl_stats);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_fill_service(struct sk_buff *skb,
> + struct ip_vs_service *svc)
> +{
> + struct nlattr *nl_service;
> + struct ip_vs_flags flags = { .flags = svc->flags,
> + .mask = ~0 };
> +
> + nl_service = nla_nest_start(skb, IPVS_CMD_ATTR_SERVICE);
> + if (!nl_service)
> + return -EMSGSIZE;
> +
> + NLA_PUT_U16(skb, IPVS_SVC_ATTR_AF, AF_INET);
> +
> + if (svc->fwmark) {
> + NLA_PUT_U32(skb, IPVS_SVC_ATTR_FWMARK, svc->fwmark);
> + } else {
> + NLA_PUT_U16(skb, IPVS_SVC_ATTR_PROTOCOL, svc->protocol);
> + NLA_PUT(skb, IPVS_SVC_ATTR_ADDR, sizeof(svc->addr), &svc->addr);
> + NLA_PUT_U16(skb, IPVS_SVC_ATTR_PORT, svc->port);
> + }
> +
> + NLA_PUT_STRING(skb, IPVS_SVC_ATTR_SCHED_NAME, svc->scheduler->name);
> + NLA_PUT(skb, IPVS_SVC_ATTR_FLAGS, sizeof(flags), &flags);
> + NLA_PUT_U32(skb, IPVS_SVC_ATTR_TIMEOUT, svc->timeout / HZ);
> + NLA_PUT_U32(skb, IPVS_SVC_ATTR_NETMASK, svc->netmask);
> +
> + if (ip_vs_genl_fill_stats(skb, IPVS_SVC_ATTR_STATS, &svc->stats))
> + goto nla_put_failure;
> +
> + nla_nest_end(skb, nl_service);
> +
> + return 0;
> +
> +nla_put_failure:
> + nla_nest_cancel(skb, nl_service);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_dump_service(struct sk_buff *skb,
> + struct ip_vs_service *svc,
> + struct netlink_callback *cb)
> +{
> + void *hdr;
> +
> + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
> + &ip_vs_genl_family, NLM_F_MULTI,
> + IPVS_CMD_NEW_SERVICE);
> + if (!hdr)
> + return -EMSGSIZE;
> +
> + if (ip_vs_genl_fill_service(skb, svc) < 0)
> + goto nla_put_failure;
> +
> + return genlmsg_end(skb, hdr);
> +
> +nla_put_failure:
> + genlmsg_cancel(skb, hdr);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_dump_services(struct sk_buff *skb,
> + struct netlink_callback *cb)
> +{
> + int idx = 0, i;
> + int start = cb->args[0];
> + struct ip_vs_service *svc;
> +
> + mutex_lock(&__ip_vs_mutex);
> + for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) {
> + list_for_each_entry(svc, &ip_vs_svc_table[i], s_list) {
> + if (++idx <= start)
> + continue;
> + if (ip_vs_genl_dump_service(skb, svc, cb) < 0) {
> + idx--;
> + goto nla_put_failure;
> + }
> + }
> + }
> +
> + for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) {
> + list_for_each_entry(svc, &ip_vs_svc_fwm_table[i], f_list) {
> + if (++idx <= start)
> + continue;
> + if (ip_vs_genl_dump_service(skb, svc, cb) < 0) {
> + idx--;
> + goto nla_put_failure;
> + }
> + }
> + }
> +
> +nla_put_failure:
> + mutex_unlock(&__ip_vs_mutex);
> + cb->args[0] = idx;
> +
> + return skb->len;
> +}
> +
> +static int ip_vs_genl_parse_service(struct ip_vs_service_user *usvc,
> + struct nlattr *nla, int full_entry)
> +{
> + struct nlattr *attrs[IPVS_SVC_ATTR_MAX + 1];
> + struct nlattr *nla_af, *nla_port, *nla_fwmark, *nla_protocol, *nla_addr;
> +
> + /* Parse mandatory identifying service fields first */
> + if (nla == NULL ||
> + nla_parse_nested(attrs, IPVS_SVC_ATTR_MAX, nla, ip_vs_svc_policy))
> + return -EINVAL;
> +
> + nla_af = attrs[IPVS_SVC_ATTR_AF];
> + nla_protocol = attrs[IPVS_SVC_ATTR_PROTOCOL];
> + nla_addr = attrs[IPVS_SVC_ATTR_ADDR];
> + nla_port = attrs[IPVS_SVC_ATTR_PORT];
> + nla_fwmark = attrs[IPVS_SVC_ATTR_FWMARK];
> +
> + if (!(nla_af && (nla_fwmark || (nla_port && nla_protocol && nla_addr))))
> + return -EINVAL;
> +
> + /* For now, only support IPv4 */
> + if (nla_get_u16(nla_af) != AF_INET)
> + return -EAFNOSUPPORT;
> +
> + if (nla_fwmark) {
> + usvc->protocol = IPPROTO_TCP;
> + usvc->fwmark = nla_get_u32(nla_fwmark);
> + } else {
> + usvc->protocol = nla_get_u16(nla_protocol);
> + nla_memcpy(&usvc->addr, nla_addr, sizeof(usvc->addr));
> + usvc->port = nla_get_u16(nla_port);
> + usvc->fwmark = 0;
> + }
> +
> + /* If a full entry was requested, check for the additional fields */
> + if (full_entry) {
> + struct nlattr *nla_sched, *nla_flags, *nla_timeout,
> + *nla_netmask;
> + struct ip_vs_flags flags;
> + struct ip_vs_service *svc;
> +
> + nla_sched = attrs[IPVS_SVC_ATTR_SCHED_NAME];
> + nla_flags = attrs[IPVS_SVC_ATTR_FLAGS];
> + nla_timeout = attrs[IPVS_SVC_ATTR_TIMEOUT];
> + nla_netmask = attrs[IPVS_SVC_ATTR_NETMASK];
> +
> + if (!(nla_sched && nla_flags && nla_timeout && nla_netmask))
> + return -EINVAL;
> +
> + nla_memcpy(&flags, nla_flags, sizeof(flags));
> +
> + /* prefill flags from service if it already exists */
> + if (usvc->fwmark)
> + svc = __ip_vs_svc_fwm_get(usvc->fwmark);
> + else
> + svc = __ip_vs_service_get(usvc->protocol, usvc->addr,
> + usvc->port);
> + if (svc) {
> + usvc->flags = svc->flags;
> + ip_vs_service_put(svc);
> + } else
> + usvc->flags = 0;
> +
> + /* set new flags from userland */
> + usvc->flags = (usvc->flags & ~flags.mask) |
> + (flags.flags & flags.mask);
> +
> + strlcpy(usvc->sched_name, nla_data(nla_sched),
> + sizeof(usvc->sched_name));
> + usvc->timeout = nla_get_u32(nla_timeout);
> + usvc->netmask = nla_get_u32(nla_netmask);
> + }
> +
> + return 0;
> +}
> +
> +static struct ip_vs_service *ip_vs_genl_find_service(struct nlattr *nla)
> +{
> + struct ip_vs_service_user usvc;
> + int ret;
> +
> + ret = ip_vs_genl_parse_service(&usvc, nla, 0);
> + if (ret)
> + return ERR_PTR(ret);
> +
> + if (usvc.fwmark)
> + return __ip_vs_svc_fwm_get(usvc.fwmark);
> + else
> + return __ip_vs_service_get(usvc.protocol, usvc.addr,
> + usvc.port);
> +}
> +
> +static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest)
> +{
> + struct nlattr *nl_dest;
> +
> + nl_dest = nla_nest_start(skb, IPVS_CMD_ATTR_DEST);
> + if (!nl_dest)
> + return -EMSGSIZE;
> +
> + NLA_PUT(skb, IPVS_DEST_ATTR_ADDR, sizeof(dest->addr), &dest->addr);
> + NLA_PUT_U16(skb, IPVS_DEST_ATTR_PORT, dest->port);
> +
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_FWD_METHOD,
> + atomic_read(&dest->conn_flags) & IP_VS_CONN_F_FWD_MASK);
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_WEIGHT, atomic_read(&dest->weight));
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_U_THRESH, dest->u_threshold);
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_L_THRESH, dest->l_threshold);
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_ACTIVE_CONNS,
> + atomic_read(&dest->activeconns));
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_INACT_CONNS,
> + atomic_read(&dest->inactconns));
> + NLA_PUT_U32(skb, IPVS_DEST_ATTR_PERSIST_CONNS,
> + atomic_read(&dest->persistconns));
> +
> + if (ip_vs_genl_fill_stats(skb, IPVS_DEST_ATTR_STATS, &dest->stats))
> + goto nla_put_failure;
> +
> + nla_nest_end(skb, nl_dest);
> +
> + return 0;
> +
> +nla_put_failure:
> + nla_nest_cancel(skb, nl_dest);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_dump_dest(struct sk_buff *skb, struct ip_vs_dest *dest,
> + struct netlink_callback *cb)
> +{
> + void *hdr;
> +
> + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
> + &ip_vs_genl_family, NLM_F_MULTI,
> + IPVS_CMD_NEW_DEST);
> + if (!hdr)
> + return -EMSGSIZE;
> +
> + if (ip_vs_genl_fill_dest(skb, dest) < 0)
> + goto nla_put_failure;
> +
> + return genlmsg_end(skb, hdr);
> +
> +nla_put_failure:
> + genlmsg_cancel(skb, hdr);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_dump_dests(struct sk_buff *skb,
> + struct netlink_callback *cb)
> +{
> + int idx = 0;
> + int start = cb->args[0];
> + struct ip_vs_service *svc;
> + struct ip_vs_dest *dest;
> + struct nlattr *attrs[IPVS_CMD_ATTR_MAX + 1];
> +
> + mutex_lock(&__ip_vs_mutex);
> +
> + /* Try to find the service for which to dump destinations */
> + if (nlmsg_parse(cb->nlh, GENL_HDRLEN, attrs,
> + IPVS_CMD_ATTR_MAX, ip_vs_cmd_policy))
> + goto out_err;
> +
> + svc = ip_vs_genl_find_service(attrs[IPVS_CMD_ATTR_SERVICE]);
> + if (IS_ERR(svc) || svc == NULL)
> + goto out_err;
> +
> + /* Dump the destinations */
> + list_for_each_entry(dest, &svc->destinations, n_list) {
> + if (++idx <= start)
> + continue;
> + if (ip_vs_genl_dump_dest(skb, dest, cb) < 0) {
> + idx--;
> + goto nla_put_failure;
> + }
> + }
> +
> +nla_put_failure:
> + cb->args[0] = idx;
> + ip_vs_service_put(svc);
> +
> +out_err:
> + mutex_unlock(&__ip_vs_mutex);
> +
> + return skb->len;
> +}
> +
> +static int ip_vs_genl_parse_dest(struct ip_vs_dest_user *udest,
> + struct nlattr *nla, int full_entry)
> +{
> + struct nlattr *attrs[IPVS_DEST_ATTR_MAX + 1];
> + struct nlattr *nla_addr, *nla_port;
> +
> + /* Parse mandatory identifying destination fields first */
> + if (nla == NULL ||
> + nla_parse_nested(attrs, IPVS_DEST_ATTR_MAX, nla, ip_vs_dest_policy))
> + return -EINVAL;
> +
> + nla_addr = attrs[IPVS_DEST_ATTR_ADDR];
> + nla_port = attrs[IPVS_DEST_ATTR_PORT];
> +
> + if (!(nla_addr && nla_port))
> + return -EINVAL;
> +
> + nla_memcpy(&udest->addr, nla_addr, sizeof(udest->addr));
> + udest->port = nla_get_u16(nla_port);
> +
> + /* If a full entry was requested, check for the additional fields */
> + if (full_entry) {
> + struct nlattr *nla_fwd, *nla_weight, *nla_u_thresh,
> + *nla_l_thresh;
> +
> + nla_fwd = attrs[IPVS_DEST_ATTR_FWD_METHOD];
> + nla_weight = attrs[IPVS_DEST_ATTR_WEIGHT];
> + nla_u_thresh = attrs[IPVS_DEST_ATTR_U_THRESH];
> + nla_l_thresh = attrs[IPVS_DEST_ATTR_L_THRESH];
> +
> + if (!(nla_fwd && nla_weight && nla_u_thresh && nla_l_thresh))
> + return -EINVAL;
> +
> + udest->conn_flags = nla_get_u32(nla_fwd)
> + & IP_VS_CONN_F_FWD_MASK;
> + udest->weight = nla_get_u32(nla_weight);
> + udest->u_threshold = nla_get_u32(nla_u_thresh);
> + udest->l_threshold = nla_get_u32(nla_l_thresh);
> + }
> +
> + return 0;
> +}
> +
> +static int ip_vs_genl_fill_daemon(struct sk_buff *skb, __be32 state,
> + const char *mcast_ifn, __be32 syncid)
> +{
> + struct nlattr *nl_daemon;
> +
> + nl_daemon = nla_nest_start(skb, IPVS_CMD_ATTR_DAEMON);
> + if (!nl_daemon)
> + return -EMSGSIZE;
> +
> + NLA_PUT_U32(skb, IPVS_DAEMON_ATTR_STATE, state);
> + NLA_PUT_STRING(skb, IPVS_DAEMON_ATTR_MCAST_IFN, mcast_ifn);
> + NLA_PUT_U32(skb, IPVS_DAEMON_ATTR_SYNC_ID, syncid);
> +
> + nla_nest_end(skb, nl_daemon);
> +
> + return 0;
> +
> +nla_put_failure:
> + nla_nest_cancel(skb, nl_daemon);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_dump_daemon(struct sk_buff *skb, __be32 state,
> + const char *mcast_ifn, __be32 syncid,
> + struct netlink_callback *cb)
> +{
> + void *hdr;
> + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
> + &ip_vs_genl_family, NLM_F_MULTI,
> + IPVS_CMD_NEW_DAEMON);
> + if (!hdr)
> + return -EMSGSIZE;
> +
> + if (ip_vs_genl_fill_daemon(skb, state, mcast_ifn, syncid))
> + goto nla_put_failure;
> +
> + return genlmsg_end(skb, hdr);
> +
> +nla_put_failure:
> + genlmsg_cancel(skb, hdr);
> + return -EMSGSIZE;
> +}
> +
> +static int ip_vs_genl_dump_daemons(struct sk_buff *skb,
> + struct netlink_callback *cb)
> +{
> + mutex_lock(&__ip_vs_mutex);
> + if ((ip_vs_sync_state & IP_VS_STATE_MASTER) && !cb->args[0]) {
> + if (ip_vs_genl_dump_daemon(skb, IP_VS_STATE_MASTER,
> + ip_vs_master_mcast_ifn,
> + ip_vs_master_syncid, cb) < 0)
> + goto nla_put_failure;
> +
> + cb->args[0] = 1;
> + }
> +
> + if ((ip_vs_sync_state & IP_VS_STATE_BACKUP) && !cb->args[1]) {
> + if (ip_vs_genl_dump_daemon(skb, IP_VS_STATE_BACKUP,
> + ip_vs_backup_mcast_ifn,
> + ip_vs_backup_syncid, cb) < 0)
> + goto nla_put_failure;
> +
> + cb->args[1] = 1;
> + }
> +
> +nla_put_failure:
> + mutex_unlock(&__ip_vs_mutex);
> +
> + return skb->len;
> +}
> +
> +static int ip_vs_genl_new_daemon(struct nlattr **attrs)
> +{
> + if (!(attrs[IPVS_DAEMON_ATTR_STATE] &&
> + attrs[IPVS_DAEMON_ATTR_MCAST_IFN] &&
> + attrs[IPVS_DAEMON_ATTR_SYNC_ID]))
> + return -EINVAL;
> +
> + return start_sync_thread(nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]),
> + nla_data(attrs[IPVS_DAEMON_ATTR_MCAST_IFN]),
> + nla_get_u32(attrs[IPVS_DAEMON_ATTR_SYNC_ID]));
> +}
> +
> +static int ip_vs_genl_del_daemon(struct nlattr **attrs)
> +{
> + if (!attrs[IPVS_DAEMON_ATTR_STATE])
> + return -EINVAL;
> +
> + return stop_sync_thread(nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]));
> +}
> +
> +static int ip_vs_genl_set_config(struct nlattr **attrs)
> +{
> + struct ip_vs_timeout_user t;
> +
> + __ip_vs_get_timeouts(&t);
> +
> + if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP])
> + t.tcp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP]);
> +
> + if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN])
> + t.tcp_fin_timeout =
> + nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN]);
> +
> + if (attrs[IPVS_CMD_ATTR_TIMEOUT_UDP])
> + t.udp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_UDP]);
> +
> + return ip_vs_set_timeout(&t);
> +}
> +
> +static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info)
> +{
> + struct ip_vs_service *svc = NULL;
> + struct ip_vs_service_user usvc;
> + struct ip_vs_dest_user udest;
> + int ret = 0, cmd;
> + int need_full_svc = 0, need_full_dest = 0;
> +
> + cmd = info->genlhdr->cmd;
> +
> + mutex_lock(&__ip_vs_mutex);
> +
> + if (cmd == IPVS_CMD_FLUSH) {
> + ret = ip_vs_flush();
> + goto out;
> + } else if (cmd == IPVS_CMD_SET_CONFIG) {
> + ret = ip_vs_genl_set_config(info->attrs);
> + goto out;
> + } else if (cmd == IPVS_CMD_NEW_DAEMON ||
> + cmd == IPVS_CMD_DEL_DAEMON) {
> +
> + struct nlattr *daemon_attrs[IPVS_DAEMON_ATTR_MAX + 1];
> +
> + if (!info->attrs[IPVS_CMD_ATTR_DAEMON] ||
> + nla_parse_nested(daemon_attrs, IPVS_DAEMON_ATTR_MAX,
> + info->attrs[IPVS_CMD_ATTR_DAEMON],
> + ip_vs_daemon_policy)) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + if (cmd == IPVS_CMD_NEW_DAEMON)
> + ret = ip_vs_genl_new_daemon(daemon_attrs);
> + else
> + ret = ip_vs_genl_del_daemon(daemon_attrs);
> + goto out;
> + } else if (cmd == IPVS_CMD_ZERO &&
> + !info->attrs[IPVS_CMD_ATTR_SERVICE]) {
> + ret = ip_vs_zero_all();
> + goto out;
> + }
> +
> + /* All following commands require a service argument, so check if we
> + * received a valid one. We need a full service specification when
> + * adding / editing a service. Only identifying members otherwise. */
> + if (cmd == IPVS_CMD_NEW_SERVICE || cmd == IPVS_CMD_SET_SERVICE)
> + need_full_svc = 1;
> +
> + ret = ip_vs_genl_parse_service(&usvc,
> + info->attrs[IPVS_CMD_ATTR_SERVICE],
> + need_full_svc);
> + if (ret)
> + goto out;
> +
> + /* Lookup the exact service by <protocol, addr, port> or fwmark */
> + if (usvc.fwmark == 0)
> + svc = __ip_vs_service_get(usvc.protocol, usvc.addr, usvc.port);
> + else
> + svc = __ip_vs_svc_fwm_get(usvc.fwmark);
> +
> + /* Unless we're adding a new service, the service must already exist */
> + if ((cmd != IPVS_CMD_NEW_SERVICE) && (svc == NULL)) {
> + ret = -ESRCH;
> + goto out;
> + }
> +
> + /* Destination commands require a valid destination argument. For
> + * adding / editing a destination, we need a full destination
> + * specification. */
> + if (cmd == IPVS_CMD_NEW_DEST || cmd == IPVS_CMD_SET_DEST ||
> + cmd == IPVS_CMD_DEL_DEST) {
> + if (cmd != IPVS_CMD_DEL_DEST)
> + need_full_dest = 1;
> +
> + ret = ip_vs_genl_parse_dest(&udest,
> + info->attrs[IPVS_CMD_ATTR_DEST],
> + need_full_dest);
> + if (ret)
> + goto out;
> + }
> +
> + switch (cmd) {
> + case IPVS_CMD_NEW_SERVICE:
> + if (svc == NULL)
> + ret = ip_vs_add_service(&usvc, &svc);
> + else
> + ret = -EEXIST;
> + break;
> + case IPVS_CMD_SET_SERVICE:
> + ret = ip_vs_edit_service(svc, &usvc);
> + break;
> + case IPVS_CMD_DEL_SERVICE:
> + ret = ip_vs_del_service(svc);
> + break;
> + case IPVS_CMD_NEW_DEST:
> + ret = ip_vs_add_dest(svc, &udest);
> + break;
> + case IPVS_CMD_SET_DEST:
> + ret = ip_vs_edit_dest(svc, &udest);
> + break;
> + case IPVS_CMD_DEL_DEST:
> + ret = ip_vs_del_dest(svc, &udest);
> + break;
> + case IPVS_CMD_ZERO:
> + ret = ip_vs_zero_service(svc);
> + break;
> + default:
> + ret = -EINVAL;
> + }
> +
> +out:
> + if (svc)
> + ip_vs_service_put(svc);
> + mutex_unlock(&__ip_vs_mutex);
> +
> + return ret;
> +}
> +
> +static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info)
> +{
> + struct sk_buff *msg;
> + void *reply;
> + int ret, cmd, reply_cmd;
> +
> + cmd = info->genlhdr->cmd;
> +
> + if (cmd == IPVS_CMD_GET_SERVICE)
> + reply_cmd = IPVS_CMD_NEW_SERVICE;
> + else if (cmd == IPVS_CMD_GET_INFO)
> + reply_cmd = IPVS_CMD_SET_INFO;
> + else if (cmd == IPVS_CMD_GET_CONFIG)
> + reply_cmd = IPVS_CMD_SET_CONFIG;
> + else {
> + IP_VS_ERR("unknown Generic Netlink command\n");
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
> + if (!msg) {
> + ret = -ENOMEM;
> + goto out;
> + }
> +
> + reply = genlmsg_put_reply(msg, info, &ip_vs_genl_family, 0, reply_cmd);
> + if (reply == NULL)
> + goto nla_put_failure;
These gotos now unlock a not locked mutex down in the error path.
> +
> + mutex_lock(&__ip_vs_mutex);
Is there a reason not using mutex_lock_interruptible() like the sockopt
interface does? I wondered in your earlier patch, but didn't really
bother.
> +
> + switch (cmd) {
> + case IPVS_CMD_GET_SERVICE:
> + {
> + struct ip_vs_service *svc;
> +
> + svc = ip_vs_genl_find_service(info->attrs[IPVS_CMD_ATTR_SERVICE]);
> + if (IS_ERR(svc)) {
> + ret = PTR_ERR(svc);
> + goto out_err;
> + } else if (svc) {
> + ret = ip_vs_genl_fill_service(msg, svc);
> + ip_vs_service_put(svc);
> + if (ret)
> + goto nla_put_failure;
> + } else {
> + ret = -ESRCH;
> + goto out_err;
> + }
> +
> + break;
> + }
> +
> + case IPVS_CMD_GET_CONFIG:
> + {
> + struct ip_vs_timeout_user t;
> +
> + __ip_vs_get_timeouts(&t);
> +#ifdef CONFIG_IP_VS_PROTO_TCP
> + NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP, t.tcp_timeout);
> + NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP_FIN,
> + t.tcp_fin_timeout);
> +#endif
> +#ifdef CONFIG_IP_VS_PROTO_UDP
> + NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_UDP, t.udp_timeout);
> +#endif
> +
> + break;
> + }
> +
> + case IPVS_CMD_GET_INFO:
> + NLA_PUT_U32(msg, IPVS_INFO_ATTR_VERSION, IP_VS_VERSION_CODE);
> + NLA_PUT_U32(msg, IPVS_INFO_ATTR_CONN_TAB_SIZE,
> + IP_VS_CONN_TAB_SIZE);
> + break;
> + }
> +
> + genlmsg_end(msg, reply);
> + ret = genlmsg_unicast(msg, info->snd_pid);
> + goto out;
> +
> +nla_put_failure:
> + IP_VS_ERR("not enough space in Netlink message\n");
> + ret = -EMSGSIZE;
> +
> +out_err:
> + nlmsg_free(msg);
> +out:
> + mutex_unlock(&__ip_vs_mutex);
> +
> + return ret;
> +}
> +
> +
> +static struct genl_ops ip_vs_genl_ops[] __read_mostly = {
> + {
> + .cmd = IPVS_CMD_NEW_SERVICE,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_SET_SERVICE,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_DEL_SERVICE,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_GET_SERVICE,
> + .flags = GENL_ADMIN_PERM,
> + .doit = ip_vs_genl_get_cmd,
> + .dumpit = ip_vs_genl_dump_services,
> + .policy = ip_vs_cmd_policy,
> + },
> + {
> + .cmd = IPVS_CMD_NEW_DEST,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_SET_DEST,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_DEL_DEST,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_GET_DEST,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .dumpit = ip_vs_genl_dump_dests,
> + },
> + {
> + .cmd = IPVS_CMD_NEW_DAEMON,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_DEL_DAEMON,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_GET_DAEMON,
> + .flags = GENL_ADMIN_PERM,
> + .dumpit = ip_vs_genl_dump_daemons,
> + },
> + {
> + .cmd = IPVS_CMD_SET_CONFIG,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_GET_CONFIG,
> + .flags = GENL_ADMIN_PERM,
> + .doit = ip_vs_genl_get_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_GET_INFO,
> + .flags = GENL_ADMIN_PERM,
> + .doit = ip_vs_genl_get_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_ZERO,
> + .flags = GENL_ADMIN_PERM,
> + .policy = ip_vs_cmd_policy,
> + .doit = ip_vs_genl_set_cmd,
> + },
> + {
> + .cmd = IPVS_CMD_FLUSH,
> + .flags = GENL_ADMIN_PERM,
> + .doit = ip_vs_genl_set_cmd,
> + },
> +};
> +
> +int ip_vs_genl_register(void)
static int __init
> +{
> + int ret, i;
> +
> + ret = genl_register_family(&ip_vs_genl_family);
> + if (ret)
> + return ret;
> +
> + for (i = 0; i < ARRAY_SIZE(ip_vs_genl_ops); i++) {
> + ret = genl_register_ops(&ip_vs_genl_family, &ip_vs_genl_ops[i]);
> + if (ret)
> + goto err_out;
> + }
> + return 0;
> +
> +err_out:
> + genl_unregister_family(&ip_vs_genl_family);
> + return ret;
> +}
> +
> +void ip_vs_genl_unregister(void)
static void
> +{
> + genl_unregister_family(&ip_vs_genl_family);
> +}
> +
> +/* End of Generic Netlink interface definitions */
> +
>
> int __init ip_vs_control_init(void)
> {
> @@ -2334,6 +3204,13 @@ int __init ip_vs_control_init(void)
> return ret;
> }
>
> + ret = ip_vs_genl_register();
> + if (ret) {
> + IP_VS_ERR("cannot register Generic Netlink interface.\n");
> + nf_unregister_sockopt(&ip_vs_sockopts);
> + return ret;
> + }
> +
> proc_net_fops_create(&init_net, "ip_vs", 0, &ip_vs_info_fops);
> proc_net_fops_create(&init_net, "ip_vs_stats",0, &ip_vs_stats_fops);
>
> @@ -2368,6 +3245,7 @@ void ip_vs_control_cleanup(void)
> unregister_sysctl_table(sysctl_header);
> proc_net_remove(&init_net, "ip_vs_stats");
> proc_net_remove(&init_net, "ip_vs");
> + ip_vs_genl_unregister();
> nf_unregister_sockopt(&ip_vs_sockopts);
> LeaveFunction(2);
> }
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCHv3 2/2] IPVS: Add genetlink interface implementation
2008-08-14 10:04 ` Sven Wegener
@ 2008-08-14 10:27 ` Julius Volz
2008-08-14 12:08 ` Julius Volz
0 siblings, 1 reply; 23+ messages in thread
From: Julius Volz @ 2008-08-14 10:27 UTC (permalink / raw)
To: Sven Wegener; +Cc: netdev, lvs-devel, horms, kaber, davem, tgraf, vbusam
On Thu, Aug 14, 2008 at 12:04:50PM +0200, Sven Wegener wrote:
> On Thu, 14 Aug 2008, Julius Volz wrote:
>
> > On Wed, Aug 13, 2008 at 11:51:06PM +0200, Sven Wegener wrote:
> > > On Fri, 8 Aug 2008, Julius Volz wrote:
> > > > +static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info)
> > > > +{
> > > > + struct sk_buff *msg;
> > > > + void *reply;
> > > > + int ret, cmd, reply_cmd;
> > > > +
> > > > + mutex_lock(&__ip_vs_mutex);
> > > > +
> > > > + cmd = info->genlhdr->cmd;
> > > > +
> > > > + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
> > > > + if (!msg) {
> > > > + ret = -ENOMEM;
> > > > + goto out_err;
> > >
> > > Here you want out...
> > >
> > > > + }
> > > > +
> > > > + if (cmd == IPVS_CMD_GET_SERVICE)
> > > > + reply_cmd = IPVS_CMD_NEW_SERVICE;
> > > > + else if (cmd == IPVS_CMD_GET_INFO)
> > > > + reply_cmd = IPVS_CMD_SET_INFO;
> > > > + else if (cmd == IPVS_CMD_GET_CONFIG)
> > > > + reply_cmd = IPVS_CMD_SET_CONFIG;
> > > > + else {
> > > > + IP_VS_ERR("unknown Generic Netlink command\n");
> > > > + ret = -EINVAL;
> > > > + goto out;
> > >
> > > ..and here you want out_error, to not leak msg.
> >
> > Ouch, thanks! Fixed this and locked the mutex later. I also removed the
> > "if (msg)" from out_err, as it becomes unneeded now. Here's the updated
> > patch:
>
> You missed the static on the register and unregister functions down at the
> bottom. :) Also see my comments to your change regarding the above issue
> down here.
Hrm, yes!
> > +static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info)
> > +{
> > + struct sk_buff *msg;
> > + void *reply;
> > + int ret, cmd, reply_cmd;
> > +
> > + cmd = info->genlhdr->cmd;
> > +
> > + if (cmd == IPVS_CMD_GET_SERVICE)
> > + reply_cmd = IPVS_CMD_NEW_SERVICE;
> > + else if (cmd == IPVS_CMD_GET_INFO)
> > + reply_cmd = IPVS_CMD_SET_INFO;
> > + else if (cmd == IPVS_CMD_GET_CONFIG)
> > + reply_cmd = IPVS_CMD_SET_CONFIG;
> > + else {
> > + IP_VS_ERR("unknown Generic Netlink command\n");
> > + ret = -EINVAL;
> > + goto out;
> > + }
> > +
> > + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
> > + if (!msg) {
> > + ret = -ENOMEM;
> > + goto out;
> > + }
> > +
> > + reply = genlmsg_put_reply(msg, info, &ip_vs_genl_family, 0, reply_cmd);
> > + if (reply == NULL)
> > + goto nla_put_failure;
>
> These gotos now unlock a not locked mutex down in the error path.
What did they put into my water supply :-/ Thanks!
>
> > +
> > + mutex_lock(&__ip_vs_mutex);
>
> Is there a reason not using mutex_lock_interruptible() like the sockopt
> interface does? I wondered in your earlier patch, but didn't really
> bother.
Not an expert here. I saw mutex_lock_interruptible() being used in the
old sockopt interface, but returning -ERESTARTSYS on interruption. Can I
do something similar to this from a genetlink function or what is the
right way? I was unsure, so I stuck to mutex_lock()...
Julius
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCHv3 2/2] IPVS: Add genetlink interface implementation
2008-08-14 10:27 ` Julius Volz
@ 2008-08-14 12:08 ` Julius Volz
2008-08-14 13:32 ` Sven Wegener
0 siblings, 1 reply; 23+ messages in thread
From: Julius Volz @ 2008-08-14 12:08 UTC (permalink / raw)
To: Sven Wegener; +Cc: netdev, lvs-devel, horms, kaber, davem, tgraf, vbusam
On Thu, Aug 14, 2008 at 12:27:19PM +0200, Julius Volz wrote:
> On Thu, Aug 14, 2008 at 12:04:50PM +0200, Sven Wegener wrote:
> > On Thu, 14 Aug 2008, Julius Volz wrote:
> >
> > > On Wed, Aug 13, 2008 at 11:51:06PM +0200, Sven Wegener wrote:
> > > > On Fri, 8 Aug 2008, Julius Volz wrote:
> > > > > +static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info)
> > > > > +{
> > > > > + struct sk_buff *msg;
> > > > > + void *reply;
> > > > > + int ret, cmd, reply_cmd;
> > > > > +
> > > > > + mutex_lock(&__ip_vs_mutex);
> > > > > +
> > > > > + cmd = info->genlhdr->cmd;
> > > > > +
> > > > > + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
> > > > > + if (!msg) {
> > > > > + ret = -ENOMEM;
> > > > > + goto out_err;
> > > >
> > > > Here you want out...
> > > >
> > > > > + }
> > > > > +
> > > > > + if (cmd == IPVS_CMD_GET_SERVICE)
> > > > > + reply_cmd = IPVS_CMD_NEW_SERVICE;
> > > > > + else if (cmd == IPVS_CMD_GET_INFO)
> > > > > + reply_cmd = IPVS_CMD_SET_INFO;
> > > > > + else if (cmd == IPVS_CMD_GET_CONFIG)
> > > > > + reply_cmd = IPVS_CMD_SET_CONFIG;
> > > > > + else {
> > > > > + IP_VS_ERR("unknown Generic Netlink command\n");
> > > > > + ret = -EINVAL;
> > > > > + goto out;
> > > >
> > > > ..and here you want out_error, to not leak msg.
> > >
> > > Ouch, thanks! Fixed this and locked the mutex later. I also removed the
> > > "if (msg)" from out_err, as it becomes unneeded now. Here's the updated
> > > patch:
> >
> > You missed the static on the register and unregister functions down at the
> > bottom. :) Also see my comments to your change regarding the above issue
> > down here.
>
> Hrm, yes!
>
> > > +static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info)
> > > +{
> > > + struct sk_buff *msg;
> > > + void *reply;
> > > + int ret, cmd, reply_cmd;
> > > +
> > > + cmd = info->genlhdr->cmd;
> > > +
> > > + if (cmd == IPVS_CMD_GET_SERVICE)
> > > + reply_cmd = IPVS_CMD_NEW_SERVICE;
> > > + else if (cmd == IPVS_CMD_GET_INFO)
> > > + reply_cmd = IPVS_CMD_SET_INFO;
> > > + else if (cmd == IPVS_CMD_GET_CONFIG)
> > > + reply_cmd = IPVS_CMD_SET_CONFIG;
> > > + else {
> > > + IP_VS_ERR("unknown Generic Netlink command\n");
> > > + ret = -EINVAL;
> > > + goto out;
> > > + }
> > > +
> > > + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
> > > + if (!msg) {
> > > + ret = -ENOMEM;
> > > + goto out;
> > > + }
> > > +
> > > + reply = genlmsg_put_reply(msg, info, &ip_vs_genl_family, 0, reply_cmd);
> > > + if (reply == NULL)
> > > + goto nla_put_failure;
> >
> > These gotos now unlock a not locked mutex down in the error path.
>
> What did they put into my water supply :-/ Thanks!
Ok, fixed this up. The mutex is not completely moved down to make the
code look a bit nicer (nla_put_failure assumes locked mutex). There
should not be much concurrency anyways since this mutex only locks the
userspace interface, which is mainly used by ipvsadm.
Julius
--------
Add the implementation of the new Generic Netlink interface to IPVS and
keep the old set/getsockopt interface for userspace backwards
compatibility.
Signed-off-by: Julius Volz <juliusv@google.com>
1 files changed, 875 insertions(+), 0 deletions(-)
diff --git a/net/ipv4/ipvs/ip_vs_ctl.c b/net/ipv4/ipvs/ip_vs_ctl.c
index 6379705..d1dbd8b 100644
--- a/net/ipv4/ipvs/ip_vs_ctl.c
+++ b/net/ipv4/ipvs/ip_vs_ctl.c
@@ -37,6 +37,7 @@
#include <net/ip.h>
#include <net/route.h>
#include <net/sock.h>
+#include <net/genetlink.h>
#include <asm/uaccess.h>
@@ -2320,6 +2321,872 @@ static struct nf_sockopt_ops ip_vs_sockopts = {
.owner = THIS_MODULE,
};
+/*
+ * Generic Netlink interface
+ */
+
+/* IPVS genetlink family */
+static struct genl_family ip_vs_genl_family = {
+ .id = GENL_ID_GENERATE,
+ .hdrsize = 0,
+ .name = IPVS_GENL_NAME,
+ .version = IPVS_GENL_VERSION,
+ .maxattr = IPVS_CMD_MAX,
+};
+
+/* Policy used for first-level command attributes */
+static const struct nla_policy ip_vs_cmd_policy[IPVS_CMD_ATTR_MAX + 1] = {
+ [IPVS_CMD_ATTR_SERVICE] = { .type = NLA_NESTED },
+ [IPVS_CMD_ATTR_DEST] = { .type = NLA_NESTED },
+ [IPVS_CMD_ATTR_DAEMON] = { .type = NLA_NESTED },
+ [IPVS_CMD_ATTR_TIMEOUT_TCP] = { .type = NLA_U32 },
+ [IPVS_CMD_ATTR_TIMEOUT_TCP_FIN] = { .type = NLA_U32 },
+ [IPVS_CMD_ATTR_TIMEOUT_UDP] = { .type = NLA_U32 },
+};
+
+/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_DAEMON */
+static const struct nla_policy ip_vs_daemon_policy[IPVS_DAEMON_ATTR_MAX + 1] = {
+ [IPVS_DAEMON_ATTR_STATE] = { .type = NLA_U32 },
+ [IPVS_DAEMON_ATTR_MCAST_IFN] = { .type = NLA_NUL_STRING,
+ .len = IP_VS_IFNAME_MAXLEN },
+ [IPVS_DAEMON_ATTR_SYNC_ID] = { .type = NLA_U32 },
+};
+
+/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_SERVICE */
+static const struct nla_policy ip_vs_svc_policy[IPVS_SVC_ATTR_MAX + 1] = {
+ [IPVS_SVC_ATTR_AF] = { .type = NLA_U16 },
+ [IPVS_SVC_ATTR_PROTOCOL] = { .type = NLA_U16 },
+ [IPVS_SVC_ATTR_ADDR] = { .type = NLA_BINARY,
+ .len = sizeof(union nf_inet_addr) },
+ [IPVS_SVC_ATTR_PORT] = { .type = NLA_U16 },
+ [IPVS_SVC_ATTR_FWMARK] = { .type = NLA_U32 },
+ [IPVS_SVC_ATTR_SCHED_NAME] = { .type = NLA_NUL_STRING,
+ .len = IP_VS_SCHEDNAME_MAXLEN },
+ [IPVS_SVC_ATTR_FLAGS] = { .type = NLA_BINARY,
+ .len = sizeof(struct ip_vs_flags) },
+ [IPVS_SVC_ATTR_TIMEOUT] = { .type = NLA_U32 },
+ [IPVS_SVC_ATTR_NETMASK] = { .type = NLA_U32 },
+ [IPVS_SVC_ATTR_STATS] = { .type = NLA_NESTED },
+};
+
+/* Policy used for attributes in nested attribute IPVS_CMD_ATTR_DEST */
+static const struct nla_policy ip_vs_dest_policy[IPVS_DEST_ATTR_MAX + 1] = {
+ [IPVS_DEST_ATTR_ADDR] = { .type = NLA_BINARY,
+ .len = sizeof(union nf_inet_addr) },
+ [IPVS_DEST_ATTR_PORT] = { .type = NLA_U16 },
+ [IPVS_DEST_ATTR_FWD_METHOD] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_WEIGHT] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_U_THRESH] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_L_THRESH] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_ACTIVE_CONNS] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_INACT_CONNS] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_PERSIST_CONNS] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_STATS] = { .type = NLA_NESTED },
+};
+
+static int ip_vs_genl_fill_stats(struct sk_buff *skb, int container_type,
+ struct ip_vs_stats *stats)
+{
+ struct nlattr *nl_stats = nla_nest_start(skb, container_type);
+ if (!nl_stats)
+ return -EMSGSIZE;
+
+ spin_lock_bh(&stats->lock);
+
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_CONNS, stats->conns);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_INPKTS, stats->inpkts);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTPKTS, stats->outpkts);
+ NLA_PUT_U64(skb, IPVS_STATS_ATTR_INBYTES, stats->inbytes);
+ NLA_PUT_U64(skb, IPVS_STATS_ATTR_OUTBYTES, stats->outbytes);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_CPS, stats->cps);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_INPPS, stats->inpps);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTPPS, stats->outpps);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_INBPS, stats->inbps);
+ NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTBPS, stats->outbps);
+
+ spin_unlock_bh(&stats->lock);
+
+ nla_nest_end(skb, nl_stats);
+
+ return 0;
+
+nla_put_failure:
+ spin_unlock_bh(&stats->lock);
+ nla_nest_cancel(skb, nl_stats);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_fill_service(struct sk_buff *skb,
+ struct ip_vs_service *svc)
+{
+ struct nlattr *nl_service;
+ struct ip_vs_flags flags = { .flags = svc->flags,
+ .mask = ~0 };
+
+ nl_service = nla_nest_start(skb, IPVS_CMD_ATTR_SERVICE);
+ if (!nl_service)
+ return -EMSGSIZE;
+
+ NLA_PUT_U16(skb, IPVS_SVC_ATTR_AF, AF_INET);
+
+ if (svc->fwmark) {
+ NLA_PUT_U32(skb, IPVS_SVC_ATTR_FWMARK, svc->fwmark);
+ } else {
+ NLA_PUT_U16(skb, IPVS_SVC_ATTR_PROTOCOL, svc->protocol);
+ NLA_PUT(skb, IPVS_SVC_ATTR_ADDR, sizeof(svc->addr), &svc->addr);
+ NLA_PUT_U16(skb, IPVS_SVC_ATTR_PORT, svc->port);
+ }
+
+ NLA_PUT_STRING(skb, IPVS_SVC_ATTR_SCHED_NAME, svc->scheduler->name);
+ NLA_PUT(skb, IPVS_SVC_ATTR_FLAGS, sizeof(flags), &flags);
+ NLA_PUT_U32(skb, IPVS_SVC_ATTR_TIMEOUT, svc->timeout / HZ);
+ NLA_PUT_U32(skb, IPVS_SVC_ATTR_NETMASK, svc->netmask);
+
+ if (ip_vs_genl_fill_stats(skb, IPVS_SVC_ATTR_STATS, &svc->stats))
+ goto nla_put_failure;
+
+ nla_nest_end(skb, nl_service);
+
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(skb, nl_service);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_dump_service(struct sk_buff *skb,
+ struct ip_vs_service *svc,
+ struct netlink_callback *cb)
+{
+ void *hdr;
+
+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
+ &ip_vs_genl_family, NLM_F_MULTI,
+ IPVS_CMD_NEW_SERVICE);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ if (ip_vs_genl_fill_service(skb, svc) < 0)
+ goto nla_put_failure;
+
+ return genlmsg_end(skb, hdr);
+
+nla_put_failure:
+ genlmsg_cancel(skb, hdr);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_dump_services(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ int idx = 0, i;
+ int start = cb->args[0];
+ struct ip_vs_service *svc;
+
+ mutex_lock(&__ip_vs_mutex);
+ for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) {
+ list_for_each_entry(svc, &ip_vs_svc_table[i], s_list) {
+ if (++idx <= start)
+ continue;
+ if (ip_vs_genl_dump_service(skb, svc, cb) < 0) {
+ idx--;
+ goto nla_put_failure;
+ }
+ }
+ }
+
+ for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) {
+ list_for_each_entry(svc, &ip_vs_svc_fwm_table[i], f_list) {
+ if (++idx <= start)
+ continue;
+ if (ip_vs_genl_dump_service(skb, svc, cb) < 0) {
+ idx--;
+ goto nla_put_failure;
+ }
+ }
+ }
+
+nla_put_failure:
+ mutex_unlock(&__ip_vs_mutex);
+ cb->args[0] = idx;
+
+ return skb->len;
+}
+
+static int ip_vs_genl_parse_service(struct ip_vs_service_user *usvc,
+ struct nlattr *nla, int full_entry)
+{
+ struct nlattr *attrs[IPVS_SVC_ATTR_MAX + 1];
+ struct nlattr *nla_af, *nla_port, *nla_fwmark, *nla_protocol, *nla_addr;
+
+ /* Parse mandatory identifying service fields first */
+ if (nla == NULL ||
+ nla_parse_nested(attrs, IPVS_SVC_ATTR_MAX, nla, ip_vs_svc_policy))
+ return -EINVAL;
+
+ nla_af = attrs[IPVS_SVC_ATTR_AF];
+ nla_protocol = attrs[IPVS_SVC_ATTR_PROTOCOL];
+ nla_addr = attrs[IPVS_SVC_ATTR_ADDR];
+ nla_port = attrs[IPVS_SVC_ATTR_PORT];
+ nla_fwmark = attrs[IPVS_SVC_ATTR_FWMARK];
+
+ if (!(nla_af && (nla_fwmark || (nla_port && nla_protocol && nla_addr))))
+ return -EINVAL;
+
+ /* For now, only support IPv4 */
+ if (nla_get_u16(nla_af) != AF_INET)
+ return -EAFNOSUPPORT;
+
+ if (nla_fwmark) {
+ usvc->protocol = IPPROTO_TCP;
+ usvc->fwmark = nla_get_u32(nla_fwmark);
+ } else {
+ usvc->protocol = nla_get_u16(nla_protocol);
+ nla_memcpy(&usvc->addr, nla_addr, sizeof(usvc->addr));
+ usvc->port = nla_get_u16(nla_port);
+ usvc->fwmark = 0;
+ }
+
+ /* If a full entry was requested, check for the additional fields */
+ if (full_entry) {
+ struct nlattr *nla_sched, *nla_flags, *nla_timeout,
+ *nla_netmask;
+ struct ip_vs_flags flags;
+ struct ip_vs_service *svc;
+
+ nla_sched = attrs[IPVS_SVC_ATTR_SCHED_NAME];
+ nla_flags = attrs[IPVS_SVC_ATTR_FLAGS];
+ nla_timeout = attrs[IPVS_SVC_ATTR_TIMEOUT];
+ nla_netmask = attrs[IPVS_SVC_ATTR_NETMASK];
+
+ if (!(nla_sched && nla_flags && nla_timeout && nla_netmask))
+ return -EINVAL;
+
+ nla_memcpy(&flags, nla_flags, sizeof(flags));
+
+ /* prefill flags from service if it already exists */
+ if (usvc->fwmark)
+ svc = __ip_vs_svc_fwm_get(usvc->fwmark);
+ else
+ svc = __ip_vs_service_get(usvc->protocol, usvc->addr,
+ usvc->port);
+ if (svc) {
+ usvc->flags = svc->flags;
+ ip_vs_service_put(svc);
+ } else
+ usvc->flags = 0;
+
+ /* set new flags from userland */
+ usvc->flags = (usvc->flags & ~flags.mask) |
+ (flags.flags & flags.mask);
+
+ strlcpy(usvc->sched_name, nla_data(nla_sched),
+ sizeof(usvc->sched_name));
+ usvc->timeout = nla_get_u32(nla_timeout);
+ usvc->netmask = nla_get_u32(nla_netmask);
+ }
+
+ return 0;
+}
+
+static struct ip_vs_service *ip_vs_genl_find_service(struct nlattr *nla)
+{
+ struct ip_vs_service_user usvc;
+ int ret;
+
+ ret = ip_vs_genl_parse_service(&usvc, nla, 0);
+ if (ret)
+ return ERR_PTR(ret);
+
+ if (usvc.fwmark)
+ return __ip_vs_svc_fwm_get(usvc.fwmark);
+ else
+ return __ip_vs_service_get(usvc.protocol, usvc.addr,
+ usvc.port);
+}
+
+static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest)
+{
+ struct nlattr *nl_dest;
+
+ nl_dest = nla_nest_start(skb, IPVS_CMD_ATTR_DEST);
+ if (!nl_dest)
+ return -EMSGSIZE;
+
+ NLA_PUT(skb, IPVS_DEST_ATTR_ADDR, sizeof(dest->addr), &dest->addr);
+ NLA_PUT_U16(skb, IPVS_DEST_ATTR_PORT, dest->port);
+
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_FWD_METHOD,
+ atomic_read(&dest->conn_flags) & IP_VS_CONN_F_FWD_MASK);
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_WEIGHT, atomic_read(&dest->weight));
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_U_THRESH, dest->u_threshold);
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_L_THRESH, dest->l_threshold);
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_ACTIVE_CONNS,
+ atomic_read(&dest->activeconns));
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_INACT_CONNS,
+ atomic_read(&dest->inactconns));
+ NLA_PUT_U32(skb, IPVS_DEST_ATTR_PERSIST_CONNS,
+ atomic_read(&dest->persistconns));
+
+ if (ip_vs_genl_fill_stats(skb, IPVS_DEST_ATTR_STATS, &dest->stats))
+ goto nla_put_failure;
+
+ nla_nest_end(skb, nl_dest);
+
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(skb, nl_dest);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_dump_dest(struct sk_buff *skb, struct ip_vs_dest *dest,
+ struct netlink_callback *cb)
+{
+ void *hdr;
+
+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
+ &ip_vs_genl_family, NLM_F_MULTI,
+ IPVS_CMD_NEW_DEST);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ if (ip_vs_genl_fill_dest(skb, dest) < 0)
+ goto nla_put_failure;
+
+ return genlmsg_end(skb, hdr);
+
+nla_put_failure:
+ genlmsg_cancel(skb, hdr);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_dump_dests(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ int idx = 0;
+ int start = cb->args[0];
+ struct ip_vs_service *svc;
+ struct ip_vs_dest *dest;
+ struct nlattr *attrs[IPVS_CMD_ATTR_MAX + 1];
+
+ mutex_lock(&__ip_vs_mutex);
+
+ /* Try to find the service for which to dump destinations */
+ if (nlmsg_parse(cb->nlh, GENL_HDRLEN, attrs,
+ IPVS_CMD_ATTR_MAX, ip_vs_cmd_policy))
+ goto out_err;
+
+ svc = ip_vs_genl_find_service(attrs[IPVS_CMD_ATTR_SERVICE]);
+ if (IS_ERR(svc) || svc == NULL)
+ goto out_err;
+
+ /* Dump the destinations */
+ list_for_each_entry(dest, &svc->destinations, n_list) {
+ if (++idx <= start)
+ continue;
+ if (ip_vs_genl_dump_dest(skb, dest, cb) < 0) {
+ idx--;
+ goto nla_put_failure;
+ }
+ }
+
+nla_put_failure:
+ cb->args[0] = idx;
+ ip_vs_service_put(svc);
+
+out_err:
+ mutex_unlock(&__ip_vs_mutex);
+
+ return skb->len;
+}
+
+static int ip_vs_genl_parse_dest(struct ip_vs_dest_user *udest,
+ struct nlattr *nla, int full_entry)
+{
+ struct nlattr *attrs[IPVS_DEST_ATTR_MAX + 1];
+ struct nlattr *nla_addr, *nla_port;
+
+ /* Parse mandatory identifying destination fields first */
+ if (nla == NULL ||
+ nla_parse_nested(attrs, IPVS_DEST_ATTR_MAX, nla, ip_vs_dest_policy))
+ return -EINVAL;
+
+ nla_addr = attrs[IPVS_DEST_ATTR_ADDR];
+ nla_port = attrs[IPVS_DEST_ATTR_PORT];
+
+ if (!(nla_addr && nla_port))
+ return -EINVAL;
+
+ nla_memcpy(&udest->addr, nla_addr, sizeof(udest->addr));
+ udest->port = nla_get_u16(nla_port);
+
+ /* If a full entry was requested, check for the additional fields */
+ if (full_entry) {
+ struct nlattr *nla_fwd, *nla_weight, *nla_u_thresh,
+ *nla_l_thresh;
+
+ nla_fwd = attrs[IPVS_DEST_ATTR_FWD_METHOD];
+ nla_weight = attrs[IPVS_DEST_ATTR_WEIGHT];
+ nla_u_thresh = attrs[IPVS_DEST_ATTR_U_THRESH];
+ nla_l_thresh = attrs[IPVS_DEST_ATTR_L_THRESH];
+
+ if (!(nla_fwd && nla_weight && nla_u_thresh && nla_l_thresh))
+ return -EINVAL;
+
+ udest->conn_flags = nla_get_u32(nla_fwd)
+ & IP_VS_CONN_F_FWD_MASK;
+ udest->weight = nla_get_u32(nla_weight);
+ udest->u_threshold = nla_get_u32(nla_u_thresh);
+ udest->l_threshold = nla_get_u32(nla_l_thresh);
+ }
+
+ return 0;
+}
+
+static int ip_vs_genl_fill_daemon(struct sk_buff *skb, __be32 state,
+ const char *mcast_ifn, __be32 syncid)
+{
+ struct nlattr *nl_daemon;
+
+ nl_daemon = nla_nest_start(skb, IPVS_CMD_ATTR_DAEMON);
+ if (!nl_daemon)
+ return -EMSGSIZE;
+
+ NLA_PUT_U32(skb, IPVS_DAEMON_ATTR_STATE, state);
+ NLA_PUT_STRING(skb, IPVS_DAEMON_ATTR_MCAST_IFN, mcast_ifn);
+ NLA_PUT_U32(skb, IPVS_DAEMON_ATTR_SYNC_ID, syncid);
+
+ nla_nest_end(skb, nl_daemon);
+
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(skb, nl_daemon);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_dump_daemon(struct sk_buff *skb, __be32 state,
+ const char *mcast_ifn, __be32 syncid,
+ struct netlink_callback *cb)
+{
+ void *hdr;
+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
+ &ip_vs_genl_family, NLM_F_MULTI,
+ IPVS_CMD_NEW_DAEMON);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ if (ip_vs_genl_fill_daemon(skb, state, mcast_ifn, syncid))
+ goto nla_put_failure;
+
+ return genlmsg_end(skb, hdr);
+
+nla_put_failure:
+ genlmsg_cancel(skb, hdr);
+ return -EMSGSIZE;
+}
+
+static int ip_vs_genl_dump_daemons(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ mutex_lock(&__ip_vs_mutex);
+ if ((ip_vs_sync_state & IP_VS_STATE_MASTER) && !cb->args[0]) {
+ if (ip_vs_genl_dump_daemon(skb, IP_VS_STATE_MASTER,
+ ip_vs_master_mcast_ifn,
+ ip_vs_master_syncid, cb) < 0)
+ goto nla_put_failure;
+
+ cb->args[0] = 1;
+ }
+
+ if ((ip_vs_sync_state & IP_VS_STATE_BACKUP) && !cb->args[1]) {
+ if (ip_vs_genl_dump_daemon(skb, IP_VS_STATE_BACKUP,
+ ip_vs_backup_mcast_ifn,
+ ip_vs_backup_syncid, cb) < 0)
+ goto nla_put_failure;
+
+ cb->args[1] = 1;
+ }
+
+nla_put_failure:
+ mutex_unlock(&__ip_vs_mutex);
+
+ return skb->len;
+}
+
+static int ip_vs_genl_new_daemon(struct nlattr **attrs)
+{
+ if (!(attrs[IPVS_DAEMON_ATTR_STATE] &&
+ attrs[IPVS_DAEMON_ATTR_MCAST_IFN] &&
+ attrs[IPVS_DAEMON_ATTR_SYNC_ID]))
+ return -EINVAL;
+
+ return start_sync_thread(nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]),
+ nla_data(attrs[IPVS_DAEMON_ATTR_MCAST_IFN]),
+ nla_get_u32(attrs[IPVS_DAEMON_ATTR_SYNC_ID]));
+}
+
+static int ip_vs_genl_del_daemon(struct nlattr **attrs)
+{
+ if (!attrs[IPVS_DAEMON_ATTR_STATE])
+ return -EINVAL;
+
+ return stop_sync_thread(nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]));
+}
+
+static int ip_vs_genl_set_config(struct nlattr **attrs)
+{
+ struct ip_vs_timeout_user t;
+
+ __ip_vs_get_timeouts(&t);
+
+ if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP])
+ t.tcp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP]);
+
+ if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN])
+ t.tcp_fin_timeout =
+ nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN]);
+
+ if (attrs[IPVS_CMD_ATTR_TIMEOUT_UDP])
+ t.udp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_UDP]);
+
+ return ip_vs_set_timeout(&t);
+}
+
+static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info)
+{
+ struct ip_vs_service *svc = NULL;
+ struct ip_vs_service_user usvc;
+ struct ip_vs_dest_user udest;
+ int ret = 0, cmd;
+ int need_full_svc = 0, need_full_dest = 0;
+
+ cmd = info->genlhdr->cmd;
+
+ mutex_lock(&__ip_vs_mutex);
+
+ if (cmd == IPVS_CMD_FLUSH) {
+ ret = ip_vs_flush();
+ goto out;
+ } else if (cmd == IPVS_CMD_SET_CONFIG) {
+ ret = ip_vs_genl_set_config(info->attrs);
+ goto out;
+ } else if (cmd == IPVS_CMD_NEW_DAEMON ||
+ cmd == IPVS_CMD_DEL_DAEMON) {
+
+ struct nlattr *daemon_attrs[IPVS_DAEMON_ATTR_MAX + 1];
+
+ if (!info->attrs[IPVS_CMD_ATTR_DAEMON] ||
+ nla_parse_nested(daemon_attrs, IPVS_DAEMON_ATTR_MAX,
+ info->attrs[IPVS_CMD_ATTR_DAEMON],
+ ip_vs_daemon_policy)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (cmd == IPVS_CMD_NEW_DAEMON)
+ ret = ip_vs_genl_new_daemon(daemon_attrs);
+ else
+ ret = ip_vs_genl_del_daemon(daemon_attrs);
+ goto out;
+ } else if (cmd == IPVS_CMD_ZERO &&
+ !info->attrs[IPVS_CMD_ATTR_SERVICE]) {
+ ret = ip_vs_zero_all();
+ goto out;
+ }
+
+ /* All following commands require a service argument, so check if we
+ * received a valid one. We need a full service specification when
+ * adding / editing a service. Only identifying members otherwise. */
+ if (cmd == IPVS_CMD_NEW_SERVICE || cmd == IPVS_CMD_SET_SERVICE)
+ need_full_svc = 1;
+
+ ret = ip_vs_genl_parse_service(&usvc,
+ info->attrs[IPVS_CMD_ATTR_SERVICE],
+ need_full_svc);
+ if (ret)
+ goto out;
+
+ /* Lookup the exact service by <protocol, addr, port> or fwmark */
+ if (usvc.fwmark == 0)
+ svc = __ip_vs_service_get(usvc.protocol, usvc.addr, usvc.port);
+ else
+ svc = __ip_vs_svc_fwm_get(usvc.fwmark);
+
+ /* Unless we're adding a new service, the service must already exist */
+ if ((cmd != IPVS_CMD_NEW_SERVICE) && (svc == NULL)) {
+ ret = -ESRCH;
+ goto out;
+ }
+
+ /* Destination commands require a valid destination argument. For
+ * adding / editing a destination, we need a full destination
+ * specification. */
+ if (cmd == IPVS_CMD_NEW_DEST || cmd == IPVS_CMD_SET_DEST ||
+ cmd == IPVS_CMD_DEL_DEST) {
+ if (cmd != IPVS_CMD_DEL_DEST)
+ need_full_dest = 1;
+
+ ret = ip_vs_genl_parse_dest(&udest,
+ info->attrs[IPVS_CMD_ATTR_DEST],
+ need_full_dest);
+ if (ret)
+ goto out;
+ }
+
+ switch (cmd) {
+ case IPVS_CMD_NEW_SERVICE:
+ if (svc == NULL)
+ ret = ip_vs_add_service(&usvc, &svc);
+ else
+ ret = -EEXIST;
+ break;
+ case IPVS_CMD_SET_SERVICE:
+ ret = ip_vs_edit_service(svc, &usvc);
+ break;
+ case IPVS_CMD_DEL_SERVICE:
+ ret = ip_vs_del_service(svc);
+ break;
+ case IPVS_CMD_NEW_DEST:
+ ret = ip_vs_add_dest(svc, &udest);
+ break;
+ case IPVS_CMD_SET_DEST:
+ ret = ip_vs_edit_dest(svc, &udest);
+ break;
+ case IPVS_CMD_DEL_DEST:
+ ret = ip_vs_del_dest(svc, &udest);
+ break;
+ case IPVS_CMD_ZERO:
+ ret = ip_vs_zero_service(svc);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+out:
+ if (svc)
+ ip_vs_service_put(svc);
+ mutex_unlock(&__ip_vs_mutex);
+
+ return ret;
+}
+
+static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info)
+{
+ struct sk_buff *msg;
+ void *reply;
+ int ret, cmd, reply_cmd;
+
+ cmd = info->genlhdr->cmd;
+
+ if (cmd == IPVS_CMD_GET_SERVICE)
+ reply_cmd = IPVS_CMD_NEW_SERVICE;
+ else if (cmd == IPVS_CMD_GET_INFO)
+ reply_cmd = IPVS_CMD_SET_INFO;
+ else if (cmd == IPVS_CMD_GET_CONFIG)
+ reply_cmd = IPVS_CMD_SET_CONFIG;
+ else {
+ IP_VS_ERR("unknown Generic Netlink command\n");
+ return -EINVAL;
+ }
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ mutex_lock(&__ip_vs_mutex);
+
+ reply = genlmsg_put_reply(msg, info, &ip_vs_genl_family, 0, reply_cmd);
+ if (reply == NULL)
+ goto nla_put_failure;
+
+ switch (cmd) {
+ case IPVS_CMD_GET_SERVICE:
+ {
+ struct ip_vs_service *svc;
+
+ svc = ip_vs_genl_find_service(info->attrs[IPVS_CMD_ATTR_SERVICE]);
+ if (IS_ERR(svc)) {
+ ret = PTR_ERR(svc);
+ goto out_err;
+ } else if (svc) {
+ ret = ip_vs_genl_fill_service(msg, svc);
+ ip_vs_service_put(svc);
+ if (ret)
+ goto nla_put_failure;
+ } else {
+ ret = -ESRCH;
+ goto out_err;
+ }
+
+ break;
+ }
+
+ case IPVS_CMD_GET_CONFIG:
+ {
+ struct ip_vs_timeout_user t;
+
+ __ip_vs_get_timeouts(&t);
+#ifdef CONFIG_IP_VS_PROTO_TCP
+ NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP, t.tcp_timeout);
+ NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP_FIN,
+ t.tcp_fin_timeout);
+#endif
+#ifdef CONFIG_IP_VS_PROTO_UDP
+ NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_UDP, t.udp_timeout);
+#endif
+
+ break;
+ }
+
+ case IPVS_CMD_GET_INFO:
+ NLA_PUT_U32(msg, IPVS_INFO_ATTR_VERSION, IP_VS_VERSION_CODE);
+ NLA_PUT_U32(msg, IPVS_INFO_ATTR_CONN_TAB_SIZE,
+ IP_VS_CONN_TAB_SIZE);
+ break;
+ }
+
+ genlmsg_end(msg, reply);
+ ret = genlmsg_unicast(msg, info->snd_pid);
+ goto out;
+
+nla_put_failure:
+ IP_VS_ERR("not enough space in Netlink message\n");
+ ret = -EMSGSIZE;
+
+out_err:
+ nlmsg_free(msg);
+out:
+ mutex_unlock(&__ip_vs_mutex);
+
+ return ret;
+}
+
+
+static struct genl_ops ip_vs_genl_ops[] __read_mostly = {
+ {
+ .cmd = IPVS_CMD_NEW_SERVICE,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_SET_SERVICE,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_DEL_SERVICE,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_GET_SERVICE,
+ .flags = GENL_ADMIN_PERM,
+ .doit = ip_vs_genl_get_cmd,
+ .dumpit = ip_vs_genl_dump_services,
+ .policy = ip_vs_cmd_policy,
+ },
+ {
+ .cmd = IPVS_CMD_NEW_DEST,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_SET_DEST,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_DEL_DEST,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_GET_DEST,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .dumpit = ip_vs_genl_dump_dests,
+ },
+ {
+ .cmd = IPVS_CMD_NEW_DAEMON,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_DEL_DAEMON,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_GET_DAEMON,
+ .flags = GENL_ADMIN_PERM,
+ .dumpit = ip_vs_genl_dump_daemons,
+ },
+ {
+ .cmd = IPVS_CMD_SET_CONFIG,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_GET_CONFIG,
+ .flags = GENL_ADMIN_PERM,
+ .doit = ip_vs_genl_get_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_GET_INFO,
+ .flags = GENL_ADMIN_PERM,
+ .doit = ip_vs_genl_get_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_ZERO,
+ .flags = GENL_ADMIN_PERM,
+ .policy = ip_vs_cmd_policy,
+ .doit = ip_vs_genl_set_cmd,
+ },
+ {
+ .cmd = IPVS_CMD_FLUSH,
+ .flags = GENL_ADMIN_PERM,
+ .doit = ip_vs_genl_set_cmd,
+ },
+};
+
+static int __init ip_vs_genl_register(void)
+{
+ int ret, i;
+
+ ret = genl_register_family(&ip_vs_genl_family);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(ip_vs_genl_ops); i++) {
+ ret = genl_register_ops(&ip_vs_genl_family, &ip_vs_genl_ops[i]);
+ if (ret)
+ goto err_out;
+ }
+ return 0;
+
+err_out:
+ genl_unregister_family(&ip_vs_genl_family);
+ return ret;
+}
+
+static void ip_vs_genl_unregister(void)
+{
+ genl_unregister_family(&ip_vs_genl_family);
+}
+
+/* End of Generic Netlink interface definitions */
+
int __init ip_vs_control_init(void)
{
@@ -2334,6 +3201,13 @@ int __init ip_vs_control_init(void)
return ret;
}
+ ret = ip_vs_genl_register();
+ if (ret) {
+ IP_VS_ERR("cannot register Generic Netlink interface.\n");
+ nf_unregister_sockopt(&ip_vs_sockopts);
+ return ret;
+ }
+
proc_net_fops_create(&init_net, "ip_vs", 0, &ip_vs_info_fops);
proc_net_fops_create(&init_net, "ip_vs_stats",0, &ip_vs_stats_fops);
@@ -2368,6 +3242,7 @@ void ip_vs_control_cleanup(void)
unregister_sysctl_table(sysctl_header);
proc_net_remove(&init_net, "ip_vs_stats");
proc_net_remove(&init_net, "ip_vs");
+ ip_vs_genl_unregister();
nf_unregister_sockopt(&ip_vs_sockopts);
LeaveFunction(2);
}
--
1.5.4.5
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCHv3 2/2] IPVS: Add genetlink interface implementation
2008-08-14 12:08 ` Julius Volz
@ 2008-08-14 13:32 ` Sven Wegener
2008-08-14 14:22 ` Julius Volz
0 siblings, 1 reply; 23+ messages in thread
From: Sven Wegener @ 2008-08-14 13:32 UTC (permalink / raw)
To: Julius Volz; +Cc: netdev, lvs-devel, horms, kaber, davem, tgraf, vbusam
On Thu, 14 Aug 2008, Julius Volz wrote:
> On Thu, Aug 14, 2008 at 12:27:19PM +0200, Julius Volz wrote:
> > On Thu, Aug 14, 2008 at 12:04:50PM +0200, Sven Wegener wrote:
> > > On Thu, 14 Aug 2008, Julius Volz wrote:
> > >
> > > > On Wed, Aug 13, 2008 at 11:51:06PM +0200, Sven Wegener wrote:
> > > > > On Fri, 8 Aug 2008, Julius Volz wrote:
> > > > > > +static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info)
> > > > > > +{
> > > > > > + struct sk_buff *msg;
> > > > > > + void *reply;
> > > > > > + int ret, cmd, reply_cmd;
> > > > > > +
> > > > > > + mutex_lock(&__ip_vs_mutex);
> > > > > > +
> > > > > > + cmd = info->genlhdr->cmd;
> > > > > > +
> > > > > > + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
> > > > > > + if (!msg) {
> > > > > > + ret = -ENOMEM;
> > > > > > + goto out_err;
> > > > >
> > > > > Here you want out...
> > > > >
> > > > > > + }
> > > > > > +
> > > > > > + if (cmd == IPVS_CMD_GET_SERVICE)
> > > > > > + reply_cmd = IPVS_CMD_NEW_SERVICE;
> > > > > > + else if (cmd == IPVS_CMD_GET_INFO)
> > > > > > + reply_cmd = IPVS_CMD_SET_INFO;
> > > > > > + else if (cmd == IPVS_CMD_GET_CONFIG)
> > > > > > + reply_cmd = IPVS_CMD_SET_CONFIG;
> > > > > > + else {
> > > > > > + IP_VS_ERR("unknown Generic Netlink command\n");
> > > > > > + ret = -EINVAL;
> > > > > > + goto out;
> > > > >
> > > > > ..and here you want out_error, to not leak msg.
> > > >
> > > > Ouch, thanks! Fixed this and locked the mutex later. I also removed the
> > > > "if (msg)" from out_err, as it becomes unneeded now. Here's the updated
> > > > patch:
> > >
> > > You missed the static on the register and unregister functions down at the
> > > bottom. :) Also see my comments to your change regarding the above issue
> > > down here.
> >
> > Hrm, yes!
> >
> > > > +static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info)
> > > > +{
> > > > + struct sk_buff *msg;
> > > > + void *reply;
> > > > + int ret, cmd, reply_cmd;
> > > > +
> > > > + cmd = info->genlhdr->cmd;
> > > > +
> > > > + if (cmd == IPVS_CMD_GET_SERVICE)
> > > > + reply_cmd = IPVS_CMD_NEW_SERVICE;
> > > > + else if (cmd == IPVS_CMD_GET_INFO)
> > > > + reply_cmd = IPVS_CMD_SET_INFO;
> > > > + else if (cmd == IPVS_CMD_GET_CONFIG)
> > > > + reply_cmd = IPVS_CMD_SET_CONFIG;
> > > > + else {
> > > > + IP_VS_ERR("unknown Generic Netlink command\n");
> > > > + ret = -EINVAL;
> > > > + goto out;
> > > > + }
> > > > +
> > > > + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
> > > > + if (!msg) {
> > > > + ret = -ENOMEM;
> > > > + goto out;
> > > > + }
> > > > +
> > > > + reply = genlmsg_put_reply(msg, info, &ip_vs_genl_family, 0, reply_cmd);
> > > > + if (reply == NULL)
> > > > + goto nla_put_failure;
> > >
> > > These gotos now unlock a not locked mutex down in the error path.
> >
> > What did they put into my water supply :-/ Thanks!
>
> Ok, fixed this up. The mutex is not completely moved down to make the
> code look a bit nicer (nla_put_failure assumes locked mutex). There
> should not be much concurrency anyways since this mutex only locks the
> userspace interface, which is mainly used by ipvsadm.
True, it was just a hint for an optimization. Looks good to me.
Acked-by: Sven Wegener <sven.wegener@stealer.net>
Should we get this into 2.6.27? It's a new interface, currently unused, so
the chance of breaking anything is marginal.
Sven
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCHv3 2/2] IPVS: Add genetlink interface implementation
2008-08-14 13:32 ` Sven Wegener
@ 2008-08-14 14:22 ` Julius Volz
2008-08-14 22:53 ` Simon Horman
0 siblings, 1 reply; 23+ messages in thread
From: Julius Volz @ 2008-08-14 14:22 UTC (permalink / raw)
To: Sven Wegener; +Cc: netdev, lvs-devel, horms, kaber, davem, tgraf, vbusam
On Thu, Aug 14, 2008 at 3:32 PM, Sven Wegener <sven.wegener@stealer.net> wrote:
> On Thu, 14 Aug 2008, Julius Volz wrote:
>> Ok, fixed this up. The mutex is not completely moved down to make the
>> code look a bit nicer (nla_put_failure assumes locked mutex). There
>> should not be much concurrency anyways since this mutex only locks the
>> userspace interface, which is mainly used by ipvsadm.
>
> True, it was just a hint for an optimization. Looks good to me.
>
> Acked-by: Sven Wegener <sven.wegener@stealer.net>
Thanks for the help with this!
> Should we get this into 2.6.27? It's a new interface, currently unused, so
> the chance of breaking anything is marginal.
Yeah, it shouldn't break anything existing and that would be great!
Julius
--
Google Switzerland GmbH
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCHv3 2/2] IPVS: Add genetlink interface implementation
2008-08-14 14:22 ` Julius Volz
@ 2008-08-14 22:53 ` Simon Horman
0 siblings, 0 replies; 23+ messages in thread
From: Simon Horman @ 2008-08-14 22:53 UTC (permalink / raw)
To: Julius Volz; +Cc: Sven Wegener, netdev, lvs-devel, kaber, davem, tgraf, vbusam
On Thu, Aug 14, 2008 at 04:22:33PM +0200, Julius Volz wrote:
> On Thu, Aug 14, 2008 at 3:32 PM, Sven Wegener <sven.wegener@stealer.net> wrote:
> > On Thu, 14 Aug 2008, Julius Volz wrote:
> >> Ok, fixed this up. The mutex is not completely moved down to make the
> >> code look a bit nicer (nla_put_failure assumes locked mutex). There
> >> should not be much concurrency anyways since this mutex only locks the
> >> userspace interface, which is mainly used by ipvsadm.
> >
> > True, it was just a hint for an optimization. Looks good to me.
> >
> > Acked-by: Sven Wegener <sven.wegener@stealer.net>
>
> Thanks for the help with this!
>
> > Should we get this into 2.6.27? It's a new interface, currently unused, so
> > the chance of breaking anything is marginal.
>
> Yeah, it shouldn't break anything existing and that would be great!
Fine by me.
^ permalink raw reply [flat|nested] 23+ messages in thread
end of thread, other threads:[~2008-08-14 22:53 UTC | newest]
Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-08-07 14:43 [PATCHv3 0/2] IPVS: Add Generic Netlink configuration interface Julius Volz
2008-08-07 14:43 ` [PATCHv3 1/2] IPVS: Add genetlink interface definitions to ip_vs.h Julius Volz
2008-08-07 14:43 ` [PATCHv3 2/2] IPVS: Add genetlink interface implementation Julius Volz
2008-08-08 11:29 ` Julius Volz
2008-08-13 21:51 ` Sven Wegener
2008-08-13 21:53 ` Sven Wegener
2008-08-14 9:32 ` Julius Volz
2008-08-14 9:52 ` Simon Horman
2008-08-14 10:04 ` Sven Wegener
2008-08-14 10:27 ` Julius Volz
2008-08-14 12:08 ` Julius Volz
2008-08-14 13:32 ` Sven Wegener
2008-08-14 14:22 ` Julius Volz
2008-08-14 22:53 ` Simon Horman
2008-08-14 5:39 ` Sven Wegener
2008-08-08 2:26 ` [PATCHv3 0/2] IPVS: Add Generic Netlink configuration interface Simon Horman
2008-08-08 12:06 ` Julius Volz
2008-08-09 14:23 ` Simon Horman
2008-08-09 19:57 ` Julius Volz
2008-08-13 16:00 ` Julius Volz
2008-08-13 23:09 ` Simon Horman
2008-08-14 4:12 ` Simon Horman
2008-08-14 9:34 ` Julius Volz
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).