| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Linux NET3: Internet Group Management Protocol [IGMP] |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 10 | 11 | * |
|---|
| 11 | 12 | * Authors: |
|---|
| 12 | 13 | * Alan Cox <alan@lxorguk.ukuu.org.uk> |
|---|
| 13 | | - * |
|---|
| 14 | | - * This program is free software; you can redistribute it and/or |
|---|
| 15 | | - * modify it under the terms of the GNU General Public License |
|---|
| 16 | | - * as published by the Free Software Foundation; either version |
|---|
| 17 | | - * 2 of the License, or (at your option) any later version. |
|---|
| 18 | 14 | * |
|---|
| 19 | 15 | * Fixes: |
|---|
| 20 | 16 | * |
|---|
| .. | .. |
|---|
| 111 | 107 | #ifdef CONFIG_IP_MULTICAST |
|---|
| 112 | 108 | /* Parameter names and values are taken from igmp-v2-06 draft */ |
|---|
| 113 | 109 | |
|---|
| 114 | | -#define IGMP_V2_UNSOLICITED_REPORT_INTERVAL (10*HZ) |
|---|
| 115 | | -#define IGMP_V3_UNSOLICITED_REPORT_INTERVAL (1*HZ) |
|---|
| 116 | 110 | #define IGMP_QUERY_INTERVAL (125*HZ) |
|---|
| 117 | 111 | #define IGMP_QUERY_RESPONSE_INTERVAL (10*HZ) |
|---|
| 118 | 112 | |
|---|
| .. | .. |
|---|
| 159 | 153 | return interval_jiffies; |
|---|
| 160 | 154 | } |
|---|
| 161 | 155 | |
|---|
| 162 | | -static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im); |
|---|
| 156 | +static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im, |
|---|
| 157 | + gfp_t gfp); |
|---|
| 163 | 158 | static void igmpv3_del_delrec(struct in_device *in_dev, struct ip_mc_list *im); |
|---|
| 164 | 159 | static void igmpv3_clear_delrec(struct in_device *in_dev); |
|---|
| 165 | 160 | static int sf_setstate(struct ip_mc_list *pmc); |
|---|
| .. | .. |
|---|
| 335 | 330 | const struct flowi4 *fl4) |
|---|
| 336 | 331 | { |
|---|
| 337 | 332 | struct in_device *in_dev = __in_dev_get_rcu(dev); |
|---|
| 333 | + const struct in_ifaddr *ifa; |
|---|
| 338 | 334 | |
|---|
| 339 | 335 | if (!in_dev) |
|---|
| 340 | 336 | return htonl(INADDR_ANY); |
|---|
| 341 | 337 | |
|---|
| 342 | | - for_ifa(in_dev) { |
|---|
| 338 | + in_dev_for_each_ifa_rcu(ifa, in_dev) { |
|---|
| 343 | 339 | if (fl4->saddr == ifa->ifa_local) |
|---|
| 344 | 340 | return fl4->saddr; |
|---|
| 345 | | - } endfor_ifa(in_dev); |
|---|
| 341 | + } |
|---|
| 346 | 342 | |
|---|
| 347 | 343 | return htonl(INADDR_ANY); |
|---|
| 348 | 344 | } |
|---|
| .. | .. |
|---|
| 471 | 467 | |
|---|
| 472 | 468 | if (pmc->multiaddr == IGMP_ALL_HOSTS) |
|---|
| 473 | 469 | return skb; |
|---|
| 474 | | - if (ipv4_is_local_multicast(pmc->multiaddr) && !net->ipv4.sysctl_igmp_llm_reports) |
|---|
| 470 | + if (ipv4_is_local_multicast(pmc->multiaddr) && |
|---|
| 471 | + !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) |
|---|
| 475 | 472 | return skb; |
|---|
| 476 | 473 | |
|---|
| 477 | 474 | mtu = READ_ONCE(dev->mtu); |
|---|
| .. | .. |
|---|
| 597 | 594 | if (pmc->multiaddr == IGMP_ALL_HOSTS) |
|---|
| 598 | 595 | continue; |
|---|
| 599 | 596 | if (ipv4_is_local_multicast(pmc->multiaddr) && |
|---|
| 600 | | - !net->ipv4.sysctl_igmp_llm_reports) |
|---|
| 597 | + !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) |
|---|
| 601 | 598 | continue; |
|---|
| 602 | 599 | spin_lock_bh(&pmc->lock); |
|---|
| 603 | 600 | if (pmc->sfcount[MCAST_EXCLUDE]) |
|---|
| .. | .. |
|---|
| 740 | 737 | if (type == IGMPV3_HOST_MEMBERSHIP_REPORT) |
|---|
| 741 | 738 | return igmpv3_send_report(in_dev, pmc); |
|---|
| 742 | 739 | |
|---|
| 743 | | - if (ipv4_is_local_multicast(group) && !net->ipv4.sysctl_igmp_llm_reports) |
|---|
| 740 | + if (ipv4_is_local_multicast(group) && |
|---|
| 741 | + !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) |
|---|
| 744 | 742 | return 0; |
|---|
| 745 | 743 | |
|---|
| 746 | 744 | if (type == IGMP_HOST_LEAVE_MESSAGE) |
|---|
| .. | .. |
|---|
| 822 | 820 | struct net *net = dev_net(in_dev->dev); |
|---|
| 823 | 821 | if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) |
|---|
| 824 | 822 | return; |
|---|
| 825 | | - in_dev->mr_ifc_count = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv; |
|---|
| 823 | + in_dev->mr_ifc_count = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); |
|---|
| 826 | 824 | igmp_ifc_start_timer(in_dev, 1); |
|---|
| 827 | 825 | } |
|---|
| 828 | 826 | |
|---|
| .. | .. |
|---|
| 917 | 915 | |
|---|
| 918 | 916 | if (group == IGMP_ALL_HOSTS) |
|---|
| 919 | 917 | return false; |
|---|
| 920 | | - if (ipv4_is_local_multicast(group) && !net->ipv4.sysctl_igmp_llm_reports) |
|---|
| 918 | + if (ipv4_is_local_multicast(group) && |
|---|
| 919 | + !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) |
|---|
| 921 | 920 | return false; |
|---|
| 922 | 921 | |
|---|
| 923 | 922 | rcu_read_lock(); |
|---|
| .. | .. |
|---|
| 1003 | 1002 | * received value was zero, use the default or statically |
|---|
| 1004 | 1003 | * configured value. |
|---|
| 1005 | 1004 | */ |
|---|
| 1006 | | - in_dev->mr_qrv = ih3->qrv ?: net->ipv4.sysctl_igmp_qrv; |
|---|
| 1005 | + in_dev->mr_qrv = ih3->qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); |
|---|
| 1007 | 1006 | in_dev->mr_qi = IGMPV3_QQIC(ih3->qqic)*HZ ?: IGMP_QUERY_INTERVAL; |
|---|
| 1008 | 1007 | |
|---|
| 1009 | 1008 | /* RFC3376, 8.3. Query Response Interval: |
|---|
| .. | .. |
|---|
| 1042 | 1041 | if (im->multiaddr == IGMP_ALL_HOSTS) |
|---|
| 1043 | 1042 | continue; |
|---|
| 1044 | 1043 | if (ipv4_is_local_multicast(im->multiaddr) && |
|---|
| 1045 | | - !net->ipv4.sysctl_igmp_llm_reports) |
|---|
| 1044 | + !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) |
|---|
| 1046 | 1045 | continue; |
|---|
| 1047 | 1046 | spin_lock_bh(&im->lock); |
|---|
| 1048 | 1047 | if (im->tm_running) |
|---|
| .. | .. |
|---|
| 1163 | 1162 | /* |
|---|
| 1164 | 1163 | * deleted ip_mc_list manipulation |
|---|
| 1165 | 1164 | */ |
|---|
| 1166 | | -static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im) |
|---|
| 1165 | +static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im, |
|---|
| 1166 | + gfp_t gfp) |
|---|
| 1167 | 1167 | { |
|---|
| 1168 | 1168 | struct ip_mc_list *pmc; |
|---|
| 1169 | 1169 | struct net *net = dev_net(in_dev->dev); |
|---|
| .. | .. |
|---|
| 1174 | 1174 | * for deleted items allows change reports to use common code with |
|---|
| 1175 | 1175 | * non-deleted or query-response MCA's. |
|---|
| 1176 | 1176 | */ |
|---|
| 1177 | | - pmc = kzalloc(sizeof(*pmc), GFP_KERNEL); |
|---|
| 1177 | + pmc = kzalloc(sizeof(*pmc), gfp); |
|---|
| 1178 | 1178 | if (!pmc) |
|---|
| 1179 | 1179 | return; |
|---|
| 1180 | 1180 | spin_lock_init(&pmc->lock); |
|---|
| .. | .. |
|---|
| 1182 | 1182 | pmc->interface = im->interface; |
|---|
| 1183 | 1183 | in_dev_hold(in_dev); |
|---|
| 1184 | 1184 | pmc->multiaddr = im->multiaddr; |
|---|
| 1185 | | - pmc->crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv; |
|---|
| 1185 | + pmc->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); |
|---|
| 1186 | 1186 | pmc->sfmode = im->sfmode; |
|---|
| 1187 | 1187 | if (pmc->sfmode == MCAST_INCLUDE) { |
|---|
| 1188 | 1188 | struct ip_sf_list *psf; |
|---|
| .. | .. |
|---|
| 1233 | 1233 | swap(im->tomb, pmc->tomb); |
|---|
| 1234 | 1234 | swap(im->sources, pmc->sources); |
|---|
| 1235 | 1235 | for (psf = im->sources; psf; psf = psf->sf_next) |
|---|
| 1236 | | - psf->sf_crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv; |
|---|
| 1236 | + psf->sf_crcount = in_dev->mr_qrv ?: |
|---|
| 1237 | + READ_ONCE(net->ipv4.sysctl_igmp_qrv); |
|---|
| 1237 | 1238 | } else { |
|---|
| 1238 | | - im->crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv; |
|---|
| 1239 | + im->crcount = in_dev->mr_qrv ?: |
|---|
| 1240 | + READ_ONCE(net->ipv4.sysctl_igmp_qrv); |
|---|
| 1239 | 1241 | } |
|---|
| 1240 | 1242 | in_dev_put(pmc->interface); |
|---|
| 1241 | 1243 | kfree_pmc(pmc); |
|---|
| .. | .. |
|---|
| 1276 | 1278 | } |
|---|
| 1277 | 1279 | #endif |
|---|
| 1278 | 1280 | |
|---|
| 1279 | | -static void igmp_group_dropped(struct ip_mc_list *im) |
|---|
| 1281 | +static void __igmp_group_dropped(struct ip_mc_list *im, gfp_t gfp) |
|---|
| 1280 | 1282 | { |
|---|
| 1281 | 1283 | struct in_device *in_dev = im->interface; |
|---|
| 1282 | 1284 | #ifdef CONFIG_IP_MULTICAST |
|---|
| .. | .. |
|---|
| 1292 | 1294 | #ifdef CONFIG_IP_MULTICAST |
|---|
| 1293 | 1295 | if (im->multiaddr == IGMP_ALL_HOSTS) |
|---|
| 1294 | 1296 | return; |
|---|
| 1295 | | - if (ipv4_is_local_multicast(im->multiaddr) && !net->ipv4.sysctl_igmp_llm_reports) |
|---|
| 1297 | + if (ipv4_is_local_multicast(im->multiaddr) && |
|---|
| 1298 | + !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) |
|---|
| 1296 | 1299 | return; |
|---|
| 1297 | 1300 | |
|---|
| 1298 | 1301 | reporter = im->reporter; |
|---|
| .. | .. |
|---|
| 1307 | 1310 | return; |
|---|
| 1308 | 1311 | } |
|---|
| 1309 | 1312 | /* IGMPv3 */ |
|---|
| 1310 | | - igmpv3_add_delrec(in_dev, im); |
|---|
| 1313 | + igmpv3_add_delrec(in_dev, im, gfp); |
|---|
| 1311 | 1314 | |
|---|
| 1312 | 1315 | igmp_ifc_event(in_dev); |
|---|
| 1313 | 1316 | } |
|---|
| 1314 | 1317 | #endif |
|---|
| 1318 | +} |
|---|
| 1319 | + |
|---|
| 1320 | +static void igmp_group_dropped(struct ip_mc_list *im) |
|---|
| 1321 | +{ |
|---|
| 1322 | + __igmp_group_dropped(im, GFP_KERNEL); |
|---|
| 1315 | 1323 | } |
|---|
| 1316 | 1324 | |
|---|
| 1317 | 1325 | static void igmp_group_added(struct ip_mc_list *im) |
|---|
| .. | .. |
|---|
| 1329 | 1337 | #ifdef CONFIG_IP_MULTICAST |
|---|
| 1330 | 1338 | if (im->multiaddr == IGMP_ALL_HOSTS) |
|---|
| 1331 | 1339 | return; |
|---|
| 1332 | | - if (ipv4_is_local_multicast(im->multiaddr) && !net->ipv4.sysctl_igmp_llm_reports) |
|---|
| 1340 | + if (ipv4_is_local_multicast(im->multiaddr) && |
|---|
| 1341 | + !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) |
|---|
| 1333 | 1342 | return; |
|---|
| 1334 | 1343 | |
|---|
| 1335 | 1344 | if (in_dev->dead) |
|---|
| 1336 | 1345 | return; |
|---|
| 1337 | 1346 | |
|---|
| 1338 | | - im->unsolicit_count = net->ipv4.sysctl_igmp_qrv; |
|---|
| 1347 | + im->unsolicit_count = READ_ONCE(net->ipv4.sysctl_igmp_qrv); |
|---|
| 1339 | 1348 | if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) { |
|---|
| 1340 | 1349 | spin_lock_bh(&im->lock); |
|---|
| 1341 | 1350 | igmp_start_timer(im, IGMP_INITIAL_REPORT_DELAY); |
|---|
| .. | .. |
|---|
| 1349 | 1358 | * IN() to IN(A). |
|---|
| 1350 | 1359 | */ |
|---|
| 1351 | 1360 | if (im->sfmode == MCAST_EXCLUDE) |
|---|
| 1352 | | - im->crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv; |
|---|
| 1361 | + im->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); |
|---|
| 1353 | 1362 | |
|---|
| 1354 | 1363 | igmp_ifc_event(in_dev); |
|---|
| 1355 | 1364 | #endif |
|---|
| .. | .. |
|---|
| 1415 | 1424 | /* |
|---|
| 1416 | 1425 | * A socket has joined a multicast group on device dev. |
|---|
| 1417 | 1426 | */ |
|---|
| 1418 | | -static void __ip_mc_inc_group(struct in_device *in_dev, __be32 addr, |
|---|
| 1419 | | - unsigned int mode) |
|---|
| 1427 | +static void ____ip_mc_inc_group(struct in_device *in_dev, __be32 addr, |
|---|
| 1428 | + unsigned int mode, gfp_t gfp) |
|---|
| 1420 | 1429 | { |
|---|
| 1421 | 1430 | struct ip_mc_list *im; |
|---|
| 1422 | 1431 | |
|---|
| .. | .. |
|---|
| 1430 | 1439 | } |
|---|
| 1431 | 1440 | } |
|---|
| 1432 | 1441 | |
|---|
| 1433 | | - im = kzalloc(sizeof(*im), GFP_KERNEL); |
|---|
| 1442 | + im = kzalloc(sizeof(*im), gfp); |
|---|
| 1434 | 1443 | if (!im) |
|---|
| 1435 | 1444 | goto out; |
|---|
| 1436 | 1445 | |
|---|
| .. | .. |
|---|
| 1463 | 1472 | return; |
|---|
| 1464 | 1473 | } |
|---|
| 1465 | 1474 | |
|---|
| 1475 | +void __ip_mc_inc_group(struct in_device *in_dev, __be32 addr, gfp_t gfp) |
|---|
| 1476 | +{ |
|---|
| 1477 | + ____ip_mc_inc_group(in_dev, addr, MCAST_EXCLUDE, gfp); |
|---|
| 1478 | +} |
|---|
| 1479 | +EXPORT_SYMBOL(__ip_mc_inc_group); |
|---|
| 1480 | + |
|---|
| 1466 | 1481 | void ip_mc_inc_group(struct in_device *in_dev, __be32 addr) |
|---|
| 1467 | 1482 | { |
|---|
| 1468 | | - __ip_mc_inc_group(in_dev, addr, MCAST_EXCLUDE); |
|---|
| 1483 | + __ip_mc_inc_group(in_dev, addr, GFP_KERNEL); |
|---|
| 1469 | 1484 | } |
|---|
| 1470 | 1485 | EXPORT_SYMBOL(ip_mc_inc_group); |
|---|
| 1471 | 1486 | |
|---|
| .. | .. |
|---|
| 1508 | 1523 | |
|---|
| 1509 | 1524 | len += sizeof(struct igmpv3_report); |
|---|
| 1510 | 1525 | |
|---|
| 1511 | | - return pskb_may_pull(skb, len) ? 0 : -EINVAL; |
|---|
| 1526 | + return ip_mc_may_pull(skb, len) ? 0 : -EINVAL; |
|---|
| 1512 | 1527 | } |
|---|
| 1513 | 1528 | |
|---|
| 1514 | 1529 | static int ip_mc_check_igmp_query(struct sk_buff *skb) |
|---|
| 1515 | 1530 | { |
|---|
| 1516 | | - unsigned int len = skb_transport_offset(skb); |
|---|
| 1517 | | - |
|---|
| 1518 | | - len += sizeof(struct igmphdr); |
|---|
| 1519 | | - if (skb->len < len) |
|---|
| 1520 | | - return -EINVAL; |
|---|
| 1531 | + unsigned int transport_len = ip_transport_len(skb); |
|---|
| 1532 | + unsigned int len; |
|---|
| 1521 | 1533 | |
|---|
| 1522 | 1534 | /* IGMPv{1,2}? */ |
|---|
| 1523 | | - if (skb->len != len) { |
|---|
| 1535 | + if (transport_len != sizeof(struct igmphdr)) { |
|---|
| 1524 | 1536 | /* or IGMPv3? */ |
|---|
| 1525 | | - len += sizeof(struct igmpv3_query) - sizeof(struct igmphdr); |
|---|
| 1526 | | - if (skb->len < len || !pskb_may_pull(skb, len)) |
|---|
| 1537 | + if (transport_len < sizeof(struct igmpv3_query)) |
|---|
| 1538 | + return -EINVAL; |
|---|
| 1539 | + |
|---|
| 1540 | + len = skb_transport_offset(skb) + sizeof(struct igmpv3_query); |
|---|
| 1541 | + if (!ip_mc_may_pull(skb, len)) |
|---|
| 1527 | 1542 | return -EINVAL; |
|---|
| 1528 | 1543 | } |
|---|
| 1529 | 1544 | |
|---|
| .. | .. |
|---|
| 1543 | 1558 | case IGMP_HOST_LEAVE_MESSAGE: |
|---|
| 1544 | 1559 | case IGMP_HOST_MEMBERSHIP_REPORT: |
|---|
| 1545 | 1560 | case IGMPV2_HOST_MEMBERSHIP_REPORT: |
|---|
| 1546 | | - /* fall through */ |
|---|
| 1547 | 1561 | return 0; |
|---|
| 1548 | 1562 | case IGMPV3_HOST_MEMBERSHIP_REPORT: |
|---|
| 1549 | 1563 | return ip_mc_check_igmp_reportv3(skb); |
|---|
| .. | .. |
|---|
| 1554 | 1568 | } |
|---|
| 1555 | 1569 | } |
|---|
| 1556 | 1570 | |
|---|
| 1557 | | -static inline __sum16 ip_mc_validate_checksum(struct sk_buff *skb) |
|---|
| 1571 | +static __sum16 ip_mc_validate_checksum(struct sk_buff *skb) |
|---|
| 1558 | 1572 | { |
|---|
| 1559 | 1573 | return skb_checksum_simple_validate(skb); |
|---|
| 1560 | 1574 | } |
|---|
| 1561 | 1575 | |
|---|
| 1562 | | -static int __ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed) |
|---|
| 1563 | | - |
|---|
| 1576 | +static int ip_mc_check_igmp_csum(struct sk_buff *skb) |
|---|
| 1564 | 1577 | { |
|---|
| 1565 | | - struct sk_buff *skb_chk; |
|---|
| 1566 | | - unsigned int transport_len; |
|---|
| 1567 | 1578 | unsigned int len = skb_transport_offset(skb) + sizeof(struct igmphdr); |
|---|
| 1568 | | - int ret = -EINVAL; |
|---|
| 1579 | + unsigned int transport_len = ip_transport_len(skb); |
|---|
| 1580 | + struct sk_buff *skb_chk; |
|---|
| 1569 | 1581 | |
|---|
| 1570 | | - transport_len = ntohs(ip_hdr(skb)->tot_len) - ip_hdrlen(skb); |
|---|
| 1582 | + if (!ip_mc_may_pull(skb, len)) |
|---|
| 1583 | + return -EINVAL; |
|---|
| 1571 | 1584 | |
|---|
| 1572 | 1585 | skb_chk = skb_checksum_trimmed(skb, transport_len, |
|---|
| 1573 | 1586 | ip_mc_validate_checksum); |
|---|
| 1574 | 1587 | if (!skb_chk) |
|---|
| 1575 | | - goto err; |
|---|
| 1588 | + return -EINVAL; |
|---|
| 1576 | 1589 | |
|---|
| 1577 | | - if (!pskb_may_pull(skb_chk, len)) |
|---|
| 1578 | | - goto err; |
|---|
| 1579 | | - |
|---|
| 1580 | | - ret = ip_mc_check_igmp_msg(skb_chk); |
|---|
| 1581 | | - if (ret) |
|---|
| 1582 | | - goto err; |
|---|
| 1583 | | - |
|---|
| 1584 | | - if (skb_trimmed) |
|---|
| 1585 | | - *skb_trimmed = skb_chk; |
|---|
| 1586 | | - /* free now unneeded clone */ |
|---|
| 1587 | | - else if (skb_chk != skb) |
|---|
| 1590 | + if (skb_chk != skb) |
|---|
| 1588 | 1591 | kfree_skb(skb_chk); |
|---|
| 1589 | 1592 | |
|---|
| 1590 | | - ret = 0; |
|---|
| 1591 | | - |
|---|
| 1592 | | -err: |
|---|
| 1593 | | - if (ret && skb_chk && skb_chk != skb) |
|---|
| 1594 | | - kfree_skb(skb_chk); |
|---|
| 1595 | | - |
|---|
| 1596 | | - return ret; |
|---|
| 1593 | + return 0; |
|---|
| 1597 | 1594 | } |
|---|
| 1598 | 1595 | |
|---|
| 1599 | 1596 | /** |
|---|
| 1600 | 1597 | * ip_mc_check_igmp - checks whether this is a sane IGMP packet |
|---|
| 1601 | 1598 | * @skb: the skb to validate |
|---|
| 1602 | | - * @skb_trimmed: to store an skb pointer trimmed to IPv4 packet tail (optional) |
|---|
| 1603 | 1599 | * |
|---|
| 1604 | 1600 | * Checks whether an IPv4 packet is a valid IGMP packet. If so sets |
|---|
| 1605 | 1601 | * skb transport header accordingly and returns zero. |
|---|
| .. | .. |
|---|
| 1609 | 1605 | * -ENOMSG: IP header validation succeeded but it is not an IGMP packet. |
|---|
| 1610 | 1606 | * -ENOMEM: A memory allocation failure happened. |
|---|
| 1611 | 1607 | * |
|---|
| 1612 | | - * Optionally, an skb pointer might be provided via skb_trimmed (or set it |
|---|
| 1613 | | - * to NULL): After parsing an IGMP packet successfully it will point to |
|---|
| 1614 | | - * an skb which has its tail aligned to the IP packet end. This might |
|---|
| 1615 | | - * either be the originally provided skb or a trimmed, cloned version if |
|---|
| 1616 | | - * the skb frame had data beyond the IP packet. A cloned skb allows us |
|---|
| 1617 | | - * to leave the original skb and its full frame unchanged (which might be |
|---|
| 1618 | | - * desirable for layer 2 frame jugglers). |
|---|
| 1619 | | - * |
|---|
| 1620 | 1608 | * Caller needs to set the skb network header and free any returned skb if it |
|---|
| 1621 | 1609 | * differs from the provided skb. |
|---|
| 1622 | 1610 | */ |
|---|
| 1623 | | -int ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed) |
|---|
| 1611 | +int ip_mc_check_igmp(struct sk_buff *skb) |
|---|
| 1624 | 1612 | { |
|---|
| 1625 | 1613 | int ret = ip_mc_check_iphdr(skb); |
|---|
| 1626 | 1614 | |
|---|
| .. | .. |
|---|
| 1630 | 1618 | if (ip_hdr(skb)->protocol != IPPROTO_IGMP) |
|---|
| 1631 | 1619 | return -ENOMSG; |
|---|
| 1632 | 1620 | |
|---|
| 1633 | | - return __ip_mc_check_igmp(skb, skb_trimmed); |
|---|
| 1621 | + ret = ip_mc_check_igmp_csum(skb); |
|---|
| 1622 | + if (ret < 0) |
|---|
| 1623 | + return ret; |
|---|
| 1624 | + |
|---|
| 1625 | + return ip_mc_check_igmp_msg(skb); |
|---|
| 1634 | 1626 | } |
|---|
| 1635 | 1627 | EXPORT_SYMBOL(ip_mc_check_igmp); |
|---|
| 1636 | 1628 | |
|---|
| .. | .. |
|---|
| 1650 | 1642 | if (im->multiaddr == IGMP_ALL_HOSTS) |
|---|
| 1651 | 1643 | continue; |
|---|
| 1652 | 1644 | if (ipv4_is_local_multicast(im->multiaddr) && |
|---|
| 1653 | | - !net->ipv4.sysctl_igmp_llm_reports) |
|---|
| 1645 | + !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) |
|---|
| 1654 | 1646 | continue; |
|---|
| 1655 | 1647 | |
|---|
| 1656 | 1648 | /* a failover is happening and switches |
|---|
| .. | .. |
|---|
| 1671 | 1663 | * A socket has left a multicast group on device dev |
|---|
| 1672 | 1664 | */ |
|---|
| 1673 | 1665 | |
|---|
| 1674 | | -void ip_mc_dec_group(struct in_device *in_dev, __be32 addr) |
|---|
| 1666 | +void __ip_mc_dec_group(struct in_device *in_dev, __be32 addr, gfp_t gfp) |
|---|
| 1675 | 1667 | { |
|---|
| 1676 | 1668 | struct ip_mc_list *i; |
|---|
| 1677 | 1669 | struct ip_mc_list __rcu **ip; |
|---|
| .. | .. |
|---|
| 1686 | 1678 | ip_mc_hash_remove(in_dev, i); |
|---|
| 1687 | 1679 | *ip = i->next_rcu; |
|---|
| 1688 | 1680 | in_dev->mc_count--; |
|---|
| 1689 | | - igmp_group_dropped(i); |
|---|
| 1681 | + __igmp_group_dropped(i, gfp); |
|---|
| 1690 | 1682 | ip_mc_clear_src(i); |
|---|
| 1691 | 1683 | |
|---|
| 1692 | 1684 | if (!in_dev->dead) |
|---|
| .. | .. |
|---|
| 1699 | 1691 | } |
|---|
| 1700 | 1692 | } |
|---|
| 1701 | 1693 | } |
|---|
| 1702 | | -EXPORT_SYMBOL(ip_mc_dec_group); |
|---|
| 1694 | +EXPORT_SYMBOL(__ip_mc_dec_group); |
|---|
| 1703 | 1695 | |
|---|
| 1704 | 1696 | /* Device changing type */ |
|---|
| 1705 | 1697 | |
|---|
| .. | .. |
|---|
| 1757 | 1749 | |
|---|
| 1758 | 1750 | in_dev->mr_qi = IGMP_QUERY_INTERVAL; |
|---|
| 1759 | 1751 | in_dev->mr_qri = IGMP_QUERY_RESPONSE_INTERVAL; |
|---|
| 1760 | | - in_dev->mr_qrv = net->ipv4.sysctl_igmp_qrv; |
|---|
| 1752 | + in_dev->mr_qrv = READ_ONCE(net->ipv4.sysctl_igmp_qrv); |
|---|
| 1761 | 1753 | } |
|---|
| 1762 | 1754 | #else |
|---|
| 1763 | 1755 | static void ip_mc_reset(struct in_device *in_dev) |
|---|
| .. | .. |
|---|
| 1891 | 1883 | #ifdef CONFIG_IP_MULTICAST |
|---|
| 1892 | 1884 | if (psf->sf_oldin && |
|---|
| 1893 | 1885 | !IGMP_V1_SEEN(in_dev) && !IGMP_V2_SEEN(in_dev)) { |
|---|
| 1894 | | - psf->sf_crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv; |
|---|
| 1886 | + psf->sf_crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); |
|---|
| 1895 | 1887 | psf->sf_next = pmc->tomb; |
|---|
| 1896 | 1888 | pmc->tomb = psf; |
|---|
| 1897 | 1889 | rv = 1; |
|---|
| .. | .. |
|---|
| 1955 | 1947 | /* filter mode change */ |
|---|
| 1956 | 1948 | pmc->sfmode = MCAST_INCLUDE; |
|---|
| 1957 | 1949 | #ifdef CONFIG_IP_MULTICAST |
|---|
| 1958 | | - pmc->crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv; |
|---|
| 1950 | + pmc->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); |
|---|
| 1959 | 1951 | in_dev->mr_ifc_count = pmc->crcount; |
|---|
| 1960 | 1952 | for (psf = pmc->sources; psf; psf = psf->sf_next) |
|---|
| 1961 | 1953 | psf->sf_crcount = 0; |
|---|
| .. | .. |
|---|
| 2134 | 2126 | #ifdef CONFIG_IP_MULTICAST |
|---|
| 2135 | 2127 | /* else no filters; keep old mode for reports */ |
|---|
| 2136 | 2128 | |
|---|
| 2137 | | - pmc->crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv; |
|---|
| 2129 | + pmc->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); |
|---|
| 2138 | 2130 | in_dev->mr_ifc_count = pmc->crcount; |
|---|
| 2139 | 2131 | for (psf = pmc->sources; psf; psf = psf->sf_next) |
|---|
| 2140 | 2132 | psf->sf_crcount = 0; |
|---|
| .. | .. |
|---|
| 2200 | 2192 | count++; |
|---|
| 2201 | 2193 | } |
|---|
| 2202 | 2194 | err = -ENOBUFS; |
|---|
| 2203 | | - if (count >= net->ipv4.sysctl_igmp_max_memberships) |
|---|
| 2195 | + if (count >= READ_ONCE(net->ipv4.sysctl_igmp_max_memberships)) |
|---|
| 2204 | 2196 | goto done; |
|---|
| 2205 | 2197 | iml = sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL); |
|---|
| 2206 | 2198 | if (!iml) |
|---|
| .. | .. |
|---|
| 2211 | 2203 | iml->sflist = NULL; |
|---|
| 2212 | 2204 | iml->sfmode = mode; |
|---|
| 2213 | 2205 | rcu_assign_pointer(inet->mc_list, iml); |
|---|
| 2214 | | - __ip_mc_inc_group(in_dev, addr, mode); |
|---|
| 2206 | + ____ip_mc_inc_group(in_dev, addr, mode, GFP_KERNEL); |
|---|
| 2215 | 2207 | err = 0; |
|---|
| 2216 | 2208 | done: |
|---|
| 2217 | 2209 | return err; |
|---|
| .. | .. |
|---|
| 2387 | 2379 | } |
|---|
| 2388 | 2380 | /* else, add a new source to the filter */ |
|---|
| 2389 | 2381 | |
|---|
| 2390 | | - if (psl && psl->sl_count >= net->ipv4.sysctl_igmp_max_msf) { |
|---|
| 2382 | + if (psl && psl->sl_count >= READ_ONCE(net->ipv4.sysctl_igmp_max_msf)) { |
|---|
| 2391 | 2383 | err = -ENOBUFS; |
|---|
| 2392 | 2384 | goto done; |
|---|
| 2393 | 2385 | } |
|---|
| .. | .. |
|---|
| 2409 | 2401 | newpsl->sl_addr[i] = psl->sl_addr[i]; |
|---|
| 2410 | 2402 | /* decrease mem now to avoid the memleak warning */ |
|---|
| 2411 | 2403 | atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc); |
|---|
| 2412 | | - kfree_rcu(psl, rcu); |
|---|
| 2413 | 2404 | } |
|---|
| 2414 | 2405 | rcu_assign_pointer(pmc->sflist, newpsl); |
|---|
| 2406 | + if (psl) |
|---|
| 2407 | + kfree_rcu(psl, rcu); |
|---|
| 2415 | 2408 | psl = newpsl; |
|---|
| 2416 | 2409 | } |
|---|
| 2417 | 2410 | rv = 1; /* > 0 for insert logic below if sl_count is 0 */ |
|---|
| .. | .. |
|---|
| 2509 | 2502 | psl->sl_count, psl->sl_addr, 0); |
|---|
| 2510 | 2503 | /* decrease mem now to avoid the memleak warning */ |
|---|
| 2511 | 2504 | atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc); |
|---|
| 2512 | | - kfree_rcu(psl, rcu); |
|---|
| 2513 | | - } else |
|---|
| 2505 | + } else { |
|---|
| 2514 | 2506 | (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode, |
|---|
| 2515 | 2507 | 0, NULL, 0); |
|---|
| 2508 | + } |
|---|
| 2516 | 2509 | rcu_assign_pointer(pmc->sflist, newpsl); |
|---|
| 2510 | + if (psl) |
|---|
| 2511 | + kfree_rcu(psl, rcu); |
|---|
| 2517 | 2512 | pmc->sfmode = msf->imsf_fmode; |
|---|
| 2518 | 2513 | err = 0; |
|---|
| 2519 | 2514 | done: |
|---|
| .. | .. |
|---|
| 2581 | 2576 | } |
|---|
| 2582 | 2577 | |
|---|
| 2583 | 2578 | int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf, |
|---|
| 2584 | | - struct group_filter __user *optval, int __user *optlen) |
|---|
| 2579 | + struct sockaddr_storage __user *p) |
|---|
| 2585 | 2580 | { |
|---|
| 2586 | | - int err, i, count, copycount; |
|---|
| 2581 | + int i, count, copycount; |
|---|
| 2587 | 2582 | struct sockaddr_in *psin; |
|---|
| 2588 | 2583 | __be32 addr; |
|---|
| 2589 | 2584 | struct ip_mc_socklist *pmc; |
|---|
| .. | .. |
|---|
| 2599 | 2594 | if (!ipv4_is_multicast(addr)) |
|---|
| 2600 | 2595 | return -EINVAL; |
|---|
| 2601 | 2596 | |
|---|
| 2602 | | - err = -EADDRNOTAVAIL; |
|---|
| 2603 | | - |
|---|
| 2604 | 2597 | for_each_pmc_rtnl(inet, pmc) { |
|---|
| 2605 | 2598 | if (pmc->multi.imr_multiaddr.s_addr == addr && |
|---|
| 2606 | 2599 | pmc->multi.imr_ifindex == gsf->gf_interface) |
|---|
| 2607 | 2600 | break; |
|---|
| 2608 | 2601 | } |
|---|
| 2609 | 2602 | if (!pmc) /* must have a prior join */ |
|---|
| 2610 | | - goto done; |
|---|
| 2603 | + return -EADDRNOTAVAIL; |
|---|
| 2611 | 2604 | gsf->gf_fmode = pmc->sfmode; |
|---|
| 2612 | 2605 | psl = rtnl_dereference(pmc->sflist); |
|---|
| 2613 | 2606 | count = psl ? psl->sl_count : 0; |
|---|
| 2614 | 2607 | copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc; |
|---|
| 2615 | 2608 | gsf->gf_numsrc = count; |
|---|
| 2616 | | - if (put_user(GROUP_FILTER_SIZE(copycount), optlen) || |
|---|
| 2617 | | - copy_to_user(optval, gsf, GROUP_FILTER_SIZE(0))) { |
|---|
| 2618 | | - return -EFAULT; |
|---|
| 2619 | | - } |
|---|
| 2620 | | - for (i = 0; i < copycount; i++) { |
|---|
| 2609 | + for (i = 0; i < copycount; i++, p++) { |
|---|
| 2621 | 2610 | struct sockaddr_storage ss; |
|---|
| 2622 | 2611 | |
|---|
| 2623 | 2612 | psin = (struct sockaddr_in *)&ss; |
|---|
| 2624 | 2613 | memset(&ss, 0, sizeof(ss)); |
|---|
| 2625 | 2614 | psin->sin_family = AF_INET; |
|---|
| 2626 | 2615 | psin->sin_addr.s_addr = psl->sl_addr[i]; |
|---|
| 2627 | | - if (copy_to_user(&optval->gf_slist[i], &ss, sizeof(ss))) |
|---|
| 2616 | + if (copy_to_user(p, &ss, sizeof(ss))) |
|---|
| 2628 | 2617 | return -EFAULT; |
|---|
| 2629 | 2618 | } |
|---|
| 2630 | 2619 | return 0; |
|---|
| 2631 | | -done: |
|---|
| 2632 | | - return err; |
|---|
| 2633 | 2620 | } |
|---|
| 2634 | 2621 | |
|---|
| 2635 | 2622 | /* |
|---|