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: Julian Anastasov <ja@ssi.bg> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
d69efb1689
commit
2ad17defd5
7 changed files with 66 additions and 23 deletions
|
@ -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 */
|
||||
|
|
|
@ -148,7 +148,7 @@ const char * ip_vs_state_name(__u16 proto, int state)
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -160,6 +160,7 @@ static void ah_exit(struct ip_vs_protocol *pp)
|
|||
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,
|
||||
|
|
|
@ -159,6 +159,7 @@ static void esp_exit(struct ip_vs_protocol *pp)
|
|||
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,
|
||||
|
|
|
@ -594,6 +594,7 @@ static void ip_vs_tcp_exit(struct ip_vs_protocol *pp)
|
|||
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,
|
||||
|
|
|
@ -409,6 +409,7 @@ static void udp_exit(struct ip_vs_protocol *pp)
|
|||
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,
|
||||
|
|
|
@ -288,11 +288,16 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen)
|
|||
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 char *buffer, const size_t buflen)
|
|||
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 char *buffer, const size_t buflen)
|
|||
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 char *buffer, const size_t buflen)
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue