diff options
-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; + } } } } |