diff options
Diffstat (limited to 'net/ipv4/igmp.c')
| -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 | { |
