History

bridge: simplify ip_mc_check_igmp() and ipv6_mc_check_mld() calls

This patch refactors ip_mc_check_igmp(), ipv6_mc_check_mld() and their callers (more precisely, the Linux bridge) to not rely on the skb_trimmed parameter anymore. An skb with its tail trimmed to the IP packet length was initially introduced for the following three reasons: 1) To be able to verify the ICMPv6 checksum. 2) To be able to distinguish the version of an IGMP or MLD query. They are distinguishable only by their size. 3) To avoid parsing data for an IGMPv3 or MLDv2 report that is beyond the IP packet but still within the skb. The first case still uses a cloned and potentially trimmed skb to verfiy. However, there is no need to propagate it to the caller. For the second and third case explicit IP packet length checks were added. This hopefully makes ip_mc_check_igmp() and ipv6_mc_check_mld() easier to read and verfiy, as well as easier to use.

Linus Lüssing committed 7 years ago

Commit ba5ea611 parent 6679cf08 files · +70 −72Browse files

Unified · Split

8 files
  1. include/linux/igmp.h +10 −1
  2. include/linux/ip.h +5 −0
  3. include/linux/ipv6.h +6 −0
  4. include/net/addrconf.h +11 −1
  5. net/batman-adv/multicast.c +2 −2
  6. net/bridge/br_multicast.c +28 −29
  7. net/ipv4/igmp.c +4 −19
  8. net/ipv6/mcast_snoop.c +4 −20
include/linux/igmp.h+10−1
@@ -18,6 +18,7 @@ · 17 unmodified lines
1818 #include <linux/skbuff.h>
1919 #include <linux/timer.h>
2020 #include <linux/in.h>
21+#include <linux/ip.h>
2122 #include <linux/refcount.h>
2223 #include <uapi/linux/igmp.h>
2324
@@ -106,6 +107,14 @@ struct ip_mc_list { · 82 unmodified lines
106107 #define IGMPV3_QQIC(value) IGMPV3_EXP(0x80, 4, 3, value)
107108 #define IGMPV3_MRC(value) IGMPV3_EXP(0x80, 4, 3, value)
108109
110+static inline int ip_mc_may_pull(struct sk_buff *skb, unsigned int len)
111+{
112+ if (skb_transport_offset(skb) + ip_transport_len(skb) < len)
113+ return -EINVAL;
114+
115+ return pskb_may_pull(skb, len);
116+}
117+
109118 extern int ip_check_mc_rcu(struct in_device *dev, __be32 mc_addr, __be32 src_addr, u8 proto);
110119 extern int igmp_rcv(struct sk_buff *);
111120 extern int ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr);
@@ -130,6 +139,6 @@ extern void ip_mc_unmap(struct in_device *); · 18 unmodified lines
130139 extern void ip_mc_remap(struct in_device *);
131140 extern void ip_mc_dec_group(struct in_device *in_dev, __be32 addr);
132141 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);
142+int ip_mc_check_igmp(struct sk_buff *skb);
134143
135144 #endif
include/linux/ip.h+5−0
@@ -34,4 +34,9 @@ static inline struct iphdr *ipip_hdr(const struct sk_buff *skb) · 33 unmodified lines
3434 {
3535 return (struct iphdr *)skb_transport_header(skb);
3636 }
37+
38+static inline unsigned int ip_transport_len(const struct sk_buff *skb)
39+{
40+ return ntohs(ip_hdr(skb)->tot_len) - skb_network_header_len(skb);
41+}
3742 #endif /* _LINUX_IP_H */
include/linux/ipv6.h+6−0
@@ -104,6 +104,12 @@ static inline struct ipv6hdr *ipipv6_hdr(const struct sk_buff *skb) · 103 unmodified lines
104104 return (struct ipv6hdr *)skb_transport_header(skb);
105105 }
106106
107+static inline unsigned int ipv6_transport_len(const struct sk_buff *skb)
108+{
109+ return ntohs(ipv6_hdr(skb)->payload_len) + sizeof(struct ipv6hdr) -
110+ skb_network_header_len(skb);
111+}
112+
107113 /*
108114 This structure contains results of exthdrs parsing
109115 as offsets from skb->nh.
include/net/addrconf.h+11−1
@@ -49,6 +49,7 @@ struct prefix_info { · 48 unmodified lines
4949 struct in6_addr prefix;
5050 };
5151
52+#include <linux/ipv6.h>
5253 #include <linux/netdevice.h>
5354 #include <net/if_inet6.h>
5455 #include <net/ipv6.h>
@@ -201,6 +202,15 @@ u32 ipv6_addr_label(struct net *net, const struct in6_addr *addr, · 146 unmodified lines
201202 /*
202203 * multicast prototypes (mcast.c)
203204 */
205+static inline int ipv6_mc_may_pull(struct sk_buff *skb,
206+ unsigned int len)
207+{
208+ if (skb_transport_offset(skb) + ipv6_transport_len(skb) < len)
209+ return -EINVAL;
210+
211+ return pskb_may_pull(skb, len);
212+}
213+
204214 int ipv6_sock_mc_join(struct sock *sk, int ifindex,
205215 const struct in6_addr *addr);
206216 int ipv6_sock_mc_drop(struct sock *sk, int ifindex,
@@ -219,7 +229,7 @@ void ipv6_mc_unmap(struct inet6_dev *idev); · 12 unmodified lines
219229 void ipv6_mc_remap(struct inet6_dev *idev);
220230 void ipv6_mc_init_dev(struct inet6_dev *idev);
221231 void ipv6_mc_destroy_dev(struct inet6_dev *idev);
222−int ipv6_mc_check_mld(struct sk_buff *skb, struct sk_buff **skb_trimmed);
232+int ipv6_mc_check_mld(struct sk_buff *skb);
223233 void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp);
224234
225235 bool ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group,
net/batman-adv/multicast.c+2−2
@@ -674,7 +674,7 @@ static void batadv_mcast_mla_update(struct work_struct *work) · 673 unmodified lines
674674 */
675675 static bool batadv_mcast_is_report_ipv4(struct sk_buff *skb)
676676 {
677− if (ip_mc_check_igmp(skb, NULL) < 0)
677+ if (ip_mc_check_igmp(skb) < 0)
678678 return false;
679679
680680 switch (igmp_hdr(skb)->type) {
@@ -741,7 +741,7 @@ static int batadv_mcast_forw_mode_check_ipv4(struct batadv_priv *bat_priv, · 60 unmodified lines
741741 */
742742 static bool batadv_mcast_is_report_ipv6(struct sk_buff *skb)
743743 {
744− if (ipv6_mc_check_mld(skb, NULL) < 0)
744+ if (ipv6_mc_check_mld(skb) < 0)
745745 return false;
746746
747747 switch (icmp6_hdr(skb)->icmp6_type) {
net/bridge/br_multicast.c+28−29
@@ -938,7 +938,7 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br, · 937 unmodified lines
938938
939939 for (i = 0; i < num; i++) {
940940 len += sizeof(*grec);
941− if (!pskb_may_pull(skb, len))
941+ if (!ip_mc_may_pull(skb, len))
942942 return -EINVAL;
943943
944944 grec = (void *)(skb->data + len - sizeof(*grec));
@@ -946,7 +946,7 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br, · 1 unmodified line
946946 type = grec->grec_type;
947947
948948 len += ntohs(grec->grec_nsrcs) * 4;
949− if (!pskb_may_pull(skb, len))
949+ if (!ip_mc_may_pull(skb, len))
950950 return -EINVAL;
951951
952952 /* We treat this as an IGMPv2 report for now. */
@@ -985,15 +985,17 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br, · 32 unmodified lines
985985 struct sk_buff *skb,
986986 u16 vid)
987987 {
988+ unsigned int nsrcs_offset;
988989 const unsigned char *src;
989990 struct icmp6hdr *icmp6h;
990991 struct mld2_grec *grec;
992+ unsigned int grec_len;
991993 int i;
992994 int len;
993995 int num;
994996 int err = 0;
995997
996− if (!pskb_may_pull(skb, sizeof(*icmp6h)))
998+ if (!ipv6_mc_may_pull(skb, sizeof(*icmp6h)))
997999 return -EINVAL;
9981000
9991001 icmp6h = icmp6_hdr(skb);
@@ -1003,21 +1005,25 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br, · 3 unmodified lines
10031005 for (i = 0; i < num; i++) {
10041006 __be16 *nsrcs, _nsrcs;
10051007
1006− nsrcs = skb_header_pointer(skb,
1007− len + offsetof(struct mld2_grec,
1008− grec_nsrcs),
1008+ nsrcs_offset = len + offsetof(struct mld2_grec, grec_nsrcs);
1009+
1010+ if (skb_transport_offset(skb) + ipv6_transport_len(skb) <
1011+ nsrcs_offset + sizeof(_nsrcs))
1012+ return -EINVAL;
1013+
1014+ nsrcs = skb_header_pointer(skb, nsrcs_offset,
10091015 sizeof(_nsrcs), &_nsrcs);
10101016 if (!nsrcs)
10111017 return -EINVAL;
10121018
1013− if (!pskb_may_pull(skb,
1014− len + sizeof(*grec) +
1015− sizeof(struct in6_addr) * ntohs(*nsrcs)))
1019+ grec_len = sizeof(*grec) +
1020+ sizeof(struct in6_addr) * ntohs(*nsrcs);
1021+
1022+ if (!ipv6_mc_may_pull(skb, len + grec_len))
10161023 return -EINVAL;
10171024
10181025 grec = (struct mld2_grec *)(skb->data + len);
1019− len += sizeof(*grec) +
1020− sizeof(struct in6_addr) * ntohs(*nsrcs);
1026+ len += grec_len;
10211027
10221028 /* We treat these as MLDv1 reports for now. */
10231029 switch (grec->grec_type) {
@@ -1219,6 +1225,7 @@ static void br_ip4_multicast_query(struct net_bridge *br, · 195 unmodified lines
12191225 struct sk_buff *skb,
12201226 u16 vid)
12211227 {
1228+ unsigned int transport_len = ip_transport_len(skb);
12221229 const struct iphdr *iph = ip_hdr(skb);
12231230 struct igmphdr *ih = igmp_hdr(skb);
12241231 struct net_bridge_mdb_entry *mp;
@@ -1228,7 +1235,6 @@ static void br_ip4_multicast_query(struct net_bridge *br, · 3 unmodified lines
12281235 struct br_ip saddr;
12291236 unsigned long max_delay;
12301237 unsigned long now = jiffies;
1231− unsigned int offset = skb_transport_offset(skb);
12321238 __be32 group;
12331239
12341240 spin_lock(&br->multicast_lock);
@@ -1238,14 +1244,14 @@ static void br_ip4_multicast_query(struct net_bridge *br, · 3 unmodified lines
12381244
12391245 group = ih->group;
12401246
1241− if (skb->len == offset + sizeof(*ih)) {
1247+ if (transport_len == sizeof(*ih)) {
12421248 max_delay = ih->code * (HZ / IGMP_TIMER_SCALE);
12431249
12441250 if (!max_delay) {
12451251 max_delay = 10 * HZ;
12461252 group = 0;
12471253 }
1248− } else if (skb->len >= offset + sizeof(*ih3)) {
1254+ } else if (transport_len >= sizeof(*ih3)) {
12491255 ih3 = igmpv3_query_hdr(skb);
12501256 if (ih3->nsrcs)
12511257 goto out;
@@ -1296,6 +1302,7 @@ static int br_ip6_multicast_query(struct net_bridge *br, · 44 unmodified lines
12961302 struct sk_buff *skb,
12971303 u16 vid)
12981304 {
1305+ unsigned int transport_len = ipv6_transport_len(skb);
12991306 const struct ipv6hdr *ip6h = ipv6_hdr(skb);
13001307 struct mld_msg *mld;
13011308 struct net_bridge_mdb_entry *mp;
@@ -1315,7 +1322,7 @@ static int br_ip6_multicast_query(struct net_bridge *br, · 13 unmodified lines
13151322 (port && port->state == BR_STATE_DISABLED))
13161323 goto out;
13171324
1318− if (skb->len == offset + sizeof(*mld)) {
1325+ if (transport_len == sizeof(*mld)) {
13191326 if (!pskb_may_pull(skb, offset + sizeof(*mld))) {
13201327 err = -EINVAL;
13211328 goto out;
@@ -1581,12 +1588,11 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br, · 259 unmodified lines
15811588 struct sk_buff *skb,
15821589 u16 vid)
15831590 {
1584− struct sk_buff *skb_trimmed = NULL;
15851591 const unsigned char *src;
15861592 struct igmphdr *ih;
15871593 int err;
15881594
1589− err = ip_mc_check_igmp(skb, &skb_trimmed);
1595+ err = ip_mc_check_igmp(skb);
15901596
15911597 if (err == -ENOMSG) {
15921598 if (!ipv4_is_local_multicast(ip_hdr(skb)->daddr)) {
@@ -1612,19 +1618,16 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br, · 19 unmodified lines
16121618 err = br_ip4_multicast_add_group(br, port, ih->group, vid, src);
16131619 break;
16141620 case IGMPV3_HOST_MEMBERSHIP_REPORT:
1615− err = br_ip4_multicast_igmp3_report(br, port, skb_trimmed, vid);
1621+ err = br_ip4_multicast_igmp3_report(br, port, skb, vid);
16161622 break;
16171623 case IGMP_HOST_MEMBERSHIP_QUERY:
1618− br_ip4_multicast_query(br, port, skb_trimmed, vid);
1624+ br_ip4_multicast_query(br, port, skb, vid);
16191625 break;
16201626 case IGMP_HOST_LEAVE_MESSAGE:
16211627 br_ip4_multicast_leave_group(br, port, ih->group, vid, src);
16221628 break;
16231629 }
16241630
1625− if (skb_trimmed && skb_trimmed != skb)
1626− kfree_skb(skb_trimmed);
1627−
16281631 br_multicast_count(br, port, skb, BR_INPUT_SKB_CB(skb)->igmp,
16291632 BR_MCAST_DIR_RX);
16301633
@@ -1637,12 +1640,11 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br, · 6 unmodified lines
16371640 struct sk_buff *skb,
16381641 u16 vid)
16391642 {
1640− struct sk_buff *skb_trimmed = NULL;
16411643 const unsigned char *src;
16421644 struct mld_msg *mld;
16431645 int err;
16441646
1645− err = ipv6_mc_check_mld(skb, &skb_trimmed);
1647+ err = ipv6_mc_check_mld(skb);
16461648
16471649 if (err == -ENOMSG) {
16481650 if (!ipv6_addr_is_ll_all_nodes(&ipv6_hdr(skb)->daddr))
@@ -1664,10 +1666,10 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br, · 15 unmodified lines
16641666 src);
16651667 break;
16661668 case ICMPV6_MLD2_REPORT:
1667− err = br_ip6_multicast_mld2_report(br, port, skb_trimmed, vid);
1669+ err = br_ip6_multicast_mld2_report(br, port, skb, vid);
16681670 break;
16691671 case ICMPV6_MGM_QUERY:
1670− err = br_ip6_multicast_query(br, port, skb_trimmed, vid);
1672+ err = br_ip6_multicast_query(br, port, skb, vid);
16711673 break;
16721674 case ICMPV6_MGM_REDUCTION:
16731675 src = eth_hdr(skb)->h_source;
@@ -1675,9 +1677,6 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br, · 1 unmodified line
16751677 break;
16761678 }
16771679
1678− if (skb_trimmed && skb_trimmed != skb)
1679− kfree_skb(skb_trimmed);
1680−
16811680 br_multicast_count(br, port, skb, BR_INPUT_SKB_CB(skb)->igmp,
16821681 BR_MCAST_DIR_RX);
16831682
net/ipv4/igmp.c+4−19
@@ -1544,7 +1544,7 @@ static inline __sum16 ip_mc_validate_checksum(struct sk_buff *skb) · 1543 unmodified lines
15441544 return skb_checksum_simple_validate(skb);
15451545 }
15461546
1547−static int __ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed)
1547+static int __ip_mc_check_igmp(struct sk_buff *skb)
15481548
15491549 {
15501550 struct sk_buff *skb_chk;
@@ -1566,16 +1566,10 @@ static int __ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed) · 15 unmodified lines
15661566 if (ret)
15671567 goto err;
15681568
1569− if (skb_trimmed)
1570− *skb_trimmed = skb_chk;
1571− /* free now unneeded clone */
1572− else if (skb_chk != skb)
1573− kfree_skb(skb_chk);
1574−
15751569 ret = 0;
15761570
15771571 err:
1578− if (ret && skb_chk && skb_chk != skb)
1572+ if (skb_chk && skb_chk != skb)
15791573 kfree_skb(skb_chk);
15801574
15811575 return ret;
@@ -1584,7 +1578,6 @@ err: · 2 unmodified lines
15841578 /**
15851579 * ip_mc_check_igmp - checks whether this is a sane IGMP packet
15861580 * @skb: the skb to validate
1587− * @skb_trimmed: to store an skb pointer trimmed to IPv4 packet tail (optional)
15881581 *
15891582 * Checks whether an IPv4 packet is a valid IGMP packet. If so sets
15901583 * skb transport header accordingly and returns zero.
@@ -1594,18 +1587,10 @@ err: · 3 unmodified lines
15941587 * -ENOMSG: IP header validation succeeded but it is not an IGMP packet.
15951588 * -ENOMEM: A memory allocation failure happened.
15961589 *
1597− * Optionally, an skb pointer might be provided via skb_trimmed (or set it
1598− * to NULL): After parsing an IGMP packet successfully it will point to
1599− * an skb which has its tail aligned to the IP packet end. This might
1600− * either be the originally provided skb or a trimmed, cloned version if
1601− * the skb frame had data beyond the IP packet. A cloned skb allows us
1602− * to leave the original skb and its full frame unchanged (which might be
1603− * desirable for layer 2 frame jugglers).
1604− *
16051590 * Caller needs to set the skb network header and free any returned skb if it
16061591 * differs from the provided skb.
16071592 */
1608−int ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed)
1593+int ip_mc_check_igmp(struct sk_buff *skb)
16091594 {
16101595 int ret = ip_mc_check_iphdr(skb);
16111596
@@ -1615,7 +1600,7 @@ int ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed) · 3 unmodified lines
16151600 if (ip_hdr(skb)->protocol != IPPROTO_IGMP)
16161601 return -ENOMSG;
16171602
1618− return __ip_mc_check_igmp(skb, skb_trimmed);
1603+ return __ip_mc_check_igmp(skb);
16191604 }
16201605 EXPORT_SYMBOL(ip_mc_check_igmp);
16211606
net/ipv6/mcast_snoop.c+4−20
@@ -136,8 +136,7 @@ static inline __sum16 ipv6_mc_validate_checksum(struct sk_buff *skb) · 135 unmodified lines
136136 return skb_checksum_validate(skb, IPPROTO_ICMPV6, ip6_compute_pseudo);
137137 }
138138
139−static int __ipv6_mc_check_mld(struct sk_buff *skb,
140− struct sk_buff **skb_trimmed)
139+static int __ipv6_mc_check_mld(struct sk_buff *skb)
141140
142141 {
143142 struct sk_buff *skb_chk = NULL;
@@ -160,16 +159,10 @@ static int __ipv6_mc_check_mld(struct sk_buff *skb, · 16 unmodified lines
160159 if (ret)
161160 goto err;
162161
163− if (skb_trimmed)
164− *skb_trimmed = skb_chk;
165− /* free now unneeded clone */
166− else if (skb_chk != skb)
167− kfree_skb(skb_chk);
168−
169162 ret = 0;
170163
171164 err:
172− if (ret && skb_chk && skb_chk != skb)
165+ if (skb_chk && skb_chk != skb)
173166 kfree_skb(skb_chk);
174167
175168 return ret;
@@ -178,7 +171,6 @@ err: · 2 unmodified lines
178171 /**
179172 * ipv6_mc_check_mld - checks whether this is a sane MLD packet
180173 * @skb: the skb to validate
181− * @skb_trimmed: to store an skb pointer trimmed to IPv6 packet tail (optional)
182174 *
183175 * Checks whether an IPv6 packet is a valid MLD packet. If so sets
184176 * skb transport header accordingly and returns zero.
@@ -188,18 +180,10 @@ err: · 3 unmodified lines
188180 * -ENOMSG: IP header validation succeeded but it is not an MLD packet.
189181 * -ENOMEM: A memory allocation failure happened.
190182 *
191− * Optionally, an skb pointer might be provided via skb_trimmed (or set it
192− * to NULL): After parsing an MLD packet successfully it will point to
193− * an skb which has its tail aligned to the IP packet end. This might
194− * either be the originally provided skb or a trimmed, cloned version if
195− * the skb frame had data beyond the IP packet. A cloned skb allows us
196− * to leave the original skb and its full frame unchanged (which might be
197− * desirable for layer 2 frame jugglers).
198− *
199183 * Caller needs to set the skb network header and free any returned skb if it
200184 * differs from the provided skb.
201185 */
202−int ipv6_mc_check_mld(struct sk_buff *skb, struct sk_buff **skb_trimmed)
186+int ipv6_mc_check_mld(struct sk_buff *skb)
203187 {
204188 int ret;
205189
@@ -211,6 +195,6 @@ int ipv6_mc_check_mld(struct sk_buff *skb, struct sk_buff **skb_trimmed) · 5 unmodified lines
211195 if (ret < 0)
212196 return ret;
213197
214− return __ipv6_mc_check_mld(skb, skb_trimmed);
198+ return __ipv6_mc_check_mld(skb);
215199 }
216200 EXPORT_SYMBOL(ipv6_mc_check_mld);