diff options
author | Sven Wegener <sven.wegener@webde.de> | 2007-08-24 17:14:01 +0200 |
---|---|---|
committer | Sven Wegener <sven.wegener@webde.de> | 2009-01-16 12:11:37 +0100 |
commit | aa0c3972a4a35b8a92d30b63bbe2e9d3afc624a9 (patch) | |
tree | f1881b37d37711193551e5f630bfa4412e51546a | |
parent | 3fa8749e584b55f1180411ab1b51117190bac1e5 (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.h | 3 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_core.c | 51 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_ctl.c | 24 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_proto_tcp.c | 10 |
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; + } } } } |