| From 7d44cdf1c9307bd9649a4025d12896984c59199d Mon Sep 17 00:00:00 2001 |
| From: Julian Anastasov <ja@ssi.bg> |
| Date: Tue, 29 Apr 2008 03:21:23 -0700 |
| Subject: ipvs: fix oops in backup for fwmark conn templates |
| |
| From: Julian Anastasov <ja@ssi.bg> |
| |
| [ Upstream commit: 2ad17defd596ca7e8ba782d5fc6950ee0e99513c ] |
| |
| Fixes bug http://bugzilla.kernel.org/show_bug.cgi?id=10556 |
| where conn templates with protocol=IPPROTO_IP can oops backup box. |
| |
| Result from ip_vs_proto_get() should be checked because |
| protocol value can be invalid or unsupported in backup. But |
| for valid message we should not fail for templates which use |
| IPPROTO_IP. Also, add checks to validate message limits and |
| connection state. Show state NONE for templates using IPPROTO_IP. |
| |
| Fix tested and confirmed by L0op8ack <l0op8ack@hotmail.com> |
| |
| Signed-off-by: Julian Anastasov <ja@ssi.bg> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| include/net/ip_vs.h | 3 + |
| net/ipv4/ipvs/ip_vs_proto.c | 2 - |
| net/ipv4/ipvs/ip_vs_proto_ah.c | 1 |
| net/ipv4/ipvs/ip_vs_proto_esp.c | 1 |
| net/ipv4/ipvs/ip_vs_proto_tcp.c | 1 |
| net/ipv4/ipvs/ip_vs_proto_udp.c | 1 |
| net/ipv4/ipvs/ip_vs_sync.c | 80 +++++++++++++++++++++++++++++----------- |
| 7 files changed, 66 insertions(+), 23 deletions(-) |
| |
| --- a/include/net/ip_vs.h |
| +++ b/include/net/ip_vs.h |
| @@ -405,7 +405,8 @@ struct sk_buff; |
| struct ip_vs_protocol { |
| struct ip_vs_protocol *next; |
| char *name; |
| - __u16 protocol; |
| + u16 protocol; |
| + u16 num_states; |
| int dont_defrag; |
| atomic_t appcnt; /* counter of proto app incs */ |
| int *timeout_table; /* protocol timeout table */ |
| --- a/net/ipv4/ipvs/ip_vs_proto_ah.c |
| +++ b/net/ipv4/ipvs/ip_vs_proto_ah.c |
| @@ -160,6 +160,7 @@ static void ah_exit(struct ip_vs_protoco |
| struct ip_vs_protocol ip_vs_protocol_ah = { |
| .name = "AH", |
| .protocol = IPPROTO_AH, |
| + .num_states = 1, |
| .dont_defrag = 1, |
| .init = ah_init, |
| .exit = ah_exit, |
| --- a/net/ipv4/ipvs/ip_vs_proto.c |
| +++ b/net/ipv4/ipvs/ip_vs_proto.c |
| @@ -148,7 +148,7 @@ const char * ip_vs_state_name(__u16 prot |
| struct ip_vs_protocol *pp = ip_vs_proto_get(proto); |
| |
| if (pp == NULL || pp->state_name == NULL) |
| - return "ERR!"; |
| + return (IPPROTO_IP == proto) ? "NONE" : "ERR!"; |
| return pp->state_name(state); |
| } |
| |
| --- a/net/ipv4/ipvs/ip_vs_proto_esp.c |
| +++ b/net/ipv4/ipvs/ip_vs_proto_esp.c |
| @@ -159,6 +159,7 @@ static void esp_exit(struct ip_vs_protoc |
| struct ip_vs_protocol ip_vs_protocol_esp = { |
| .name = "ESP", |
| .protocol = IPPROTO_ESP, |
| + .num_states = 1, |
| .dont_defrag = 1, |
| .init = esp_init, |
| .exit = esp_exit, |
| --- a/net/ipv4/ipvs/ip_vs_proto_tcp.c |
| +++ b/net/ipv4/ipvs/ip_vs_proto_tcp.c |
| @@ -594,6 +594,7 @@ static void ip_vs_tcp_exit(struct ip_vs_ |
| struct ip_vs_protocol ip_vs_protocol_tcp = { |
| .name = "TCP", |
| .protocol = IPPROTO_TCP, |
| + .num_states = IP_VS_TCP_S_LAST, |
| .dont_defrag = 0, |
| .appcnt = ATOMIC_INIT(0), |
| .init = ip_vs_tcp_init, |
| --- a/net/ipv4/ipvs/ip_vs_proto_udp.c |
| +++ b/net/ipv4/ipvs/ip_vs_proto_udp.c |
| @@ -409,6 +409,7 @@ static void udp_exit(struct ip_vs_protoc |
| struct ip_vs_protocol ip_vs_protocol_udp = { |
| .name = "UDP", |
| .protocol = IPPROTO_UDP, |
| + .num_states = IP_VS_UDP_S_LAST, |
| .dont_defrag = 0, |
| .init = udp_init, |
| .exit = udp_exit, |
| --- a/net/ipv4/ipvs/ip_vs_sync.c |
| +++ b/net/ipv4/ipvs/ip_vs_sync.c |
| @@ -288,11 +288,16 @@ static void ip_vs_process_message(const |
| char *p; |
| int i; |
| |
| + if (buflen < sizeof(struct ip_vs_sync_mesg)) { |
| + IP_VS_ERR_RL("sync message header too short\n"); |
| + return; |
| + } |
| + |
| /* Convert size back to host byte order */ |
| m->size = ntohs(m->size); |
| |
| if (buflen != m->size) { |
| - IP_VS_ERR("bogus message\n"); |
| + IP_VS_ERR_RL("bogus sync message size\n"); |
| return; |
| } |
| |
| @@ -307,9 +312,48 @@ static void ip_vs_process_message(const |
| for (i=0; i<m->nr_conns; i++) { |
| unsigned flags, state; |
| |
| - s = (struct ip_vs_sync_conn *)p; |
| + if (p + SIMPLE_CONN_SIZE > buffer+buflen) { |
| + IP_VS_ERR_RL("bogus conn in sync message\n"); |
| + return; |
| + } |
| + s = (struct ip_vs_sync_conn *) p; |
| flags = ntohs(s->flags) | IP_VS_CONN_F_SYNC; |
| + flags &= ~IP_VS_CONN_F_HASHED; |
| + if (flags & IP_VS_CONN_F_SEQ_MASK) { |
| + opt = (struct ip_vs_sync_conn_options *)&s[1]; |
| + p += FULL_CONN_SIZE; |
| + if (p > buffer+buflen) { |
| + IP_VS_ERR_RL("bogus conn options in sync message\n"); |
| + return; |
| + } |
| + } else { |
| + opt = NULL; |
| + p += SIMPLE_CONN_SIZE; |
| + } |
| + |
| state = ntohs(s->state); |
| + if (!(flags & IP_VS_CONN_F_TEMPLATE)) { |
| + pp = ip_vs_proto_get(s->protocol); |
| + if (!pp) { |
| + IP_VS_ERR_RL("Unsupported protocol %u in sync msg\n", |
| + s->protocol); |
| + continue; |
| + } |
| + if (state >= pp->num_states) { |
| + IP_VS_DBG(2, "Invalid %s state %u in sync msg\n", |
| + pp->name, state); |
| + continue; |
| + } |
| + } else { |
| + /* protocol in templates is not used for state/timeout */ |
| + pp = NULL; |
| + if (state > 0) { |
| + IP_VS_DBG(2, "Invalid template state %u in sync msg\n", |
| + state); |
| + state = 0; |
| + } |
| + } |
| + |
| if (!(flags & IP_VS_CONN_F_TEMPLATE)) |
| cp = ip_vs_conn_in_get(s->protocol, |
| s->caddr, s->cport, |
| @@ -345,14 +389,9 @@ static void ip_vs_process_message(const |
| IP_VS_ERR("ip_vs_conn_new failed\n"); |
| return; |
| } |
| - cp->state = state; |
| } else if (!cp->dest) { |
| dest = ip_vs_try_bind_dest(cp); |
| - if (!dest) { |
| - /* it is an unbound entry created by |
| - * synchronization */ |
| - cp->flags = flags | IP_VS_CONN_F_HASHED; |
| - } else |
| + if (dest) |
| atomic_dec(&dest->refcnt); |
| } else if ((cp->dest) && (cp->protocol == IPPROTO_TCP) && |
| (cp->state != state)) { |
| @@ -371,23 +410,22 @@ static void ip_vs_process_message(const |
| } |
| } |
| |
| - if (flags & IP_VS_CONN_F_SEQ_MASK) { |
| - opt = (struct ip_vs_sync_conn_options *)&s[1]; |
| + if (opt) |
| memcpy(&cp->in_seq, opt, sizeof(*opt)); |
| - p += FULL_CONN_SIZE; |
| - } else |
| - p += SIMPLE_CONN_SIZE; |
| - |
| atomic_set(&cp->in_pkts, sysctl_ip_vs_sync_threshold[0]); |
| cp->state = state; |
| - pp = ip_vs_proto_get(s->protocol); |
| - cp->timeout = pp->timeout_table[cp->state]; |
| + cp->old_state = cp->state; |
| + /* |
| + * We can not recover the right timeout for templates |
| + * in all cases, we can not find the right fwmark |
| + * virtual service. If needed, we can do it for |
| + * non-fwmark persistent services. |
| + */ |
| + if (!(flags & IP_VS_CONN_F_TEMPLATE) && pp->timeout_table) |
| + cp->timeout = pp->timeout_table[state]; |
| + else |
| + cp->timeout = (3*60*HZ); |
| ip_vs_conn_put(cp); |
| - |
| - if (p > buffer+buflen) { |
| - IP_VS_ERR("bogus message\n"); |
| - return; |
| - } |
| } |
| } |
| |