summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSven Wegener <sven.wegener@webde.de>2007-08-24 17:14:01 +0200
committerSven Wegener <sven.wegener@webde.de>2009-01-16 12:11:37 +0100
commitaa0c3972a4a35b8a92d30b63bbe2e9d3afc624a9 (patch)
treef1881b37d37711193551e5f630bfa4412e51546a
parent3fa8749e584b55f1180411ab1b51117190bac1e5 (diff)
ipvs: Account outgoing bytes for DR and TUN servicesipvs/droutbytes
We don't see outgoing packets for DR and TUN services, but for TCP we can account the outgoing bytes based on the ACK packets coming from the client. We hijack the out_seq struct to track the sequence number. The structure is only used for NAT and we can safe a couple of bytes in the ip_vs_conn structure to prevent it from growing from 128 bytes to 256 bytes with SLAB_HWCACHE_ALIGN and certain configuration options. Signed-off-by: Sven Wegener <sven.wegener@webde.de>
-rw-r--r--include/net/ip_vs.h3
-rw-r--r--net/ipv4/ipvs/ip_vs_core.c51
-rw-r--r--net/ipv4/ipvs/ip_vs_ctl.c24
-rw-r--r--net/ipv4/ipvs/ip_vs_proto_tcp.c10
4 files changed, 84 insertions, 4 deletions
diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
index 7312c3dd309f..d63f22a683ed 100644
--- a/include/net/ip_vs.h
+++ b/include/net/ip_vs.h
@@ -643,6 +643,9 @@ extern int sysctl_ip_vs_expire_nodest_conn;
extern int sysctl_ip_vs_expire_quiescent_template;
extern int sysctl_ip_vs_sync_threshold[2];
extern int sysctl_ip_vs_nat_icmp_send;
+extern int sysctl_ip_vs_account_dr_outbytes_destination;
+extern int sysctl_ip_vs_account_dr_outbytes_service;
+extern int sysctl_ip_vs_account_dr_outbytes_global;
extern struct ip_vs_stats ip_vs_stats;
extern const struct ctl_path net_vs_ctl_path[];
diff --git a/net/ipv4/ipvs/ip_vs_core.c b/net/ipv4/ipvs/ip_vs_core.c
index a7879eafc3b5..f59f1c493061 100644
--- a/net/ipv4/ipvs/ip_vs_core.c
+++ b/net/ipv4/ipvs/ip_vs_core.c
@@ -86,24 +86,67 @@ void ip_vs_init_hash_table(struct list_head *table, int rows)
INIT_LIST_HEAD(&table[rows]);
}
+static inline u32
+ip_vs_in_stats_dr(struct ip_vs_conn *cp, struct sk_buff *skb)
+{
+ struct ip_vs_dest *dest = cp->dest;
+ struct tcphdr _tcph, *th;
+ u32 ack, bytes = 0;
+
+ if (dest->protocol != IPPROTO_TCP)
+ return 0;
+
+ if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_DROUTE &&
+ IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_TUNNEL)
+ return 0;
+
+ th = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph);
+ if (!th || !th->ack)
+ return 0;
+
+ spin_lock(&cp->lock);
+
+ if (!(cp->flags & IP_VS_CONN_F_OUT_SEQ))
+ goto out;
+
+ ack = ntohl(th->ack_seq);
+ if (after(ack, cp->out_seq.init_seq)) {
+ bytes = ack - cp->out_seq.init_seq;
+ cp->out_seq.init_seq = ack;
+ }
+
+out:
+ spin_unlock(&cp->lock);
+
+ return bytes;
+}
+
static inline void
ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
{
struct ip_vs_dest *dest = cp->dest;
if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) {
+ u32 drbytes = ip_vs_in_stats_dr(cp, skb);
+
spin_lock(&dest->stats.lock);
dest->stats.inpkts++;
dest->stats.inbytes += skb->len;
+ if (sysctl_ip_vs_account_dr_outbytes_destination)
+ dest->stats.outbytes += drbytes;
spin_unlock(&dest->stats.lock);
spin_lock(&dest->svc->stats.lock);
dest->svc->stats.inpkts++;
dest->svc->stats.inbytes += skb->len;
+ if (sysctl_ip_vs_account_dr_outbytes_service)
+ dest->svc->stats.outbytes += drbytes;
spin_unlock(&dest->svc->stats.lock);
spin_lock(&ip_vs_stats.lock);
ip_vs_stats.inpkts++;
ip_vs_stats.inbytes += skb->len;
+ if (sysctl_ip_vs_account_dr_outbytes_global)
+ ip_vs_stats.outbytes += drbytes;
spin_unlock(&ip_vs_stats.lock);
}
}
@@ -438,12 +481,12 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,
if (cp == NULL)
return NF_DROP;
- /* statistics */
- ip_vs_in_stats(cp, skb);
-
/* set state */
cs = ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pp);
+ /* statistics */
+ ip_vs_in_stats(cp, skb);
+
/* transmit the first SYN packet */
ret = cp->packet_xmit(skb, cp, pp);
/* do not touch skb anymore */
@@ -967,8 +1010,8 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb,
return NF_DROP;
}
- ip_vs_in_stats(cp, skb);
restart = ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pp);
+ ip_vs_in_stats(cp, skb);
if (cp->packet_xmit)
ret = cp->packet_xmit(skb, cp, pp);
/* do not touch skb anymore */
diff --git a/net/ipv4/ipvs/ip_vs_ctl.c b/net/ipv4/ipvs/ip_vs_ctl.c
index 6379705a8dcb..d8b6cb52136a 100644
--- a/net/ipv4/ipvs/ip_vs_ctl.c
+++ b/net/ipv4/ipvs/ip_vs_ctl.c
@@ -79,6 +79,9 @@ int sysctl_ip_vs_expire_nodest_conn = 0;
int sysctl_ip_vs_expire_quiescent_template = 0;
int sysctl_ip_vs_sync_threshold[2] = { 3, 50 };
int sysctl_ip_vs_nat_icmp_send = 0;
+int sysctl_ip_vs_account_dr_outbytes_destination = 0;
+int sysctl_ip_vs_account_dr_outbytes_service = 0;
+int sysctl_ip_vs_account_dr_outbytes_global = 0;
#ifdef CONFIG_IP_VS_DEBUG
@@ -1599,6 +1602,27 @@ static struct ctl_table vs_vars[] = {
.mode = 0644,
.proc_handler = &proc_dointvec,
},
+ {
+ .procname = "account_dr_outbytes_destination",
+ .data = &sysctl_ip_vs_account_dr_outbytes_destination,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
+ {
+ .procname = "account_dr_outbytes_service",
+ .data = &sysctl_ip_vs_account_dr_outbytes_service,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
+ {
+ .procname = "account_dr_outbytes_global",
+ .data = &sysctl_ip_vs_account_dr_outbytes_global,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
{ .ctl_name = 0 }
};
diff --git a/net/ipv4/ipvs/ip_vs_proto_tcp.c b/net/ipv4/ipvs/ip_vs_proto_tcp.c
index d0ea467986a0..922520f8b4f5 100644
--- a/net/ipv4/ipvs/ip_vs_proto_tcp.c
+++ b/net/ipv4/ipvs/ip_vs_proto_tcp.c
@@ -438,11 +438,21 @@ set_tcp_state(struct ip_vs_protocol *pp, struct ip_vs_conn *cp,
atomic_dec(&dest->activeconns);
atomic_inc(&dest->inactconns);
cp->flags |= IP_VS_CONN_F_INACTIVE;
+ if (IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_DROUTE ||
+ IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_TUNNEL)
+ cp->flags &= ~IP_VS_CONN_F_OUT_SEQ;
} else if ((cp->flags & IP_VS_CONN_F_INACTIVE) &&
(new_state == IP_VS_TCP_S_ESTABLISHED)) {
atomic_inc(&dest->activeconns);
atomic_dec(&dest->inactconns);
cp->flags &= ~IP_VS_CONN_F_INACTIVE;
+ if ((IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_DROUTE ||
+ IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_TUNNEL) &&
+ th->ack) {
+ /* we hijack the seq struct here, it's only used for NAT */
+ cp->out_seq.init_seq = ntohl(th->ack_seq);
+ cp->flags |= IP_VS_CONN_F_OUT_SEQ;
+ }
}
}
}