.. | .. |
---|
| 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 | } |
---|
.. | .. |
---|
357 | 353 | struct flowi4 fl4; |
---|
358 | 354 | int hlen = LL_RESERVED_SPACE(dev); |
---|
359 | 355 | int tlen = dev->needed_tailroom; |
---|
360 | | - unsigned int size = mtu; |
---|
| 356 | + unsigned int size; |
---|
361 | 357 | |
---|
| 358 | + size = min(mtu, IP_MAX_MTU); |
---|
362 | 359 | while (1) { |
---|
363 | 360 | skb = alloc_skb(size + hlen + tlen, |
---|
364 | 361 | GFP_ATOMIC | __GFP_NOWARN); |
---|
.. | .. |
---|
471 | 468 | |
---|
472 | 469 | if (pmc->multiaddr == IGMP_ALL_HOSTS) |
---|
473 | 470 | return skb; |
---|
474 | | - if (ipv4_is_local_multicast(pmc->multiaddr) && !net->ipv4.sysctl_igmp_llm_reports) |
---|
| 471 | + if (ipv4_is_local_multicast(pmc->multiaddr) && |
---|
| 472 | + !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) |
---|
475 | 473 | return skb; |
---|
476 | 474 | |
---|
477 | 475 | mtu = READ_ONCE(dev->mtu); |
---|
.. | .. |
---|
597 | 595 | if (pmc->multiaddr == IGMP_ALL_HOSTS) |
---|
598 | 596 | continue; |
---|
599 | 597 | if (ipv4_is_local_multicast(pmc->multiaddr) && |
---|
600 | | - !net->ipv4.sysctl_igmp_llm_reports) |
---|
| 598 | + !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) |
---|
601 | 599 | continue; |
---|
602 | 600 | spin_lock_bh(&pmc->lock); |
---|
603 | 601 | if (pmc->sfcount[MCAST_EXCLUDE]) |
---|
.. | .. |
---|
740 | 738 | if (type == IGMPV3_HOST_MEMBERSHIP_REPORT) |
---|
741 | 739 | return igmpv3_send_report(in_dev, pmc); |
---|
742 | 740 | |
---|
743 | | - if (ipv4_is_local_multicast(group) && !net->ipv4.sysctl_igmp_llm_reports) |
---|
| 741 | + if (ipv4_is_local_multicast(group) && |
---|
| 742 | + !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) |
---|
744 | 743 | return 0; |
---|
745 | 744 | |
---|
746 | 745 | if (type == IGMP_HOST_LEAVE_MESSAGE) |
---|
.. | .. |
---|
822 | 821 | struct net *net = dev_net(in_dev->dev); |
---|
823 | 822 | if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) |
---|
824 | 823 | return; |
---|
825 | | - in_dev->mr_ifc_count = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv; |
---|
| 824 | + in_dev->mr_ifc_count = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); |
---|
826 | 825 | igmp_ifc_start_timer(in_dev, 1); |
---|
827 | 826 | } |
---|
828 | 827 | |
---|
.. | .. |
---|
917 | 916 | |
---|
918 | 917 | if (group == IGMP_ALL_HOSTS) |
---|
919 | 918 | return false; |
---|
920 | | - if (ipv4_is_local_multicast(group) && !net->ipv4.sysctl_igmp_llm_reports) |
---|
| 919 | + if (ipv4_is_local_multicast(group) && |
---|
| 920 | + !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) |
---|
921 | 921 | return false; |
---|
922 | 922 | |
---|
923 | 923 | rcu_read_lock(); |
---|
.. | .. |
---|
1003 | 1003 | * received value was zero, use the default or statically |
---|
1004 | 1004 | * configured value. |
---|
1005 | 1005 | */ |
---|
1006 | | - in_dev->mr_qrv = ih3->qrv ?: net->ipv4.sysctl_igmp_qrv; |
---|
| 1006 | + in_dev->mr_qrv = ih3->qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); |
---|
1007 | 1007 | in_dev->mr_qi = IGMPV3_QQIC(ih3->qqic)*HZ ?: IGMP_QUERY_INTERVAL; |
---|
1008 | 1008 | |
---|
1009 | 1009 | /* RFC3376, 8.3. Query Response Interval: |
---|
.. | .. |
---|
1042 | 1042 | if (im->multiaddr == IGMP_ALL_HOSTS) |
---|
1043 | 1043 | continue; |
---|
1044 | 1044 | if (ipv4_is_local_multicast(im->multiaddr) && |
---|
1045 | | - !net->ipv4.sysctl_igmp_llm_reports) |
---|
| 1045 | + !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) |
---|
1046 | 1046 | continue; |
---|
1047 | 1047 | spin_lock_bh(&im->lock); |
---|
1048 | 1048 | if (im->tm_running) |
---|
.. | .. |
---|
1163 | 1163 | /* |
---|
1164 | 1164 | * deleted ip_mc_list manipulation |
---|
1165 | 1165 | */ |
---|
1166 | | -static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im) |
---|
| 1166 | +static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im, |
---|
| 1167 | + gfp_t gfp) |
---|
1167 | 1168 | { |
---|
1168 | 1169 | struct ip_mc_list *pmc; |
---|
1169 | 1170 | struct net *net = dev_net(in_dev->dev); |
---|
.. | .. |
---|
1174 | 1175 | * for deleted items allows change reports to use common code with |
---|
1175 | 1176 | * non-deleted or query-response MCA's. |
---|
1176 | 1177 | */ |
---|
1177 | | - pmc = kzalloc(sizeof(*pmc), GFP_KERNEL); |
---|
| 1178 | + pmc = kzalloc(sizeof(*pmc), gfp); |
---|
1178 | 1179 | if (!pmc) |
---|
1179 | 1180 | return; |
---|
1180 | 1181 | spin_lock_init(&pmc->lock); |
---|
.. | .. |
---|
1182 | 1183 | pmc->interface = im->interface; |
---|
1183 | 1184 | in_dev_hold(in_dev); |
---|
1184 | 1185 | pmc->multiaddr = im->multiaddr; |
---|
1185 | | - pmc->crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv; |
---|
| 1186 | + pmc->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); |
---|
1186 | 1187 | pmc->sfmode = im->sfmode; |
---|
1187 | 1188 | if (pmc->sfmode == MCAST_INCLUDE) { |
---|
1188 | 1189 | struct ip_sf_list *psf; |
---|
.. | .. |
---|
1233 | 1234 | swap(im->tomb, pmc->tomb); |
---|
1234 | 1235 | swap(im->sources, pmc->sources); |
---|
1235 | 1236 | for (psf = im->sources; psf; psf = psf->sf_next) |
---|
1236 | | - psf->sf_crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv; |
---|
| 1237 | + psf->sf_crcount = in_dev->mr_qrv ?: |
---|
| 1238 | + READ_ONCE(net->ipv4.sysctl_igmp_qrv); |
---|
1237 | 1239 | } else { |
---|
1238 | | - im->crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv; |
---|
| 1240 | + im->crcount = in_dev->mr_qrv ?: |
---|
| 1241 | + READ_ONCE(net->ipv4.sysctl_igmp_qrv); |
---|
1239 | 1242 | } |
---|
1240 | 1243 | in_dev_put(pmc->interface); |
---|
1241 | 1244 | kfree_pmc(pmc); |
---|
.. | .. |
---|
1276 | 1279 | } |
---|
1277 | 1280 | #endif |
---|
1278 | 1281 | |
---|
1279 | | -static void igmp_group_dropped(struct ip_mc_list *im) |
---|
| 1282 | +static void __igmp_group_dropped(struct ip_mc_list *im, gfp_t gfp) |
---|
1280 | 1283 | { |
---|
1281 | 1284 | struct in_device *in_dev = im->interface; |
---|
1282 | 1285 | #ifdef CONFIG_IP_MULTICAST |
---|
.. | .. |
---|
1292 | 1295 | #ifdef CONFIG_IP_MULTICAST |
---|
1293 | 1296 | if (im->multiaddr == IGMP_ALL_HOSTS) |
---|
1294 | 1297 | return; |
---|
1295 | | - if (ipv4_is_local_multicast(im->multiaddr) && !net->ipv4.sysctl_igmp_llm_reports) |
---|
| 1298 | + if (ipv4_is_local_multicast(im->multiaddr) && |
---|
| 1299 | + !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) |
---|
1296 | 1300 | return; |
---|
1297 | 1301 | |
---|
1298 | 1302 | reporter = im->reporter; |
---|
.. | .. |
---|
1307 | 1311 | return; |
---|
1308 | 1312 | } |
---|
1309 | 1313 | /* IGMPv3 */ |
---|
1310 | | - igmpv3_add_delrec(in_dev, im); |
---|
| 1314 | + igmpv3_add_delrec(in_dev, im, gfp); |
---|
1311 | 1315 | |
---|
1312 | 1316 | igmp_ifc_event(in_dev); |
---|
1313 | 1317 | } |
---|
1314 | 1318 | #endif |
---|
| 1319 | +} |
---|
| 1320 | + |
---|
| 1321 | +static void igmp_group_dropped(struct ip_mc_list *im) |
---|
| 1322 | +{ |
---|
| 1323 | + __igmp_group_dropped(im, GFP_KERNEL); |
---|
1315 | 1324 | } |
---|
1316 | 1325 | |
---|
1317 | 1326 | static void igmp_group_added(struct ip_mc_list *im) |
---|
.. | .. |
---|
1329 | 1338 | #ifdef CONFIG_IP_MULTICAST |
---|
1330 | 1339 | if (im->multiaddr == IGMP_ALL_HOSTS) |
---|
1331 | 1340 | return; |
---|
1332 | | - if (ipv4_is_local_multicast(im->multiaddr) && !net->ipv4.sysctl_igmp_llm_reports) |
---|
| 1341 | + if (ipv4_is_local_multicast(im->multiaddr) && |
---|
| 1342 | + !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) |
---|
1333 | 1343 | return; |
---|
1334 | 1344 | |
---|
1335 | 1345 | if (in_dev->dead) |
---|
1336 | 1346 | return; |
---|
1337 | 1347 | |
---|
1338 | | - im->unsolicit_count = net->ipv4.sysctl_igmp_qrv; |
---|
| 1348 | + im->unsolicit_count = READ_ONCE(net->ipv4.sysctl_igmp_qrv); |
---|
1339 | 1349 | if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) { |
---|
1340 | 1350 | spin_lock_bh(&im->lock); |
---|
1341 | 1351 | igmp_start_timer(im, IGMP_INITIAL_REPORT_DELAY); |
---|
.. | .. |
---|
1349 | 1359 | * IN() to IN(A). |
---|
1350 | 1360 | */ |
---|
1351 | 1361 | if (im->sfmode == MCAST_EXCLUDE) |
---|
1352 | | - im->crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv; |
---|
| 1362 | + im->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); |
---|
1353 | 1363 | |
---|
1354 | 1364 | igmp_ifc_event(in_dev); |
---|
1355 | 1365 | #endif |
---|
.. | .. |
---|
1415 | 1425 | /* |
---|
1416 | 1426 | * A socket has joined a multicast group on device dev. |
---|
1417 | 1427 | */ |
---|
1418 | | -static void __ip_mc_inc_group(struct in_device *in_dev, __be32 addr, |
---|
1419 | | - unsigned int mode) |
---|
| 1428 | +static void ____ip_mc_inc_group(struct in_device *in_dev, __be32 addr, |
---|
| 1429 | + unsigned int mode, gfp_t gfp) |
---|
1420 | 1430 | { |
---|
1421 | 1431 | struct ip_mc_list *im; |
---|
1422 | 1432 | |
---|
.. | .. |
---|
1430 | 1440 | } |
---|
1431 | 1441 | } |
---|
1432 | 1442 | |
---|
1433 | | - im = kzalloc(sizeof(*im), GFP_KERNEL); |
---|
| 1443 | + im = kzalloc(sizeof(*im), gfp); |
---|
1434 | 1444 | if (!im) |
---|
1435 | 1445 | goto out; |
---|
1436 | 1446 | |
---|
.. | .. |
---|
1463 | 1473 | return; |
---|
1464 | 1474 | } |
---|
1465 | 1475 | |
---|
| 1476 | +void __ip_mc_inc_group(struct in_device *in_dev, __be32 addr, gfp_t gfp) |
---|
| 1477 | +{ |
---|
| 1478 | + ____ip_mc_inc_group(in_dev, addr, MCAST_EXCLUDE, gfp); |
---|
| 1479 | +} |
---|
| 1480 | +EXPORT_SYMBOL(__ip_mc_inc_group); |
---|
| 1481 | + |
---|
1466 | 1482 | void ip_mc_inc_group(struct in_device *in_dev, __be32 addr) |
---|
1467 | 1483 | { |
---|
1468 | | - __ip_mc_inc_group(in_dev, addr, MCAST_EXCLUDE); |
---|
| 1484 | + __ip_mc_inc_group(in_dev, addr, GFP_KERNEL); |
---|
1469 | 1485 | } |
---|
1470 | 1486 | EXPORT_SYMBOL(ip_mc_inc_group); |
---|
1471 | 1487 | |
---|
.. | .. |
---|
1508 | 1524 | |
---|
1509 | 1525 | len += sizeof(struct igmpv3_report); |
---|
1510 | 1526 | |
---|
1511 | | - return pskb_may_pull(skb, len) ? 0 : -EINVAL; |
---|
| 1527 | + return ip_mc_may_pull(skb, len) ? 0 : -EINVAL; |
---|
1512 | 1528 | } |
---|
1513 | 1529 | |
---|
1514 | 1530 | static int ip_mc_check_igmp_query(struct sk_buff *skb) |
---|
1515 | 1531 | { |
---|
1516 | | - unsigned int len = skb_transport_offset(skb); |
---|
1517 | | - |
---|
1518 | | - len += sizeof(struct igmphdr); |
---|
1519 | | - if (skb->len < len) |
---|
1520 | | - return -EINVAL; |
---|
| 1532 | + unsigned int transport_len = ip_transport_len(skb); |
---|
| 1533 | + unsigned int len; |
---|
1521 | 1534 | |
---|
1522 | 1535 | /* IGMPv{1,2}? */ |
---|
1523 | | - if (skb->len != len) { |
---|
| 1536 | + if (transport_len != sizeof(struct igmphdr)) { |
---|
1524 | 1537 | /* or IGMPv3? */ |
---|
1525 | | - len += sizeof(struct igmpv3_query) - sizeof(struct igmphdr); |
---|
1526 | | - if (skb->len < len || !pskb_may_pull(skb, len)) |
---|
| 1538 | + if (transport_len < sizeof(struct igmpv3_query)) |
---|
| 1539 | + return -EINVAL; |
---|
| 1540 | + |
---|
| 1541 | + len = skb_transport_offset(skb) + sizeof(struct igmpv3_query); |
---|
| 1542 | + if (!ip_mc_may_pull(skb, len)) |
---|
1527 | 1543 | return -EINVAL; |
---|
1528 | 1544 | } |
---|
1529 | 1545 | |
---|
.. | .. |
---|
1543 | 1559 | case IGMP_HOST_LEAVE_MESSAGE: |
---|
1544 | 1560 | case IGMP_HOST_MEMBERSHIP_REPORT: |
---|
1545 | 1561 | case IGMPV2_HOST_MEMBERSHIP_REPORT: |
---|
1546 | | - /* fall through */ |
---|
1547 | 1562 | return 0; |
---|
1548 | 1563 | case IGMPV3_HOST_MEMBERSHIP_REPORT: |
---|
1549 | 1564 | return ip_mc_check_igmp_reportv3(skb); |
---|
.. | .. |
---|
1554 | 1569 | } |
---|
1555 | 1570 | } |
---|
1556 | 1571 | |
---|
1557 | | -static inline __sum16 ip_mc_validate_checksum(struct sk_buff *skb) |
---|
| 1572 | +static __sum16 ip_mc_validate_checksum(struct sk_buff *skb) |
---|
1558 | 1573 | { |
---|
1559 | 1574 | return skb_checksum_simple_validate(skb); |
---|
1560 | 1575 | } |
---|
1561 | 1576 | |
---|
1562 | | -static int __ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed) |
---|
1563 | | - |
---|
| 1577 | +static int ip_mc_check_igmp_csum(struct sk_buff *skb) |
---|
1564 | 1578 | { |
---|
1565 | | - struct sk_buff *skb_chk; |
---|
1566 | | - unsigned int transport_len; |
---|
1567 | 1579 | unsigned int len = skb_transport_offset(skb) + sizeof(struct igmphdr); |
---|
1568 | | - int ret = -EINVAL; |
---|
| 1580 | + unsigned int transport_len = ip_transport_len(skb); |
---|
| 1581 | + struct sk_buff *skb_chk; |
---|
1569 | 1582 | |
---|
1570 | | - transport_len = ntohs(ip_hdr(skb)->tot_len) - ip_hdrlen(skb); |
---|
| 1583 | + if (!ip_mc_may_pull(skb, len)) |
---|
| 1584 | + return -EINVAL; |
---|
1571 | 1585 | |
---|
1572 | 1586 | skb_chk = skb_checksum_trimmed(skb, transport_len, |
---|
1573 | 1587 | ip_mc_validate_checksum); |
---|
1574 | 1588 | if (!skb_chk) |
---|
1575 | | - goto err; |
---|
| 1589 | + return -EINVAL; |
---|
1576 | 1590 | |
---|
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) |
---|
| 1591 | + if (skb_chk != skb) |
---|
1588 | 1592 | kfree_skb(skb_chk); |
---|
1589 | 1593 | |
---|
1590 | | - ret = 0; |
---|
1591 | | - |
---|
1592 | | -err: |
---|
1593 | | - if (ret && skb_chk && skb_chk != skb) |
---|
1594 | | - kfree_skb(skb_chk); |
---|
1595 | | - |
---|
1596 | | - return ret; |
---|
| 1594 | + return 0; |
---|
1597 | 1595 | } |
---|
1598 | 1596 | |
---|
1599 | 1597 | /** |
---|
1600 | 1598 | * ip_mc_check_igmp - checks whether this is a sane IGMP packet |
---|
1601 | 1599 | * @skb: the skb to validate |
---|
1602 | | - * @skb_trimmed: to store an skb pointer trimmed to IPv4 packet tail (optional) |
---|
1603 | 1600 | * |
---|
1604 | 1601 | * Checks whether an IPv4 packet is a valid IGMP packet. If so sets |
---|
1605 | 1602 | * skb transport header accordingly and returns zero. |
---|
.. | .. |
---|
1609 | 1606 | * -ENOMSG: IP header validation succeeded but it is not an IGMP packet. |
---|
1610 | 1607 | * -ENOMEM: A memory allocation failure happened. |
---|
1611 | 1608 | * |
---|
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 | 1609 | * Caller needs to set the skb network header and free any returned skb if it |
---|
1621 | 1610 | * differs from the provided skb. |
---|
1622 | 1611 | */ |
---|
1623 | | -int ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed) |
---|
| 1612 | +int ip_mc_check_igmp(struct sk_buff *skb) |
---|
1624 | 1613 | { |
---|
1625 | 1614 | int ret = ip_mc_check_iphdr(skb); |
---|
1626 | 1615 | |
---|
.. | .. |
---|
1630 | 1619 | if (ip_hdr(skb)->protocol != IPPROTO_IGMP) |
---|
1631 | 1620 | return -ENOMSG; |
---|
1632 | 1621 | |
---|
1633 | | - return __ip_mc_check_igmp(skb, skb_trimmed); |
---|
| 1622 | + ret = ip_mc_check_igmp_csum(skb); |
---|
| 1623 | + if (ret < 0) |
---|
| 1624 | + return ret; |
---|
| 1625 | + |
---|
| 1626 | + return ip_mc_check_igmp_msg(skb); |
---|
1634 | 1627 | } |
---|
1635 | 1628 | EXPORT_SYMBOL(ip_mc_check_igmp); |
---|
1636 | 1629 | |
---|
.. | .. |
---|
1650 | 1643 | if (im->multiaddr == IGMP_ALL_HOSTS) |
---|
1651 | 1644 | continue; |
---|
1652 | 1645 | if (ipv4_is_local_multicast(im->multiaddr) && |
---|
1653 | | - !net->ipv4.sysctl_igmp_llm_reports) |
---|
| 1646 | + !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) |
---|
1654 | 1647 | continue; |
---|
1655 | 1648 | |
---|
1656 | 1649 | /* a failover is happening and switches |
---|
.. | .. |
---|
1671 | 1664 | * A socket has left a multicast group on device dev |
---|
1672 | 1665 | */ |
---|
1673 | 1666 | |
---|
1674 | | -void ip_mc_dec_group(struct in_device *in_dev, __be32 addr) |
---|
| 1667 | +void __ip_mc_dec_group(struct in_device *in_dev, __be32 addr, gfp_t gfp) |
---|
1675 | 1668 | { |
---|
1676 | 1669 | struct ip_mc_list *i; |
---|
1677 | 1670 | struct ip_mc_list __rcu **ip; |
---|
.. | .. |
---|
1686 | 1679 | ip_mc_hash_remove(in_dev, i); |
---|
1687 | 1680 | *ip = i->next_rcu; |
---|
1688 | 1681 | in_dev->mc_count--; |
---|
1689 | | - igmp_group_dropped(i); |
---|
| 1682 | + __igmp_group_dropped(i, gfp); |
---|
1690 | 1683 | ip_mc_clear_src(i); |
---|
1691 | 1684 | |
---|
1692 | 1685 | if (!in_dev->dead) |
---|
.. | .. |
---|
1699 | 1692 | } |
---|
1700 | 1693 | } |
---|
1701 | 1694 | } |
---|
1702 | | -EXPORT_SYMBOL(ip_mc_dec_group); |
---|
| 1695 | +EXPORT_SYMBOL(__ip_mc_dec_group); |
---|
1703 | 1696 | |
---|
1704 | 1697 | /* Device changing type */ |
---|
1705 | 1698 | |
---|
.. | .. |
---|
1757 | 1750 | |
---|
1758 | 1751 | in_dev->mr_qi = IGMP_QUERY_INTERVAL; |
---|
1759 | 1752 | in_dev->mr_qri = IGMP_QUERY_RESPONSE_INTERVAL; |
---|
1760 | | - in_dev->mr_qrv = net->ipv4.sysctl_igmp_qrv; |
---|
| 1753 | + in_dev->mr_qrv = READ_ONCE(net->ipv4.sysctl_igmp_qrv); |
---|
1761 | 1754 | } |
---|
1762 | 1755 | #else |
---|
1763 | 1756 | static void ip_mc_reset(struct in_device *in_dev) |
---|
.. | .. |
---|
1891 | 1884 | #ifdef CONFIG_IP_MULTICAST |
---|
1892 | 1885 | if (psf->sf_oldin && |
---|
1893 | 1886 | !IGMP_V1_SEEN(in_dev) && !IGMP_V2_SEEN(in_dev)) { |
---|
1894 | | - psf->sf_crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv; |
---|
| 1887 | + psf->sf_crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); |
---|
1895 | 1888 | psf->sf_next = pmc->tomb; |
---|
1896 | 1889 | pmc->tomb = psf; |
---|
1897 | 1890 | rv = 1; |
---|
.. | .. |
---|
1955 | 1948 | /* filter mode change */ |
---|
1956 | 1949 | pmc->sfmode = MCAST_INCLUDE; |
---|
1957 | 1950 | #ifdef CONFIG_IP_MULTICAST |
---|
1958 | | - pmc->crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv; |
---|
| 1951 | + pmc->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); |
---|
1959 | 1952 | in_dev->mr_ifc_count = pmc->crcount; |
---|
1960 | 1953 | for (psf = pmc->sources; psf; psf = psf->sf_next) |
---|
1961 | 1954 | psf->sf_crcount = 0; |
---|
.. | .. |
---|
2134 | 2127 | #ifdef CONFIG_IP_MULTICAST |
---|
2135 | 2128 | /* else no filters; keep old mode for reports */ |
---|
2136 | 2129 | |
---|
2137 | | - pmc->crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv; |
---|
| 2130 | + pmc->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); |
---|
2138 | 2131 | in_dev->mr_ifc_count = pmc->crcount; |
---|
2139 | 2132 | for (psf = pmc->sources; psf; psf = psf->sf_next) |
---|
2140 | 2133 | psf->sf_crcount = 0; |
---|
.. | .. |
---|
2200 | 2193 | count++; |
---|
2201 | 2194 | } |
---|
2202 | 2195 | err = -ENOBUFS; |
---|
2203 | | - if (count >= net->ipv4.sysctl_igmp_max_memberships) |
---|
| 2196 | + if (count >= READ_ONCE(net->ipv4.sysctl_igmp_max_memberships)) |
---|
2204 | 2197 | goto done; |
---|
2205 | 2198 | iml = sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL); |
---|
2206 | 2199 | if (!iml) |
---|
.. | .. |
---|
2211 | 2204 | iml->sflist = NULL; |
---|
2212 | 2205 | iml->sfmode = mode; |
---|
2213 | 2206 | rcu_assign_pointer(inet->mc_list, iml); |
---|
2214 | | - __ip_mc_inc_group(in_dev, addr, mode); |
---|
| 2207 | + ____ip_mc_inc_group(in_dev, addr, mode, GFP_KERNEL); |
---|
2215 | 2208 | err = 0; |
---|
2216 | 2209 | done: |
---|
2217 | 2210 | return err; |
---|
.. | .. |
---|
2387 | 2380 | } |
---|
2388 | 2381 | /* else, add a new source to the filter */ |
---|
2389 | 2382 | |
---|
2390 | | - if (psl && psl->sl_count >= net->ipv4.sysctl_igmp_max_msf) { |
---|
| 2383 | + if (psl && psl->sl_count >= READ_ONCE(net->ipv4.sysctl_igmp_max_msf)) { |
---|
2391 | 2384 | err = -ENOBUFS; |
---|
2392 | 2385 | goto done; |
---|
2393 | 2386 | } |
---|
.. | .. |
---|
2409 | 2402 | newpsl->sl_addr[i] = psl->sl_addr[i]; |
---|
2410 | 2403 | /* decrease mem now to avoid the memleak warning */ |
---|
2411 | 2404 | atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc); |
---|
2412 | | - kfree_rcu(psl, rcu); |
---|
2413 | 2405 | } |
---|
2414 | 2406 | rcu_assign_pointer(pmc->sflist, newpsl); |
---|
| 2407 | + if (psl) |
---|
| 2408 | + kfree_rcu(psl, rcu); |
---|
2415 | 2409 | psl = newpsl; |
---|
2416 | 2410 | } |
---|
2417 | 2411 | rv = 1; /* > 0 for insert logic below if sl_count is 0 */ |
---|
.. | .. |
---|
2509 | 2503 | psl->sl_count, psl->sl_addr, 0); |
---|
2510 | 2504 | /* decrease mem now to avoid the memleak warning */ |
---|
2511 | 2505 | atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc); |
---|
2512 | | - kfree_rcu(psl, rcu); |
---|
2513 | | - } else |
---|
| 2506 | + } else { |
---|
2514 | 2507 | (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode, |
---|
2515 | 2508 | 0, NULL, 0); |
---|
| 2509 | + } |
---|
2516 | 2510 | rcu_assign_pointer(pmc->sflist, newpsl); |
---|
| 2511 | + if (psl) |
---|
| 2512 | + kfree_rcu(psl, rcu); |
---|
2517 | 2513 | pmc->sfmode = msf->imsf_fmode; |
---|
2518 | 2514 | err = 0; |
---|
2519 | 2515 | done: |
---|
.. | .. |
---|
2581 | 2577 | } |
---|
2582 | 2578 | |
---|
2583 | 2579 | int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf, |
---|
2584 | | - struct group_filter __user *optval, int __user *optlen) |
---|
| 2580 | + struct sockaddr_storage __user *p) |
---|
2585 | 2581 | { |
---|
2586 | | - int err, i, count, copycount; |
---|
| 2582 | + int i, count, copycount; |
---|
2587 | 2583 | struct sockaddr_in *psin; |
---|
2588 | 2584 | __be32 addr; |
---|
2589 | 2585 | struct ip_mc_socklist *pmc; |
---|
.. | .. |
---|
2599 | 2595 | if (!ipv4_is_multicast(addr)) |
---|
2600 | 2596 | return -EINVAL; |
---|
2601 | 2597 | |
---|
2602 | | - err = -EADDRNOTAVAIL; |
---|
2603 | | - |
---|
2604 | 2598 | for_each_pmc_rtnl(inet, pmc) { |
---|
2605 | 2599 | if (pmc->multi.imr_multiaddr.s_addr == addr && |
---|
2606 | 2600 | pmc->multi.imr_ifindex == gsf->gf_interface) |
---|
2607 | 2601 | break; |
---|
2608 | 2602 | } |
---|
2609 | 2603 | if (!pmc) /* must have a prior join */ |
---|
2610 | | - goto done; |
---|
| 2604 | + return -EADDRNOTAVAIL; |
---|
2611 | 2605 | gsf->gf_fmode = pmc->sfmode; |
---|
2612 | 2606 | psl = rtnl_dereference(pmc->sflist); |
---|
2613 | 2607 | count = psl ? psl->sl_count : 0; |
---|
2614 | 2608 | copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc; |
---|
2615 | 2609 | 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++) { |
---|
| 2610 | + for (i = 0; i < copycount; i++, p++) { |
---|
2621 | 2611 | struct sockaddr_storage ss; |
---|
2622 | 2612 | |
---|
2623 | 2613 | psin = (struct sockaddr_in *)&ss; |
---|
2624 | 2614 | memset(&ss, 0, sizeof(ss)); |
---|
2625 | 2615 | psin->sin_family = AF_INET; |
---|
2626 | 2616 | psin->sin_addr.s_addr = psl->sl_addr[i]; |
---|
2627 | | - if (copy_to_user(&optval->gf_slist[i], &ss, sizeof(ss))) |
---|
| 2617 | + if (copy_to_user(p, &ss, sizeof(ss))) |
---|
2628 | 2618 | return -EFAULT; |
---|
2629 | 2619 | } |
---|
2630 | 2620 | return 0; |
---|
2631 | | -done: |
---|
2632 | | - return err; |
---|
2633 | 2621 | } |
---|
2634 | 2622 | |
---|
2635 | 2623 | /* |
---|