Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'igmp_mld_export'

Linus Lüssing says:

====================
Exporting IGMP/MLD checking from bridge code

The multicast optimizations in batman-adv are yet only usable and
enabled in non-bridged scenarios. To be able to support bridged setups
batman-adv needs to be able to detect IGMP/MLD queriers and reports on
mesh nodes without bridges, too. See the following link for details:

http://www.open-mesh.org/projects/batman-adv/wiki/Multicast-optimizations-listener-reports

To avoid duplicate code between the bridge and batman-adv, the IGMP/MLD
message validation code is moved from the bridge to the IPv4/IPv6 stack.

On the way, some refactoring to increase readability and to iron out
some subtle differences between the IGMP and MLD parsing code is done.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>

+498 -208
+1
include/linux/igmp.h
··· 130 130 extern void ip_mc_remap(struct in_device *); 131 131 extern void ip_mc_dec_group(struct in_device *in_dev, __be32 addr); 132 132 extern void ip_mc_inc_group(struct in_device *in_dev, __be32 addr); 133 + int ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed); 133 134 134 135 #endif
+3
include/linux/skbuff.h
··· 3419 3419 bool skb_partial_csum_set(struct sk_buff *skb, u16 start, u16 off); 3420 3420 3421 3421 int skb_checksum_setup(struct sk_buff *skb, bool recalculate); 3422 + struct sk_buff *skb_checksum_trimmed(struct sk_buff *skb, 3423 + unsigned int transport_len, 3424 + __sum16(*skb_chkf)(struct sk_buff *skb)); 3422 3425 3423 3426 u32 skb_get_poff(const struct sk_buff *skb); 3424 3427 u32 __skb_get_poff(const struct sk_buff *skb, void *data,
+1
include/net/addrconf.h
··· 142 142 void ipv6_mc_remap(struct inet6_dev *idev); 143 143 void ipv6_mc_init_dev(struct inet6_dev *idev); 144 144 void ipv6_mc_destroy_dev(struct inet6_dev *idev); 145 + int ipv6_mc_check_mld(struct sk_buff *skb, struct sk_buff **skb_trimmed); 145 146 void addrconf_dad_failure(struct inet6_ifaddr *ifp); 146 147 147 148 bool ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group,
+30 -208
net/bridge/br_multicast.c
··· 975 975 int err = 0; 976 976 __be32 group; 977 977 978 - if (!pskb_may_pull(skb, sizeof(*ih))) 979 - return -EINVAL; 980 - 981 978 ih = igmpv3_report_hdr(skb); 982 979 num = ntohs(ih->ngrec); 983 980 len = sizeof(*ih); ··· 1245 1248 max_delay = 10 * HZ; 1246 1249 group = 0; 1247 1250 } 1248 - } else { 1249 - if (!pskb_may_pull(skb, sizeof(struct igmpv3_query))) { 1250 - err = -EINVAL; 1251 - goto out; 1252 - } 1253 - 1251 + } else if (skb->len >= sizeof(*ih3)) { 1254 1252 ih3 = igmpv3_query_hdr(skb); 1255 1253 if (ih3->nsrcs) 1256 1254 goto out; 1257 1255 1258 1256 max_delay = ih3->code ? 1259 1257 IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE) : 1; 1260 - } 1261 - 1262 - /* RFC2236+RFC3376 (IGMPv2+IGMPv3) require the multicast link layer 1263 - * all-systems destination addresses (224.0.0.1) for general queries 1264 - */ 1265 - if (!group && iph->daddr != htonl(INADDR_ALLHOSTS_GROUP)) { 1266 - err = -EINVAL; 1258 + } else { 1267 1259 goto out; 1268 1260 } 1269 1261 ··· 1315 1329 (port && port->state == BR_STATE_DISABLED)) 1316 1330 goto out; 1317 1331 1318 - /* RFC2710+RFC3810 (MLDv1+MLDv2) require link-local source addresses */ 1319 - if (!(ipv6_addr_type(&ip6h->saddr) & IPV6_ADDR_LINKLOCAL)) { 1320 - err = -EINVAL; 1321 - goto out; 1322 - } 1323 - 1324 1332 if (skb->len == sizeof(*mld)) { 1325 1333 if (!pskb_may_pull(skb, sizeof(*mld))) { 1326 1334 err = -EINVAL; ··· 1337 1357 } 1338 1358 1339 1359 is_general_query = group && ipv6_addr_any(group); 1340 - 1341 - /* RFC2710+RFC3810 (MLDv1+MLDv2) require the multicast link layer 1342 - * all-nodes destination address (ff02::1) for general queries 1343 - */ 1344 - if (is_general_query && !ipv6_addr_is_ll_all_nodes(&ip6h->daddr)) { 1345 - err = -EINVAL; 1346 - goto out; 1347 - } 1348 1360 1349 1361 if (is_general_query) { 1350 1362 saddr.proto = htons(ETH_P_IPV6); ··· 1529 1557 struct sk_buff *skb, 1530 1558 u16 vid) 1531 1559 { 1532 - struct sk_buff *skb2 = skb; 1533 - const struct iphdr *iph; 1560 + struct sk_buff *skb_trimmed = NULL; 1534 1561 struct igmphdr *ih; 1535 - unsigned int len; 1536 - unsigned int offset; 1537 1562 int err; 1538 1563 1539 - /* We treat OOM as packet loss for now. */ 1540 - if (!pskb_may_pull(skb, sizeof(*iph))) 1541 - return -EINVAL; 1564 + err = ip_mc_check_igmp(skb, &skb_trimmed); 1542 1565 1543 - iph = ip_hdr(skb); 1544 - 1545 - if (iph->ihl < 5 || iph->version != 4) 1546 - return -EINVAL; 1547 - 1548 - if (!pskb_may_pull(skb, ip_hdrlen(skb))) 1549 - return -EINVAL; 1550 - 1551 - iph = ip_hdr(skb); 1552 - 1553 - if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl))) 1554 - return -EINVAL; 1555 - 1556 - if (iph->protocol != IPPROTO_IGMP) { 1557 - if (!ipv4_is_local_multicast(iph->daddr)) 1566 + if (err == -ENOMSG) { 1567 + if (!ipv4_is_local_multicast(ip_hdr(skb)->daddr)) 1558 1568 BR_INPUT_SKB_CB(skb)->mrouters_only = 1; 1559 1569 return 0; 1570 + } else if (err < 0) { 1571 + return err; 1560 1572 } 1561 - 1562 - len = ntohs(iph->tot_len); 1563 - if (skb->len < len || len < ip_hdrlen(skb)) 1564 - return -EINVAL; 1565 - 1566 - if (skb->len > len) { 1567 - skb2 = skb_clone(skb, GFP_ATOMIC); 1568 - if (!skb2) 1569 - return -ENOMEM; 1570 - 1571 - err = pskb_trim_rcsum(skb2, len); 1572 - if (err) 1573 - goto err_out; 1574 - } 1575 - 1576 - len -= ip_hdrlen(skb2); 1577 - offset = skb_network_offset(skb2) + ip_hdrlen(skb2); 1578 - __skb_pull(skb2, offset); 1579 - skb_reset_transport_header(skb2); 1580 - 1581 - err = -EINVAL; 1582 - if (!pskb_may_pull(skb2, sizeof(*ih))) 1583 - goto out; 1584 - 1585 - switch (skb2->ip_summed) { 1586 - case CHECKSUM_COMPLETE: 1587 - if (!csum_fold(skb2->csum)) 1588 - break; 1589 - /* fall through */ 1590 - case CHECKSUM_NONE: 1591 - skb2->csum = 0; 1592 - if (skb_checksum_complete(skb2)) 1593 - goto out; 1594 - } 1595 - 1596 - err = 0; 1597 1573 1598 1574 BR_INPUT_SKB_CB(skb)->igmp = 1; 1599 - ih = igmp_hdr(skb2); 1575 + ih = igmp_hdr(skb); 1600 1576 1601 1577 switch (ih->type) { 1602 1578 case IGMP_HOST_MEMBERSHIP_REPORT: ··· 1553 1633 err = br_ip4_multicast_add_group(br, port, ih->group, vid); 1554 1634 break; 1555 1635 case IGMPV3_HOST_MEMBERSHIP_REPORT: 1556 - err = br_ip4_multicast_igmp3_report(br, port, skb2, vid); 1636 + err = br_ip4_multicast_igmp3_report(br, port, skb_trimmed, vid); 1557 1637 break; 1558 1638 case IGMP_HOST_MEMBERSHIP_QUERY: 1559 - err = br_ip4_multicast_query(br, port, skb2, vid); 1639 + err = br_ip4_multicast_query(br, port, skb_trimmed, vid); 1560 1640 break; 1561 1641 case IGMP_HOST_LEAVE_MESSAGE: 1562 1642 br_ip4_multicast_leave_group(br, port, ih->group, vid); 1563 1643 break; 1564 1644 } 1565 1645 1566 - out: 1567 - __skb_push(skb2, offset); 1568 - err_out: 1569 - if (skb2 != skb) 1570 - kfree_skb(skb2); 1646 + if (skb_trimmed) 1647 + kfree_skb(skb_trimmed); 1648 + 1571 1649 return err; 1572 1650 } 1573 1651 ··· 1575 1657 struct sk_buff *skb, 1576 1658 u16 vid) 1577 1659 { 1578 - struct sk_buff *skb2; 1579 - const struct ipv6hdr *ip6h; 1580 - u8 icmp6_type; 1581 - u8 nexthdr; 1582 - __be16 frag_off; 1583 - unsigned int len; 1584 - int offset; 1660 + struct sk_buff *skb_trimmed = NULL; 1661 + struct mld_msg *mld; 1585 1662 int err; 1586 1663 1587 - if (!pskb_may_pull(skb, sizeof(*ip6h))) 1588 - return -EINVAL; 1664 + err = ipv6_mc_check_mld(skb, &skb_trimmed); 1589 1665 1590 - ip6h = ipv6_hdr(skb); 1591 - 1592 - /* 1593 - * We're interested in MLD messages only. 1594 - * - Version is 6 1595 - * - MLD has always Router Alert hop-by-hop option 1596 - * - But we do not support jumbrograms. 1597 - */ 1598 - if (ip6h->version != 6) 1666 + if (err == -ENOMSG) { 1667 + if (!ipv6_addr_is_ll_all_nodes(&ipv6_hdr(skb)->daddr)) 1668 + BR_INPUT_SKB_CB(skb)->mrouters_only = 1; 1599 1669 return 0; 1600 - 1601 - /* Prevent flooding this packet if there is no listener present */ 1602 - if (!ipv6_addr_is_ll_all_nodes(&ip6h->daddr)) 1603 - BR_INPUT_SKB_CB(skb)->mrouters_only = 1; 1604 - 1605 - if (ip6h->nexthdr != IPPROTO_HOPOPTS || 1606 - ip6h->payload_len == 0) 1607 - return 0; 1608 - 1609 - len = ntohs(ip6h->payload_len) + sizeof(*ip6h); 1610 - if (skb->len < len) 1611 - return -EINVAL; 1612 - 1613 - nexthdr = ip6h->nexthdr; 1614 - offset = ipv6_skip_exthdr(skb, sizeof(*ip6h), &nexthdr, &frag_off); 1615 - 1616 - if (offset < 0 || nexthdr != IPPROTO_ICMPV6) 1617 - return 0; 1618 - 1619 - /* Okay, we found ICMPv6 header */ 1620 - skb2 = skb_clone(skb, GFP_ATOMIC); 1621 - if (!skb2) 1622 - return -ENOMEM; 1623 - 1624 - err = -EINVAL; 1625 - if (!pskb_may_pull(skb2, offset + sizeof(struct icmp6hdr))) 1626 - goto out; 1627 - 1628 - len -= offset - skb_network_offset(skb2); 1629 - 1630 - __skb_pull(skb2, offset); 1631 - skb_reset_transport_header(skb2); 1632 - skb_postpull_rcsum(skb2, skb_network_header(skb2), 1633 - skb_network_header_len(skb2)); 1634 - 1635 - icmp6_type = icmp6_hdr(skb2)->icmp6_type; 1636 - 1637 - switch (icmp6_type) { 1638 - case ICMPV6_MGM_QUERY: 1639 - case ICMPV6_MGM_REPORT: 1640 - case ICMPV6_MGM_REDUCTION: 1641 - case ICMPV6_MLD2_REPORT: 1642 - break; 1643 - default: 1644 - err = 0; 1645 - goto out; 1670 + } else if (err < 0) { 1671 + return err; 1646 1672 } 1647 - 1648 - /* Okay, we found MLD message. Check further. */ 1649 - if (skb2->len > len) { 1650 - err = pskb_trim_rcsum(skb2, len); 1651 - if (err) 1652 - goto out; 1653 - err = -EINVAL; 1654 - } 1655 - 1656 - ip6h = ipv6_hdr(skb2); 1657 - 1658 - switch (skb2->ip_summed) { 1659 - case CHECKSUM_COMPLETE: 1660 - if (!csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, skb2->len, 1661 - IPPROTO_ICMPV6, skb2->csum)) 1662 - break; 1663 - /*FALLTHROUGH*/ 1664 - case CHECKSUM_NONE: 1665 - skb2->csum = ~csum_unfold(csum_ipv6_magic(&ip6h->saddr, 1666 - &ip6h->daddr, 1667 - skb2->len, 1668 - IPPROTO_ICMPV6, 0)); 1669 - if (__skb_checksum_complete(skb2)) 1670 - goto out; 1671 - } 1672 - 1673 - err = 0; 1674 1673 1675 1674 BR_INPUT_SKB_CB(skb)->igmp = 1; 1675 + mld = (struct mld_msg *)skb_transport_header(skb); 1676 1676 1677 - switch (icmp6_type) { 1677 + switch (mld->mld_type) { 1678 1678 case ICMPV6_MGM_REPORT: 1679 - { 1680 - struct mld_msg *mld; 1681 - if (!pskb_may_pull(skb2, sizeof(*mld))) { 1682 - err = -EINVAL; 1683 - goto out; 1684 - } 1685 - mld = (struct mld_msg *)skb_transport_header(skb2); 1686 1679 BR_INPUT_SKB_CB(skb)->mrouters_only = 1; 1687 1680 err = br_ip6_multicast_add_group(br, port, &mld->mld_mca, vid); 1688 1681 break; 1689 - } 1690 1682 case ICMPV6_MLD2_REPORT: 1691 - err = br_ip6_multicast_mld2_report(br, port, skb2, vid); 1683 + err = br_ip6_multicast_mld2_report(br, port, skb_trimmed, vid); 1692 1684 break; 1693 1685 case ICMPV6_MGM_QUERY: 1694 - err = br_ip6_multicast_query(br, port, skb2, vid); 1686 + err = br_ip6_multicast_query(br, port, skb_trimmed, vid); 1695 1687 break; 1696 1688 case ICMPV6_MGM_REDUCTION: 1697 - { 1698 - struct mld_msg *mld; 1699 - if (!pskb_may_pull(skb2, sizeof(*mld))) { 1700 - err = -EINVAL; 1701 - goto out; 1702 - } 1703 - mld = (struct mld_msg *)skb_transport_header(skb2); 1704 1689 br_ip6_multicast_leave_group(br, port, &mld->mld_mca, vid); 1705 - } 1690 + break; 1706 1691 } 1707 1692 1708 - out: 1709 - kfree_skb(skb2); 1693 + if (skb_trimmed) 1694 + kfree_skb(skb_trimmed); 1695 + 1710 1696 return err; 1711 1697 } 1712 1698 #endif
+87
net/core/skbuff.c
··· 4030 4030 } 4031 4031 EXPORT_SYMBOL(skb_checksum_setup); 4032 4032 4033 + /** 4034 + * skb_checksum_maybe_trim - maybe trims the given skb 4035 + * @skb: the skb to check 4036 + * @transport_len: the data length beyond the network header 4037 + * 4038 + * Checks whether the given skb has data beyond the given transport length. 4039 + * If so, returns a cloned skb trimmed to this transport length. 4040 + * Otherwise returns the provided skb. Returns NULL in error cases 4041 + * (e.g. transport_len exceeds skb length or out-of-memory). 4042 + * 4043 + * Caller needs to set the skb transport header and release the returned skb. 4044 + * Provided skb is consumed. 4045 + */ 4046 + static struct sk_buff *skb_checksum_maybe_trim(struct sk_buff *skb, 4047 + unsigned int transport_len) 4048 + { 4049 + struct sk_buff *skb_chk; 4050 + unsigned int len = skb_transport_offset(skb) + transport_len; 4051 + int ret; 4052 + 4053 + if (skb->len < len) { 4054 + kfree_skb(skb); 4055 + return NULL; 4056 + } else if (skb->len == len) { 4057 + return skb; 4058 + } 4059 + 4060 + skb_chk = skb_clone(skb, GFP_ATOMIC); 4061 + kfree_skb(skb); 4062 + 4063 + if (!skb_chk) 4064 + return NULL; 4065 + 4066 + ret = pskb_trim_rcsum(skb_chk, len); 4067 + if (ret) { 4068 + kfree_skb(skb_chk); 4069 + return NULL; 4070 + } 4071 + 4072 + return skb_chk; 4073 + } 4074 + 4075 + /** 4076 + * skb_checksum_trimmed - validate checksum of an skb 4077 + * @skb: the skb to check 4078 + * @transport_len: the data length beyond the network header 4079 + * @skb_chkf: checksum function to use 4080 + * 4081 + * Applies the given checksum function skb_chkf to the provided skb. 4082 + * Returns a checked and maybe trimmed skb. Returns NULL on error. 4083 + * 4084 + * If the skb has data beyond the given transport length, then a 4085 + * trimmed & cloned skb is checked and returned. 4086 + * 4087 + * Caller needs to set the skb transport header and release the returned skb. 4088 + * Provided skb is consumed. 4089 + */ 4090 + struct sk_buff *skb_checksum_trimmed(struct sk_buff *skb, 4091 + unsigned int transport_len, 4092 + __sum16(*skb_chkf)(struct sk_buff *skb)) 4093 + { 4094 + struct sk_buff *skb_chk; 4095 + unsigned int offset = skb_transport_offset(skb); 4096 + int ret; 4097 + 4098 + skb_chk = skb_checksum_maybe_trim(skb, transport_len); 4099 + if (!skb_chk) 4100 + return NULL; 4101 + 4102 + if (!pskb_may_pull(skb_chk, offset)) { 4103 + kfree_skb(skb_chk); 4104 + return NULL; 4105 + } 4106 + 4107 + __skb_pull(skb_chk, offset); 4108 + ret = skb_chkf(skb_chk); 4109 + __skb_push(skb_chk, offset); 4110 + 4111 + if (ret) { 4112 + kfree_skb(skb_chk); 4113 + return NULL; 4114 + } 4115 + 4116 + return skb_chk; 4117 + } 4118 + EXPORT_SYMBOL(skb_checksum_trimmed); 4119 + 4033 4120 void __skb_warn_lro_forwarding(const struct sk_buff *skb) 4034 4121 { 4035 4122 net_warn_ratelimited("%s: received packets cannot be forwarded while LRO is enabled\n",
+162
net/ipv4/igmp.c
··· 1339 1339 } 1340 1340 EXPORT_SYMBOL(ip_mc_inc_group); 1341 1341 1342 + static int ip_mc_check_iphdr(struct sk_buff *skb) 1343 + { 1344 + const struct iphdr *iph; 1345 + unsigned int len; 1346 + unsigned int offset = skb_network_offset(skb) + sizeof(*iph); 1347 + 1348 + if (!pskb_may_pull(skb, offset)) 1349 + return -EINVAL; 1350 + 1351 + iph = ip_hdr(skb); 1352 + 1353 + if (iph->version != 4 || ip_hdrlen(skb) < sizeof(*iph)) 1354 + return -EINVAL; 1355 + 1356 + offset += ip_hdrlen(skb) - sizeof(*iph); 1357 + 1358 + if (!pskb_may_pull(skb, offset)) 1359 + return -EINVAL; 1360 + 1361 + iph = ip_hdr(skb); 1362 + 1363 + if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl))) 1364 + return -EINVAL; 1365 + 1366 + len = skb_network_offset(skb) + ntohs(iph->tot_len); 1367 + if (skb->len < len || len < offset) 1368 + return -EINVAL; 1369 + 1370 + skb_set_transport_header(skb, offset); 1371 + 1372 + return 0; 1373 + } 1374 + 1375 + static int ip_mc_check_igmp_reportv3(struct sk_buff *skb) 1376 + { 1377 + unsigned int len = skb_transport_offset(skb); 1378 + 1379 + len += sizeof(struct igmpv3_report); 1380 + 1381 + return pskb_may_pull(skb, len) ? 0 : -EINVAL; 1382 + } 1383 + 1384 + static int ip_mc_check_igmp_query(struct sk_buff *skb) 1385 + { 1386 + unsigned int len = skb_transport_offset(skb); 1387 + 1388 + len += sizeof(struct igmphdr); 1389 + if (skb->len < len) 1390 + return -EINVAL; 1391 + 1392 + /* IGMPv{1,2}? */ 1393 + if (skb->len != len) { 1394 + /* or IGMPv3? */ 1395 + len += sizeof(struct igmpv3_query) - sizeof(struct igmphdr); 1396 + if (skb->len < len || !pskb_may_pull(skb, len)) 1397 + return -EINVAL; 1398 + } 1399 + 1400 + /* RFC2236+RFC3376 (IGMPv2+IGMPv3) require the multicast link layer 1401 + * all-systems destination addresses (224.0.0.1) for general queries 1402 + */ 1403 + if (!igmp_hdr(skb)->group && 1404 + ip_hdr(skb)->daddr != htonl(INADDR_ALLHOSTS_GROUP)) 1405 + return -EINVAL; 1406 + 1407 + return 0; 1408 + } 1409 + 1410 + static int ip_mc_check_igmp_msg(struct sk_buff *skb) 1411 + { 1412 + switch (igmp_hdr(skb)->type) { 1413 + case IGMP_HOST_LEAVE_MESSAGE: 1414 + case IGMP_HOST_MEMBERSHIP_REPORT: 1415 + case IGMPV2_HOST_MEMBERSHIP_REPORT: 1416 + /* fall through */ 1417 + return 0; 1418 + case IGMPV3_HOST_MEMBERSHIP_REPORT: 1419 + return ip_mc_check_igmp_reportv3(skb); 1420 + case IGMP_HOST_MEMBERSHIP_QUERY: 1421 + return ip_mc_check_igmp_query(skb); 1422 + default: 1423 + return -ENOMSG; 1424 + } 1425 + } 1426 + 1427 + static inline __sum16 ip_mc_validate_checksum(struct sk_buff *skb) 1428 + { 1429 + return skb_checksum_simple_validate(skb); 1430 + } 1431 + 1432 + static int __ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed) 1433 + 1434 + { 1435 + struct sk_buff *skb_chk; 1436 + unsigned int transport_len; 1437 + unsigned int len = skb_transport_offset(skb) + sizeof(struct igmphdr); 1438 + int ret; 1439 + 1440 + transport_len = ntohs(ip_hdr(skb)->tot_len) - ip_hdrlen(skb); 1441 + 1442 + skb_get(skb); 1443 + skb_chk = skb_checksum_trimmed(skb, transport_len, 1444 + ip_mc_validate_checksum); 1445 + if (!skb_chk) 1446 + return -EINVAL; 1447 + 1448 + if (!pskb_may_pull(skb_chk, len)) { 1449 + kfree_skb(skb_chk); 1450 + return -EINVAL; 1451 + } 1452 + 1453 + ret = ip_mc_check_igmp_msg(skb_chk); 1454 + if (ret) { 1455 + kfree_skb(skb_chk); 1456 + return ret; 1457 + } 1458 + 1459 + if (skb_trimmed) 1460 + *skb_trimmed = skb_chk; 1461 + else 1462 + kfree_skb(skb_chk); 1463 + 1464 + return 0; 1465 + } 1466 + 1467 + /** 1468 + * ip_mc_check_igmp - checks whether this is a sane IGMP packet 1469 + * @skb: the skb to validate 1470 + * @skb_trimmed: to store an skb pointer trimmed to IPv4 packet tail (optional) 1471 + * 1472 + * Checks whether an IPv4 packet is a valid IGMP packet. If so sets 1473 + * skb network and transport headers accordingly and returns zero. 1474 + * 1475 + * -EINVAL: A broken packet was detected, i.e. it violates some internet 1476 + * standard 1477 + * -ENOMSG: IP header validation succeeded but it is not an IGMP packet. 1478 + * -ENOMEM: A memory allocation failure happened. 1479 + * 1480 + * Optionally, an skb pointer might be provided via skb_trimmed (or set it 1481 + * to NULL): After parsing an IGMP packet successfully it will point to 1482 + * an skb which has its tail aligned to the IP packet end. This might 1483 + * either be the originally provided skb or a trimmed, cloned version if 1484 + * the skb frame had data beyond the IP packet. A cloned skb allows us 1485 + * to leave the original skb and its full frame unchanged (which might be 1486 + * desirable for layer 2 frame jugglers). 1487 + * 1488 + * The caller needs to release a reference count from any returned skb_trimmed. 1489 + */ 1490 + int ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed) 1491 + { 1492 + int ret = ip_mc_check_iphdr(skb); 1493 + 1494 + if (ret < 0) 1495 + return ret; 1496 + 1497 + if (ip_hdr(skb)->protocol != IPPROTO_IGMP) 1498 + return -ENOMSG; 1499 + 1500 + return __ip_mc_check_igmp(skb, skb_trimmed); 1501 + } 1502 + EXPORT_SYMBOL(ip_mc_check_igmp); 1503 + 1342 1504 /* 1343 1505 * Resend IGMP JOIN report; used by netdev notifier. 1344 1506 */
+1
net/ipv6/Makefile
··· 48 48 49 49 ifneq ($(CONFIG_IPV6),) 50 50 obj-$(CONFIG_NET_UDP_TUNNEL) += ip6_udp_tunnel.o 51 + obj-y += mcast_snoop.o 51 52 endif
+213
net/ipv6/mcast_snoop.c
··· 1 + /* Copyright (C) 2010: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org> 2 + * Copyright (C) 2015: Linus Lüssing <linus.luessing@c0d3.blue> 3 + * 4 + * This program is free software; you can redistribute it and/or 5 + * modify it under the terms of version 2 of the GNU General Public 6 + * License as published by the Free Software Foundation. 7 + * 8 + * This program is distributed in the hope that it will be useful, but 9 + * WITHOUT ANY WARRANTY; without even the implied warranty of 10 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 + * General Public License for more details. 12 + * 13 + * You should have received a copy of the GNU General Public License 14 + * along with this program; if not, see <http://www.gnu.org/licenses/>. 15 + * 16 + * 17 + * Based on the MLD support added to br_multicast.c by YOSHIFUJI Hideaki. 18 + */ 19 + 20 + #include <linux/skbuff.h> 21 + #include <net/ipv6.h> 22 + #include <net/mld.h> 23 + #include <net/addrconf.h> 24 + #include <net/ip6_checksum.h> 25 + 26 + static int ipv6_mc_check_ip6hdr(struct sk_buff *skb) 27 + { 28 + const struct ipv6hdr *ip6h; 29 + unsigned int len; 30 + unsigned int offset = skb_network_offset(skb) + sizeof(*ip6h); 31 + 32 + if (!pskb_may_pull(skb, offset)) 33 + return -EINVAL; 34 + 35 + ip6h = ipv6_hdr(skb); 36 + 37 + if (ip6h->version != 6) 38 + return -EINVAL; 39 + 40 + len = offset + ntohs(ip6h->payload_len); 41 + if (skb->len < len || len <= offset) 42 + return -EINVAL; 43 + 44 + return 0; 45 + } 46 + 47 + static int ipv6_mc_check_exthdrs(struct sk_buff *skb) 48 + { 49 + const struct ipv6hdr *ip6h; 50 + unsigned int offset; 51 + u8 nexthdr; 52 + __be16 frag_off; 53 + 54 + ip6h = ipv6_hdr(skb); 55 + 56 + if (ip6h->nexthdr != IPPROTO_HOPOPTS) 57 + return -ENOMSG; 58 + 59 + nexthdr = ip6h->nexthdr; 60 + offset = skb_network_offset(skb) + sizeof(*ip6h); 61 + offset = ipv6_skip_exthdr(skb, offset, &nexthdr, &frag_off); 62 + 63 + if (offset < 0) 64 + return -EINVAL; 65 + 66 + if (nexthdr != IPPROTO_ICMPV6) 67 + return -ENOMSG; 68 + 69 + skb_set_transport_header(skb, offset); 70 + 71 + return 0; 72 + } 73 + 74 + static int ipv6_mc_check_mld_reportv2(struct sk_buff *skb) 75 + { 76 + unsigned int len = skb_transport_offset(skb); 77 + 78 + len += sizeof(struct mld2_report); 79 + 80 + return pskb_may_pull(skb, len) ? 0 : -EINVAL; 81 + } 82 + 83 + static int ipv6_mc_check_mld_query(struct sk_buff *skb) 84 + { 85 + struct mld_msg *mld; 86 + unsigned int len = skb_transport_offset(skb); 87 + 88 + /* RFC2710+RFC3810 (MLDv1+MLDv2) require link-local source addresses */ 89 + if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) 90 + return -EINVAL; 91 + 92 + len += sizeof(struct mld_msg); 93 + if (skb->len < len) 94 + return -EINVAL; 95 + 96 + /* MLDv1? */ 97 + if (skb->len != len) { 98 + /* or MLDv2? */ 99 + len += sizeof(struct mld2_query) - sizeof(struct mld_msg); 100 + if (skb->len < len || !pskb_may_pull(skb, len)) 101 + return -EINVAL; 102 + } 103 + 104 + mld = (struct mld_msg *)skb_transport_header(skb); 105 + 106 + /* RFC2710+RFC3810 (MLDv1+MLDv2) require the multicast link layer 107 + * all-nodes destination address (ff02::1) for general queries 108 + */ 109 + if (ipv6_addr_any(&mld->mld_mca) && 110 + !ipv6_addr_is_ll_all_nodes(&ipv6_hdr(skb)->daddr)) 111 + return -EINVAL; 112 + 113 + return 0; 114 + } 115 + 116 + static int ipv6_mc_check_mld_msg(struct sk_buff *skb) 117 + { 118 + struct mld_msg *mld = (struct mld_msg *)skb_transport_header(skb); 119 + 120 + switch (mld->mld_type) { 121 + case ICMPV6_MGM_REDUCTION: 122 + case ICMPV6_MGM_REPORT: 123 + /* fall through */ 124 + return 0; 125 + case ICMPV6_MLD2_REPORT: 126 + return ipv6_mc_check_mld_reportv2(skb); 127 + case ICMPV6_MGM_QUERY: 128 + return ipv6_mc_check_mld_query(skb); 129 + default: 130 + return -ENOMSG; 131 + } 132 + } 133 + 134 + static inline __sum16 ipv6_mc_validate_checksum(struct sk_buff *skb) 135 + { 136 + return skb_checksum_validate(skb, IPPROTO_ICMPV6, ip6_compute_pseudo); 137 + } 138 + 139 + static int __ipv6_mc_check_mld(struct sk_buff *skb, 140 + struct sk_buff **skb_trimmed) 141 + 142 + { 143 + struct sk_buff *skb_chk = NULL; 144 + unsigned int transport_len; 145 + unsigned int len = skb_transport_offset(skb) + sizeof(struct mld_msg); 146 + int ret; 147 + 148 + transport_len = ntohs(ipv6_hdr(skb)->payload_len); 149 + transport_len -= skb_transport_offset(skb) - sizeof(struct ipv6hdr); 150 + 151 + skb_get(skb); 152 + skb_chk = skb_checksum_trimmed(skb, transport_len, 153 + ipv6_mc_validate_checksum); 154 + if (!skb_chk) 155 + return -EINVAL; 156 + 157 + if (!pskb_may_pull(skb_chk, len)) { 158 + kfree_skb(skb_chk); 159 + return -EINVAL; 160 + } 161 + 162 + ret = ipv6_mc_check_mld_msg(skb_chk); 163 + if (ret) { 164 + kfree_skb(skb_chk); 165 + return ret; 166 + } 167 + 168 + if (skb_trimmed) 169 + *skb_trimmed = skb_chk; 170 + else 171 + kfree_skb(skb_chk); 172 + 173 + return 0; 174 + } 175 + 176 + /** 177 + * ipv6_mc_check_mld - checks whether this is a sane MLD packet 178 + * @skb: the skb to validate 179 + * @skb_trimmed: to store an skb pointer trimmed to IPv6 packet tail (optional) 180 + * 181 + * Checks whether an IPv6 packet is a valid MLD packet. If so sets 182 + * skb network and transport headers accordingly and returns zero. 183 + * 184 + * -EINVAL: A broken packet was detected, i.e. it violates some internet 185 + * standard 186 + * -ENOMSG: IP header validation succeeded but it is not an MLD packet. 187 + * -ENOMEM: A memory allocation failure happened. 188 + * 189 + * Optionally, an skb pointer might be provided via skb_trimmed (or set it 190 + * to NULL): After parsing an MLD packet successfully it will point to 191 + * an skb which has its tail aligned to the IP packet end. This might 192 + * either be the originally provided skb or a trimmed, cloned version if 193 + * the skb frame had data beyond the IP packet. A cloned skb allows us 194 + * to leave the original skb and its full frame unchanged (which might be 195 + * desirable for layer 2 frame jugglers). 196 + * 197 + * The caller needs to release a reference count from any returned skb_trimmed. 198 + */ 199 + int ipv6_mc_check_mld(struct sk_buff *skb, struct sk_buff **skb_trimmed) 200 + { 201 + int ret; 202 + 203 + ret = ipv6_mc_check_ip6hdr(skb); 204 + if (ret < 0) 205 + return ret; 206 + 207 + ret = ipv6_mc_check_exthdrs(skb); 208 + if (ret < 0) 209 + return ret; 210 + 211 + return __ipv6_mc_check_mld(skb, skb_trimmed); 212 + } 213 + EXPORT_SYMBOL(ipv6_mc_check_mld);