diff options
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/igmp.c | 33 |
1 files changed, 18 insertions, 15 deletions
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 651cdf648ec4..9fdfd9deac11 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c | |||
@@ -1435,33 +1435,35 @@ static int __ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed) | |||
1435 | struct sk_buff *skb_chk; | 1435 | struct sk_buff *skb_chk; |
1436 | unsigned int transport_len; | 1436 | unsigned int transport_len; |
1437 | unsigned int len = skb_transport_offset(skb) + sizeof(struct igmphdr); | 1437 | unsigned int len = skb_transport_offset(skb) + sizeof(struct igmphdr); |
1438 | int ret; | 1438 | int ret = -EINVAL; |
1439 | 1439 | ||
1440 | transport_len = ntohs(ip_hdr(skb)->tot_len) - ip_hdrlen(skb); | 1440 | transport_len = ntohs(ip_hdr(skb)->tot_len) - ip_hdrlen(skb); |
1441 | 1441 | ||
1442 | skb_get(skb); | ||
1443 | skb_chk = skb_checksum_trimmed(skb, transport_len, | 1442 | skb_chk = skb_checksum_trimmed(skb, transport_len, |
1444 | ip_mc_validate_checksum); | 1443 | ip_mc_validate_checksum); |
1445 | if (!skb_chk) | 1444 | if (!skb_chk) |
1446 | return -EINVAL; | 1445 | goto err; |
1447 | 1446 | ||
1448 | if (!pskb_may_pull(skb_chk, len)) { | 1447 | if (!pskb_may_pull(skb_chk, len)) |
1449 | kfree_skb(skb_chk); | 1448 | goto err; |
1450 | return -EINVAL; | ||
1451 | } | ||
1452 | 1449 | ||
1453 | ret = ip_mc_check_igmp_msg(skb_chk); | 1450 | ret = ip_mc_check_igmp_msg(skb_chk); |
1454 | if (ret) { | 1451 | if (ret) |
1455 | kfree_skb(skb_chk); | 1452 | goto err; |
1456 | return ret; | ||
1457 | } | ||
1458 | 1453 | ||
1459 | if (skb_trimmed) | 1454 | if (skb_trimmed) |
1460 | *skb_trimmed = skb_chk; | 1455 | *skb_trimmed = skb_chk; |
1461 | else | 1456 | /* free now unneeded clone */ |
1457 | else if (skb_chk != skb) | ||
1462 | kfree_skb(skb_chk); | 1458 | kfree_skb(skb_chk); |
1463 | 1459 | ||
1464 | return 0; | 1460 | ret = 0; |
1461 | |||
1462 | err: | ||
1463 | if (ret && skb_chk && skb_chk != skb) | ||
1464 | kfree_skb(skb_chk); | ||
1465 | |||
1466 | return ret; | ||
1465 | } | 1467 | } |
1466 | 1468 | ||
1467 | /** | 1469 | /** |
@@ -1470,7 +1472,7 @@ static int __ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed) | |||
1470 | * @skb_trimmed: to store an skb pointer trimmed to IPv4 packet tail (optional) | 1472 | * @skb_trimmed: to store an skb pointer trimmed to IPv4 packet tail (optional) |
1471 | * | 1473 | * |
1472 | * Checks whether an IPv4 packet is a valid IGMP packet. If so sets | 1474 | * Checks whether an IPv4 packet is a valid IGMP packet. If so sets |
1473 | * skb network and transport headers accordingly and returns zero. | 1475 | * skb transport header accordingly and returns zero. |
1474 | * | 1476 | * |
1475 | * -EINVAL: A broken packet was detected, i.e. it violates some internet | 1477 | * -EINVAL: A broken packet was detected, i.e. it violates some internet |
1476 | * standard | 1478 | * standard |
@@ -1485,7 +1487,8 @@ static int __ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed) | |||
1485 | * to leave the original skb and its full frame unchanged (which might be | 1487 | * to leave the original skb and its full frame unchanged (which might be |
1486 | * desirable for layer 2 frame jugglers). | 1488 | * desirable for layer 2 frame jugglers). |
1487 | * | 1489 | * |
1488 | * The caller needs to release a reference count from any returned skb_trimmed. | 1490 | * Caller needs to set the skb network header and free any returned skb if it |
1491 | * differs from the provided skb. | ||
1489 | */ | 1492 | */ |
1490 | int ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed) | 1493 | int ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed) |
1491 | { | 1494 | { |