summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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;
+ }
}
}
}