Commit 2ad17def authored by Julian Anastasov's avatar Julian Anastasov Committed by David S. Miller

ipvs: fix oops in backup for fwmark conn templates

	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.
Signed-off-by: default avatarJulian Anastasov <ja@ssi.bg>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d69efb16
...@@ -405,7 +405,8 @@ struct sk_buff; ...@@ -405,7 +405,8 @@ struct sk_buff;
struct ip_vs_protocol { struct ip_vs_protocol {
struct ip_vs_protocol *next; struct ip_vs_protocol *next;
char *name; char *name;
__u16 protocol; u16 protocol;
u16 num_states;
int dont_defrag; int dont_defrag;
atomic_t appcnt; /* counter of proto app incs */ atomic_t appcnt; /* counter of proto app incs */
int *timeout_table; /* protocol timeout table */ int *timeout_table; /* protocol timeout table */
......
...@@ -148,7 +148,7 @@ const char * ip_vs_state_name(__u16 proto, int state) ...@@ -148,7 +148,7 @@ const char * ip_vs_state_name(__u16 proto, int state)
struct ip_vs_protocol *pp = ip_vs_proto_get(proto); struct ip_vs_protocol *pp = ip_vs_proto_get(proto);
if (pp == NULL || pp->state_name == NULL) if (pp == NULL || pp->state_name == NULL)
return "ERR!"; return (IPPROTO_IP == proto) ? "NONE" : "ERR!";
return pp->state_name(state); return pp->state_name(state);
} }
......
...@@ -160,6 +160,7 @@ static void ah_exit(struct ip_vs_protocol *pp) ...@@ -160,6 +160,7 @@ static void ah_exit(struct ip_vs_protocol *pp)
struct ip_vs_protocol ip_vs_protocol_ah = { struct ip_vs_protocol ip_vs_protocol_ah = {
.name = "AH", .name = "AH",
.protocol = IPPROTO_AH, .protocol = IPPROTO_AH,
.num_states = 1,
.dont_defrag = 1, .dont_defrag = 1,
.init = ah_init, .init = ah_init,
.exit = ah_exit, .exit = ah_exit,
......
...@@ -159,6 +159,7 @@ static void esp_exit(struct ip_vs_protocol *pp) ...@@ -159,6 +159,7 @@ static void esp_exit(struct ip_vs_protocol *pp)
struct ip_vs_protocol ip_vs_protocol_esp = { struct ip_vs_protocol ip_vs_protocol_esp = {
.name = "ESP", .name = "ESP",
.protocol = IPPROTO_ESP, .protocol = IPPROTO_ESP,
.num_states = 1,
.dont_defrag = 1, .dont_defrag = 1,
.init = esp_init, .init = esp_init,
.exit = esp_exit, .exit = esp_exit,
......
...@@ -594,6 +594,7 @@ static void ip_vs_tcp_exit(struct ip_vs_protocol *pp) ...@@ -594,6 +594,7 @@ static void ip_vs_tcp_exit(struct ip_vs_protocol *pp)
struct ip_vs_protocol ip_vs_protocol_tcp = { struct ip_vs_protocol ip_vs_protocol_tcp = {
.name = "TCP", .name = "TCP",
.protocol = IPPROTO_TCP, .protocol = IPPROTO_TCP,
.num_states = IP_VS_TCP_S_LAST,
.dont_defrag = 0, .dont_defrag = 0,
.appcnt = ATOMIC_INIT(0), .appcnt = ATOMIC_INIT(0),
.init = ip_vs_tcp_init, .init = ip_vs_tcp_init,
......
...@@ -409,6 +409,7 @@ static void udp_exit(struct ip_vs_protocol *pp) ...@@ -409,6 +409,7 @@ static void udp_exit(struct ip_vs_protocol *pp)
struct ip_vs_protocol ip_vs_protocol_udp = { struct ip_vs_protocol ip_vs_protocol_udp = {
.name = "UDP", .name = "UDP",
.protocol = IPPROTO_UDP, .protocol = IPPROTO_UDP,
.num_states = IP_VS_UDP_S_LAST,
.dont_defrag = 0, .dont_defrag = 0,
.init = udp_init, .init = udp_init,
.exit = udp_exit, .exit = udp_exit,
......
...@@ -288,11 +288,16 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen) ...@@ -288,11 +288,16 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen)
char *p; char *p;
int i; 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 */ /* Convert size back to host byte order */
m->size = ntohs(m->size); m->size = ntohs(m->size);
if (buflen != m->size) { if (buflen != m->size) {
IP_VS_ERR("bogus message\n"); IP_VS_ERR_RL("bogus sync message size\n");
return; return;
} }
...@@ -307,9 +312,48 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen) ...@@ -307,9 +312,48 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen)
for (i=0; i<m->nr_conns; i++) { for (i=0; i<m->nr_conns; i++) {
unsigned flags, state; 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 = 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); 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)) if (!(flags & IP_VS_CONN_F_TEMPLATE))
cp = ip_vs_conn_in_get(s->protocol, cp = ip_vs_conn_in_get(s->protocol,
s->caddr, s->cport, s->caddr, s->cport,
...@@ -345,14 +389,9 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen) ...@@ -345,14 +389,9 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen)
IP_VS_ERR("ip_vs_conn_new failed\n"); IP_VS_ERR("ip_vs_conn_new failed\n");
return; return;
} }
cp->state = state;
} else if (!cp->dest) { } else if (!cp->dest) {
dest = ip_vs_try_bind_dest(cp); dest = ip_vs_try_bind_dest(cp);
if (!dest) { if (dest)
/* it is an unbound entry created by
* synchronization */
cp->flags = flags | IP_VS_CONN_F_HASHED;
} else
atomic_dec(&dest->refcnt); atomic_dec(&dest->refcnt);
} else if ((cp->dest) && (cp->protocol == IPPROTO_TCP) && } else if ((cp->dest) && (cp->protocol == IPPROTO_TCP) &&
(cp->state != state)) { (cp->state != state)) {
...@@ -371,23 +410,22 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen) ...@@ -371,23 +410,22 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen)
} }
} }
if (flags & IP_VS_CONN_F_SEQ_MASK) { if (opt)
opt = (struct ip_vs_sync_conn_options *)&s[1];
memcpy(&cp->in_seq, opt, sizeof(*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]); atomic_set(&cp->in_pkts, sysctl_ip_vs_sync_threshold[0]);
cp->state = state; cp->state = state;
pp = ip_vs_proto_get(s->protocol); cp->old_state = cp->state;
cp->timeout = pp->timeout_table[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); ip_vs_conn_put(cp);
if (p > buffer+buflen) {
IP_VS_ERR("bogus message\n");
return;
}
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment