Skip to content

Commit 9afd85c

Browse files
T-Xdavem330
authored andcommitted
net: Export IGMP/MLD message validation code
With this patch, the IGMP and MLD message validation functions are moved from the bridge code to IPv4/IPv6 multicast files. Some small refactoring was done to enhance readibility and to iron out some differences in behaviour between the IGMP and MLD parsing code (e.g. the skb-cloning of MLD messages is now only done if necessary, just like the IGMP part always did). Finally, these IGMP and MLD message validation functions are exported so that not only the bridge can use it but batman-adv later, too. Signed-off-by: Linus Lüssing <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 3c9e4f8 commit 9afd85c

File tree

8 files changed

+498
-188
lines changed

8 files changed

+498
-188
lines changed

include/linux/igmp.h

+1
Original file line numberDiff line numberDiff line change
@@ -130,5 +130,6 @@ extern void ip_mc_unmap(struct in_device *);
130130
extern void ip_mc_remap(struct in_device *);
131131
extern void ip_mc_dec_group(struct in_device *in_dev, __be32 addr);
132132
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);
133134

134135
#endif

include/linux/skbuff.h

+3
Original file line numberDiff line numberDiff line change
@@ -3419,6 +3419,9 @@ static inline void skb_checksum_none_assert(const struct sk_buff *skb)
34193419
bool skb_partial_csum_set(struct sk_buff *skb, u16 start, u16 off);
34203420

34213421
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));
34223425

34233426
u32 skb_get_poff(const struct sk_buff *skb);
34243427
u32 __skb_get_poff(const struct sk_buff *skb, void *data,

include/net/addrconf.h

+1
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ void ipv6_mc_unmap(struct inet6_dev *idev);
142142
void ipv6_mc_remap(struct inet6_dev *idev);
143143
void ipv6_mc_init_dev(struct inet6_dev *idev);
144144
void ipv6_mc_destroy_dev(struct inet6_dev *idev);
145+
int ipv6_mc_check_mld(struct sk_buff *skb, struct sk_buff **skb_trimmed);
145146
void addrconf_dad_failure(struct inet6_ifaddr *ifp);
146147

147148
bool ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group,

net/bridge/br_multicast.c

+30-188
Original file line numberDiff line numberDiff line change
@@ -975,9 +975,6 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br,
975975
int err = 0;
976976
__be32 group;
977977

978-
if (!pskb_may_pull(skb, sizeof(*ih)))
979-
return -EINVAL;
980-
981978
ih = igmpv3_report_hdr(skb);
982979
num = ntohs(ih->ngrec);
983980
len = sizeof(*ih);
@@ -1248,25 +1245,14 @@ static int br_ip4_multicast_query(struct net_bridge *br,
12481245
max_delay = 10 * HZ;
12491246
group = 0;
12501247
}
1251-
} else {
1252-
if (!pskb_may_pull(skb, sizeof(struct igmpv3_query))) {
1253-
err = -EINVAL;
1254-
goto out;
1255-
}
1256-
1248+
} else if (skb->len >= sizeof(*ih3)) {
12571249
ih3 = igmpv3_query_hdr(skb);
12581250
if (ih3->nsrcs)
12591251
goto out;
12601252

12611253
max_delay = ih3->code ?
12621254
IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE) : 1;
1263-
}
1264-
1265-
/* RFC2236+RFC3376 (IGMPv2+IGMPv3) require the multicast link layer
1266-
* all-systems destination addresses (224.0.0.1) for general queries
1267-
*/
1268-
if (!group && iph->daddr != htonl(INADDR_ALLHOSTS_GROUP)) {
1269-
err = -EINVAL;
1255+
} else {
12701256
goto out;
12711257
}
12721258

@@ -1329,12 +1315,6 @@ static int br_ip6_multicast_query(struct net_bridge *br,
13291315
(port && port->state == BR_STATE_DISABLED))
13301316
goto out;
13311317

1332-
/* RFC2710+RFC3810 (MLDv1+MLDv2) require link-local source addresses */
1333-
if (!(ipv6_addr_type(&ip6h->saddr) & IPV6_ADDR_LINKLOCAL)) {
1334-
err = -EINVAL;
1335-
goto out;
1336-
}
1337-
13381318
if (skb->len == sizeof(*mld)) {
13391319
if (!pskb_may_pull(skb, sizeof(*mld))) {
13401320
err = -EINVAL;
@@ -1358,14 +1338,6 @@ static int br_ip6_multicast_query(struct net_bridge *br,
13581338

13591339
is_general_query = group && ipv6_addr_any(group);
13601340

1361-
/* RFC2710+RFC3810 (MLDv1+MLDv2) require the multicast link layer
1362-
* all-nodes destination address (ff02::1) for general queries
1363-
*/
1364-
if (is_general_query && !ipv6_addr_is_ll_all_nodes(&ip6h->daddr)) {
1365-
err = -EINVAL;
1366-
goto out;
1367-
}
1368-
13691341
if (is_general_query) {
13701342
saddr.proto = htons(ETH_P_IPV6);
13711343
saddr.u.ip6 = ip6h->saddr;
@@ -1557,66 +1529,22 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
15571529
struct sk_buff *skb,
15581530
u16 vid)
15591531
{
1560-
struct sk_buff *skb2 = skb;
1561-
const struct iphdr *iph;
1532+
struct sk_buff *skb_trimmed = NULL;
15621533
struct igmphdr *ih;
1563-
unsigned int len;
1564-
unsigned int offset;
15651534
int err;
15661535

1567-
/* We treat OOM as packet loss for now. */
1568-
if (!pskb_may_pull(skb, sizeof(*iph)))
1569-
return -EINVAL;
1570-
1571-
iph = ip_hdr(skb);
1572-
1573-
if (iph->ihl < 5 || iph->version != 4)
1574-
return -EINVAL;
1575-
1576-
if (!pskb_may_pull(skb, ip_hdrlen(skb)))
1577-
return -EINVAL;
1578-
1579-
iph = ip_hdr(skb);
1536+
err = ip_mc_check_igmp(skb, &skb_trimmed);
15801537

1581-
if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
1582-
return -EINVAL;
1583-
1584-
if (iph->protocol != IPPROTO_IGMP) {
1585-
if (!ipv4_is_local_multicast(iph->daddr))
1538+
if (err == -ENOMSG) {
1539+
if (!ipv4_is_local_multicast(ip_hdr(skb)->daddr))
15861540
BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
15871541
return 0;
1542+
} else if (err < 0) {
1543+
return err;
15881544
}
15891545

1590-
len = ntohs(iph->tot_len);
1591-
if (skb->len < len || len < ip_hdrlen(skb))
1592-
return -EINVAL;
1593-
1594-
if (skb->len > len) {
1595-
skb2 = skb_clone(skb, GFP_ATOMIC);
1596-
if (!skb2)
1597-
return -ENOMEM;
1598-
1599-
err = pskb_trim_rcsum(skb2, len);
1600-
if (err)
1601-
goto err_out;
1602-
}
1603-
1604-
len -= ip_hdrlen(skb2);
1605-
offset = skb_network_offset(skb2) + ip_hdrlen(skb2);
1606-
__skb_pull(skb2, offset);
1607-
skb_reset_transport_header(skb2);
1608-
1609-
err = -EINVAL;
1610-
if (!pskb_may_pull(skb2, sizeof(*ih)))
1611-
goto out;
1612-
1613-
if (skb_checksum_simple_validate(skb2))
1614-
goto out;
1615-
1616-
err = 0;
1617-
16181546
BR_INPUT_SKB_CB(skb)->igmp = 1;
1619-
ih = igmp_hdr(skb2);
1547+
ih = igmp_hdr(skb);
16201548

16211549
switch (ih->type) {
16221550
case IGMP_HOST_MEMBERSHIP_REPORT:
@@ -1625,21 +1553,19 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
16251553
err = br_ip4_multicast_add_group(br, port, ih->group, vid);
16261554
break;
16271555
case IGMPV3_HOST_MEMBERSHIP_REPORT:
1628-
err = br_ip4_multicast_igmp3_report(br, port, skb2, vid);
1556+
err = br_ip4_multicast_igmp3_report(br, port, skb_trimmed, vid);
16291557
break;
16301558
case IGMP_HOST_MEMBERSHIP_QUERY:
1631-
err = br_ip4_multicast_query(br, port, skb2, vid);
1559+
err = br_ip4_multicast_query(br, port, skb_trimmed, vid);
16321560
break;
16331561
case IGMP_HOST_LEAVE_MESSAGE:
16341562
br_ip4_multicast_leave_group(br, port, ih->group, vid);
16351563
break;
16361564
}
16371565

1638-
out:
1639-
__skb_push(skb2, offset);
1640-
err_out:
1641-
if (skb2 != skb)
1642-
kfree_skb(skb2);
1566+
if (skb_trimmed)
1567+
kfree_skb(skb_trimmed);
1568+
16431569
return err;
16441570
}
16451571

@@ -1649,126 +1575,42 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
16491575
struct sk_buff *skb,
16501576
u16 vid)
16511577
{
1652-
struct sk_buff *skb2;
1653-
const struct ipv6hdr *ip6h;
1654-
u8 icmp6_type;
1655-
u8 nexthdr;
1656-
__be16 frag_off;
1657-
unsigned int len;
1658-
int offset;
1578+
struct sk_buff *skb_trimmed = NULL;
1579+
struct mld_msg *mld;
16591580
int err;
16601581

1661-
if (!pskb_may_pull(skb, sizeof(*ip6h)))
1662-
return -EINVAL;
1582+
err = ipv6_mc_check_mld(skb, &skb_trimmed);
16631583

1664-
ip6h = ipv6_hdr(skb);
1665-
1666-
/*
1667-
* We're interested in MLD messages only.
1668-
* - Version is 6
1669-
* - MLD has always Router Alert hop-by-hop option
1670-
* - But we do not support jumbrograms.
1671-
*/
1672-
if (ip6h->version != 6)
1673-
return 0;
1674-
1675-
/* Prevent flooding this packet if there is no listener present */
1676-
if (!ipv6_addr_is_ll_all_nodes(&ip6h->daddr))
1677-
BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
1678-
1679-
if (ip6h->nexthdr != IPPROTO_HOPOPTS ||
1680-
ip6h->payload_len == 0)
1681-
return 0;
1682-
1683-
len = ntohs(ip6h->payload_len) + sizeof(*ip6h);
1684-
if (skb->len < len)
1685-
return -EINVAL;
1686-
1687-
nexthdr = ip6h->nexthdr;
1688-
offset = ipv6_skip_exthdr(skb, sizeof(*ip6h), &nexthdr, &frag_off);
1689-
1690-
if (offset < 0 || nexthdr != IPPROTO_ICMPV6)
1584+
if (err == -ENOMSG) {
1585+
if (!ipv6_addr_is_ll_all_nodes(&ipv6_hdr(skb)->daddr))
1586+
BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
16911587
return 0;
1692-
1693-
/* Okay, we found ICMPv6 header */
1694-
skb2 = skb_clone(skb, GFP_ATOMIC);
1695-
if (!skb2)
1696-
return -ENOMEM;
1697-
1698-
err = -EINVAL;
1699-
if (!pskb_may_pull(skb2, offset + sizeof(struct icmp6hdr)))
1700-
goto out;
1701-
1702-
len -= offset - skb_network_offset(skb2);
1703-
1704-
__skb_pull(skb2, offset);
1705-
skb_reset_transport_header(skb2);
1706-
skb_postpull_rcsum(skb2, skb_network_header(skb2),
1707-
skb_network_header_len(skb2));
1708-
1709-
icmp6_type = icmp6_hdr(skb2)->icmp6_type;
1710-
1711-
switch (icmp6_type) {
1712-
case ICMPV6_MGM_QUERY:
1713-
case ICMPV6_MGM_REPORT:
1714-
case ICMPV6_MGM_REDUCTION:
1715-
case ICMPV6_MLD2_REPORT:
1716-
break;
1717-
default:
1718-
err = 0;
1719-
goto out;
1720-
}
1721-
1722-
/* Okay, we found MLD message. Check further. */
1723-
if (skb2->len > len) {
1724-
err = pskb_trim_rcsum(skb2, len);
1725-
if (err)
1726-
goto out;
1727-
err = -EINVAL;
1588+
} else if (err < 0) {
1589+
return err;
17281590
}
17291591

1730-
ip6h = ipv6_hdr(skb2);
1731-
1732-
if (skb_checksum_validate(skb2, IPPROTO_ICMPV6, ip6_compute_pseudo))
1733-
goto out;
1734-
1735-
err = 0;
1736-
17371592
BR_INPUT_SKB_CB(skb)->igmp = 1;
1593+
mld = (struct mld_msg *)skb_transport_header(skb);
17381594

1739-
switch (icmp6_type) {
1595+
switch (mld->mld_type) {
17401596
case ICMPV6_MGM_REPORT:
1741-
{
1742-
struct mld_msg *mld;
1743-
if (!pskb_may_pull(skb2, sizeof(*mld))) {
1744-
err = -EINVAL;
1745-
goto out;
1746-
}
1747-
mld = (struct mld_msg *)skb_transport_header(skb2);
17481597
BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
17491598
err = br_ip6_multicast_add_group(br, port, &mld->mld_mca, vid);
17501599
break;
1751-
}
17521600
case ICMPV6_MLD2_REPORT:
1753-
err = br_ip6_multicast_mld2_report(br, port, skb2, vid);
1601+
err = br_ip6_multicast_mld2_report(br, port, skb_trimmed, vid);
17541602
break;
17551603
case ICMPV6_MGM_QUERY:
1756-
err = br_ip6_multicast_query(br, port, skb2, vid);
1604+
err = br_ip6_multicast_query(br, port, skb_trimmed, vid);
17571605
break;
17581606
case ICMPV6_MGM_REDUCTION:
1759-
{
1760-
struct mld_msg *mld;
1761-
if (!pskb_may_pull(skb2, sizeof(*mld))) {
1762-
err = -EINVAL;
1763-
goto out;
1764-
}
1765-
mld = (struct mld_msg *)skb_transport_header(skb2);
17661607
br_ip6_multicast_leave_group(br, port, &mld->mld_mca, vid);
1767-
}
1608+
break;
17681609
}
17691610

1770-
out:
1771-
kfree_skb(skb2);
1611+
if (skb_trimmed)
1612+
kfree_skb(skb_trimmed);
1613+
17721614
return err;
17731615
}
17741616
#endif

0 commit comments

Comments
 (0)