diff options
author | Julian Anastasov <ja@ssi.bg> | 2008-04-29 06:21:23 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-04-29 06:21:23 -0400 |
commit | 2ad17defd596ca7e8ba782d5fc6950ee0e99513c (patch) | |
tree | fa971402d7e832c3dcfa4bb2dd401b76f5249a58 | |
parent | d69efb16891ddfa6c0b527f912a7193054d50281 (diff) |
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>
-rw-r--r-- | include/net/ip_vs.h | 3 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_proto.c | 2 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_proto_ah.c | 1 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_proto_esp.c | 1 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_proto_tcp.c | 1 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_proto_udp.c | 1 | ||||
-rw-r--r-- | net/ipv4/ipvs/ip_vs_sync.c | 80 |
7 files changed, 66 insertions, 23 deletions
diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 56f3c94ae620..9a51ebad3f1f 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h | |||
@@ -405,7 +405,8 @@ struct sk_buff; | |||
405 | struct ip_vs_protocol { | 405 | struct ip_vs_protocol { |
406 | struct ip_vs_protocol *next; | 406 | struct ip_vs_protocol *next; |
407 | char *name; | 407 | char *name; |
408 | __u16 protocol; | 408 | u16 protocol; |
409 | u16 num_states; | ||
409 | int dont_defrag; | 410 | int dont_defrag; |
410 | atomic_t appcnt; /* counter of proto app incs */ | 411 | atomic_t appcnt; /* counter of proto app incs */ |
411 | int *timeout_table; /* protocol timeout table */ | 412 | int *timeout_table; /* protocol timeout table */ |
diff --git a/net/ipv4/ipvs/ip_vs_proto.c b/net/ipv4/ipvs/ip_vs_proto.c index dde28a250d92..4b1c16cbb16b 100644 --- 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 proto, int state) | |||
148 | struct ip_vs_protocol *pp = ip_vs_proto_get(proto); | 148 | struct ip_vs_protocol *pp = ip_vs_proto_get(proto); |
149 | 149 | ||
150 | if (pp == NULL || pp->state_name == NULL) | 150 | if (pp == NULL || pp->state_name == NULL) |
151 | return "ERR!"; | 151 | return (IPPROTO_IP == proto) ? "NONE" : "ERR!"; |
152 | return pp->state_name(state); | 152 | return pp->state_name(state); |
153 | } | 153 | } |
154 | 154 | ||
diff --git a/net/ipv4/ipvs/ip_vs_proto_ah.c b/net/ipv4/ipvs/ip_vs_proto_ah.c index a842676e1c69..4bf835e1d86d 100644 --- 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_protocol *pp) | |||
160 | struct ip_vs_protocol ip_vs_protocol_ah = { | 160 | struct ip_vs_protocol ip_vs_protocol_ah = { |
161 | .name = "AH", | 161 | .name = "AH", |
162 | .protocol = IPPROTO_AH, | 162 | .protocol = IPPROTO_AH, |
163 | .num_states = 1, | ||
163 | .dont_defrag = 1, | 164 | .dont_defrag = 1, |
164 | .init = ah_init, | 165 | .init = ah_init, |
165 | .exit = ah_exit, | 166 | .exit = ah_exit, |
diff --git a/net/ipv4/ipvs/ip_vs_proto_esp.c b/net/ipv4/ipvs/ip_vs_proto_esp.c index aef0d3ee8e44..db6a6b7b1a0b 100644 --- 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_protocol *pp) | |||
159 | struct ip_vs_protocol ip_vs_protocol_esp = { | 159 | struct ip_vs_protocol ip_vs_protocol_esp = { |
160 | .name = "ESP", | 160 | .name = "ESP", |
161 | .protocol = IPPROTO_ESP, | 161 | .protocol = IPPROTO_ESP, |
162 | .num_states = 1, | ||
162 | .dont_defrag = 1, | 163 | .dont_defrag = 1, |
163 | .init = esp_init, | 164 | .init = esp_init, |
164 | .exit = esp_exit, | 165 | .exit = esp_exit, |
diff --git a/net/ipv4/ipvs/ip_vs_proto_tcp.c b/net/ipv4/ipvs/ip_vs_proto_tcp.c index 620e40ff79a9..b83dc14b0a4d 100644 --- 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_protocol *pp) | |||
594 | struct ip_vs_protocol ip_vs_protocol_tcp = { | 594 | struct ip_vs_protocol ip_vs_protocol_tcp = { |
595 | .name = "TCP", | 595 | .name = "TCP", |
596 | .protocol = IPPROTO_TCP, | 596 | .protocol = IPPROTO_TCP, |
597 | .num_states = IP_VS_TCP_S_LAST, | ||
597 | .dont_defrag = 0, | 598 | .dont_defrag = 0, |
598 | .appcnt = ATOMIC_INIT(0), | 599 | .appcnt = ATOMIC_INIT(0), |
599 | .init = ip_vs_tcp_init, | 600 | .init = ip_vs_tcp_init, |
diff --git a/net/ipv4/ipvs/ip_vs_proto_udp.c b/net/ipv4/ipvs/ip_vs_proto_udp.c index 1caa2908373f..75771cb3cd6f 100644 --- 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_protocol *pp) | |||
409 | struct ip_vs_protocol ip_vs_protocol_udp = { | 409 | struct ip_vs_protocol ip_vs_protocol_udp = { |
410 | .name = "UDP", | 410 | .name = "UDP", |
411 | .protocol = IPPROTO_UDP, | 411 | .protocol = IPPROTO_UDP, |
412 | .num_states = IP_VS_UDP_S_LAST, | ||
412 | .dont_defrag = 0, | 413 | .dont_defrag = 0, |
413 | .init = udp_init, | 414 | .init = udp_init, |
414 | .exit = udp_exit, | 415 | .exit = udp_exit, |
diff --git a/net/ipv4/ipvs/ip_vs_sync.c b/net/ipv4/ipvs/ip_vs_sync.c index 69c56663cc9a..eff54efe0351 100644 --- 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 *buffer, const size_t buflen) | |||
288 | char *p; | 288 | char *p; |
289 | int i; | 289 | int i; |
290 | 290 | ||
291 | if (buflen < sizeof(struct ip_vs_sync_mesg)) { | ||
292 | IP_VS_ERR_RL("sync message header too short\n"); | ||
293 | return; | ||
294 | } | ||
295 | |||
291 | /* Convert size back to host byte order */ | 296 | /* Convert size back to host byte order */ |
292 | m->size = ntohs(m->size); | 297 | m->size = ntohs(m->size); |
293 | 298 | ||
294 | if (buflen != m->size) { | 299 | if (buflen != m->size) { |
295 | IP_VS_ERR("bogus message\n"); | 300 | IP_VS_ERR_RL("bogus sync message size\n"); |
296 | return; | 301 | return; |
297 | } | 302 | } |
298 | 303 | ||
@@ -307,9 +312,48 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen) | |||
307 | for (i=0; i<m->nr_conns; i++) { | 312 | for (i=0; i<m->nr_conns; i++) { |
308 | unsigned flags, state; | 313 | unsigned flags, state; |
309 | 314 | ||
310 | s = (struct ip_vs_sync_conn *)p; | 315 | if (p + SIMPLE_CONN_SIZE > buffer+buflen) { |
316 | IP_VS_ERR_RL("bogus conn in sync message\n"); | ||
317 | return; | ||
318 | } | ||
319 | s = (struct ip_vs_sync_conn *) p; | ||
311 | flags = ntohs(s->flags) | IP_VS_CONN_F_SYNC; | 320 | flags = ntohs(s->flags) | IP_VS_CONN_F_SYNC; |
321 | flags &= ~IP_VS_CONN_F_HASHED; | ||
322 | if (flags & IP_VS_CONN_F_SEQ_MASK) { | ||
323 | opt = (struct ip_vs_sync_conn_options *)&s[1]; | ||
324 | p += FULL_CONN_SIZE; | ||
325 | if (p > buffer+buflen) { | ||
326 | IP_VS_ERR_RL("bogus conn options in sync message\n"); | ||
327 | return; | ||
328 | } | ||
329 | } else { | ||
330 | opt = NULL; | ||
331 | p += SIMPLE_CONN_SIZE; | ||
332 | } | ||
333 | |||
312 | state = ntohs(s->state); | 334 | state = ntohs(s->state); |
335 | if (!(flags & IP_VS_CONN_F_TEMPLATE)) { | ||
336 | pp = ip_vs_proto_get(s->protocol); | ||
337 | if (!pp) { | ||
338 | IP_VS_ERR_RL("Unsupported protocol %u in sync msg\n", | ||
339 | s->protocol); | ||
340 | continue; | ||
341 | } | ||
342 | if (state >= pp->num_states) { | ||
343 | IP_VS_DBG(2, "Invalid %s state %u in sync msg\n", | ||
344 | pp->name, state); | ||
345 | continue; | ||
346 | } | ||
347 | } else { | ||
348 | /* protocol in templates is not used for state/timeout */ | ||
349 | pp = NULL; | ||
350 | if (state > 0) { | ||
351 | IP_VS_DBG(2, "Invalid template state %u in sync msg\n", | ||
352 | state); | ||
353 | state = 0; | ||
354 | } | ||
355 | } | ||
356 | |||
313 | if (!(flags & IP_VS_CONN_F_TEMPLATE)) | 357 | if (!(flags & IP_VS_CONN_F_TEMPLATE)) |
314 | cp = ip_vs_conn_in_get(s->protocol, | 358 | cp = ip_vs_conn_in_get(s->protocol, |
315 | s->caddr, s->cport, | 359 | s->caddr, s->cport, |
@@ -345,14 +389,9 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen) | |||
345 | IP_VS_ERR("ip_vs_conn_new failed\n"); | 389 | IP_VS_ERR("ip_vs_conn_new failed\n"); |
346 | return; | 390 | return; |
347 | } | 391 | } |
348 | cp->state = state; | ||
349 | } else if (!cp->dest) { | 392 | } else if (!cp->dest) { |
350 | dest = ip_vs_try_bind_dest(cp); | 393 | dest = ip_vs_try_bind_dest(cp); |
351 | if (!dest) { | 394 | if (dest) |
352 | /* it is an unbound entry created by | ||
353 | * synchronization */ | ||
354 | cp->flags = flags | IP_VS_CONN_F_HASHED; | ||
355 | } else | ||
356 | atomic_dec(&dest->refcnt); | 395 | atomic_dec(&dest->refcnt); |
357 | } else if ((cp->dest) && (cp->protocol == IPPROTO_TCP) && | 396 | } else if ((cp->dest) && (cp->protocol == IPPROTO_TCP) && |
358 | (cp->state != state)) { | 397 | (cp->state != state)) { |
@@ -371,23 +410,22 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen) | |||
371 | } | 410 | } |
372 | } | 411 | } |
373 | 412 | ||
374 | if (flags & IP_VS_CONN_F_SEQ_MASK) { | 413 | if (opt) |
375 | opt = (struct ip_vs_sync_conn_options *)&s[1]; | ||
376 | memcpy(&cp->in_seq, opt, sizeof(*opt)); | 414 | memcpy(&cp->in_seq, opt, sizeof(*opt)); |
377 | p += FULL_CONN_SIZE; | ||
378 | } else | ||
379 | p += SIMPLE_CONN_SIZE; | ||
380 | |||
381 | atomic_set(&cp->in_pkts, sysctl_ip_vs_sync_threshold[0]); | 415 | atomic_set(&cp->in_pkts, sysctl_ip_vs_sync_threshold[0]); |
382 | cp->state = state; | 416 | cp->state = state; |
383 | pp = ip_vs_proto_get(s->protocol); | 417 | cp->old_state = cp->state; |
384 | cp->timeout = pp->timeout_table[cp->state]; | 418 | /* |
419 | * We can not recover the right timeout for templates | ||
420 | * in all cases, we can not find the right fwmark | ||
421 | * virtual service. If needed, we can do it for | ||
422 | * non-fwmark persistent services. | ||
423 | */ | ||
424 | if (!(flags & IP_VS_CONN_F_TEMPLATE) && pp->timeout_table) | ||
425 | cp->timeout = pp->timeout_table[state]; | ||
426 | else | ||
427 | cp->timeout = (3*60*HZ); | ||
385 | ip_vs_conn_put(cp); | 428 | ip_vs_conn_put(cp); |
386 | |||
387 | if (p > buffer+buflen) { | ||
388 | IP_VS_ERR("bogus message\n"); | ||
389 | return; | ||
390 | } | ||
391 | } | 429 | } |
392 | } | 430 | } |
393 | 431 | ||