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 /net/ipv4/ipvs/ip_vs_sync.c | |
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>
Diffstat (limited to 'net/ipv4/ipvs/ip_vs_sync.c')
-rw-r--r-- | net/ipv4/ipvs/ip_vs_sync.c | 80 |
1 files changed, 59 insertions, 21 deletions
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 | ||