1 // SPDX-License-Identifier: GPL-2.0-only << 2 /* Copyright (C) 2010: YOSHIFUJI Hideaki <yosh 1 /* Copyright (C) 2010: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org> 3 * Copyright (C) 2015: Linus Lüssing <linus.l 2 * Copyright (C) 2015: Linus Lüssing <linus.luessing@c0d3.blue> 4 * 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 * 5 * Based on the MLD support added to br_multic 17 * Based on the MLD support added to br_multicast.c by YOSHIFUJI Hideaki. 6 */ 18 */ 7 19 8 #include <linux/skbuff.h> 20 #include <linux/skbuff.h> 9 #include <net/ipv6.h> 21 #include <net/ipv6.h> 10 #include <net/mld.h> 22 #include <net/mld.h> 11 #include <net/addrconf.h> 23 #include <net/addrconf.h> 12 #include <net/ip6_checksum.h> 24 #include <net/ip6_checksum.h> 13 25 14 static int ipv6_mc_check_ip6hdr(struct sk_buff 26 static int ipv6_mc_check_ip6hdr(struct sk_buff *skb) 15 { 27 { 16 const struct ipv6hdr *ip6h; 28 const struct ipv6hdr *ip6h; 17 unsigned int len; 29 unsigned int len; 18 unsigned int offset = skb_network_offs 30 unsigned int offset = skb_network_offset(skb) + sizeof(*ip6h); 19 31 20 if (!pskb_may_pull(skb, offset)) 32 if (!pskb_may_pull(skb, offset)) 21 return -EINVAL; 33 return -EINVAL; 22 34 23 ip6h = ipv6_hdr(skb); 35 ip6h = ipv6_hdr(skb); 24 36 25 if (ip6h->version != 6) 37 if (ip6h->version != 6) 26 return -EINVAL; 38 return -EINVAL; 27 39 28 len = offset + ntohs(ip6h->payload_len 40 len = offset + ntohs(ip6h->payload_len); 29 if (skb->len < len || len <= offset) 41 if (skb->len < len || len <= offset) 30 return -EINVAL; 42 return -EINVAL; 31 43 32 skb_set_transport_header(skb, offset); << 33 << 34 return 0; 44 return 0; 35 } 45 } 36 46 37 static int ipv6_mc_check_exthdrs(struct sk_buf 47 static int ipv6_mc_check_exthdrs(struct sk_buff *skb) 38 { 48 { 39 const struct ipv6hdr *ip6h; 49 const struct ipv6hdr *ip6h; 40 int offset; 50 int offset; 41 u8 nexthdr; 51 u8 nexthdr; 42 __be16 frag_off; 52 __be16 frag_off; 43 53 44 ip6h = ipv6_hdr(skb); 54 ip6h = ipv6_hdr(skb); 45 55 46 if (ip6h->nexthdr != IPPROTO_HOPOPTS) 56 if (ip6h->nexthdr != IPPROTO_HOPOPTS) 47 return -ENOMSG; 57 return -ENOMSG; 48 58 49 nexthdr = ip6h->nexthdr; 59 nexthdr = ip6h->nexthdr; 50 offset = skb_network_offset(skb) + siz 60 offset = skb_network_offset(skb) + sizeof(*ip6h); 51 offset = ipv6_skip_exthdr(skb, offset, 61 offset = ipv6_skip_exthdr(skb, offset, &nexthdr, &frag_off); 52 62 53 if (offset < 0) 63 if (offset < 0) 54 return -EINVAL; 64 return -EINVAL; 55 65 56 if (nexthdr != IPPROTO_ICMPV6) 66 if (nexthdr != IPPROTO_ICMPV6) 57 return -ENOMSG; 67 return -ENOMSG; 58 68 59 skb_set_transport_header(skb, offset); 69 skb_set_transport_header(skb, offset); 60 70 61 return 0; 71 return 0; 62 } 72 } 63 73 64 static int ipv6_mc_check_mld_reportv2(struct s 74 static int ipv6_mc_check_mld_reportv2(struct sk_buff *skb) 65 { 75 { 66 unsigned int len = skb_transport_offse 76 unsigned int len = skb_transport_offset(skb); 67 77 68 len += sizeof(struct mld2_report); 78 len += sizeof(struct mld2_report); 69 79 70 return ipv6_mc_may_pull(skb, len) ? 0 !! 80 return pskb_may_pull(skb, len) ? 0 : -EINVAL; 71 } 81 } 72 82 73 static int ipv6_mc_check_mld_query(struct sk_b 83 static int ipv6_mc_check_mld_query(struct sk_buff *skb) 74 { 84 { 75 unsigned int transport_len = ipv6_tran << 76 struct mld_msg *mld; 85 struct mld_msg *mld; 77 unsigned int len; !! 86 unsigned int len = skb_transport_offset(skb); 78 87 79 /* RFC2710+RFC3810 (MLDv1+MLDv2) requi 88 /* RFC2710+RFC3810 (MLDv1+MLDv2) require link-local source addresses */ 80 if (!(ipv6_addr_type(&ipv6_hdr(skb)->s 89 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) 81 return -EINVAL; 90 return -EINVAL; 82 91 >> 92 len += sizeof(struct mld_msg); >> 93 if (skb->len < len) >> 94 return -EINVAL; >> 95 83 /* MLDv1? */ 96 /* MLDv1? */ 84 if (transport_len != sizeof(struct mld !! 97 if (skb->len != len) { 85 /* or MLDv2? */ 98 /* or MLDv2? */ 86 if (transport_len < sizeof(str !! 99 len += sizeof(struct mld2_query) - sizeof(struct mld_msg); 87 return -EINVAL; !! 100 if (skb->len < len || !pskb_may_pull(skb, len)) 88 << 89 len = skb_transport_offset(skb << 90 if (!ipv6_mc_may_pull(skb, len << 91 return -EINVAL; 101 return -EINVAL; 92 } 102 } 93 103 94 mld = (struct mld_msg *)skb_transport_ 104 mld = (struct mld_msg *)skb_transport_header(skb); 95 105 96 /* RFC2710+RFC3810 (MLDv1+MLDv2) requi 106 /* RFC2710+RFC3810 (MLDv1+MLDv2) require the multicast link layer 97 * all-nodes destination address (ff02 107 * all-nodes destination address (ff02::1) for general queries 98 */ 108 */ 99 if (ipv6_addr_any(&mld->mld_mca) && 109 if (ipv6_addr_any(&mld->mld_mca) && 100 !ipv6_addr_is_ll_all_nodes(&ipv6_h 110 !ipv6_addr_is_ll_all_nodes(&ipv6_hdr(skb)->daddr)) 101 return -EINVAL; 111 return -EINVAL; 102 112 103 return 0; 113 return 0; 104 } 114 } 105 115 106 static int ipv6_mc_check_mld_msg(struct sk_buf 116 static int ipv6_mc_check_mld_msg(struct sk_buff *skb) 107 { 117 { 108 unsigned int len = skb_transport_offse !! 118 struct mld_msg *mld = (struct mld_msg *)skb_transport_header(skb); 109 struct mld_msg *mld; << 110 << 111 if (!ipv6_mc_may_pull(skb, len)) << 112 return -ENODATA; << 113 << 114 mld = (struct mld_msg *)skb_transport_ << 115 119 116 switch (mld->mld_type) { 120 switch (mld->mld_type) { 117 case ICMPV6_MGM_REDUCTION: 121 case ICMPV6_MGM_REDUCTION: 118 case ICMPV6_MGM_REPORT: 122 case ICMPV6_MGM_REPORT: >> 123 /* fall through */ 119 return 0; 124 return 0; 120 case ICMPV6_MLD2_REPORT: 125 case ICMPV6_MLD2_REPORT: 121 return ipv6_mc_check_mld_repor 126 return ipv6_mc_check_mld_reportv2(skb); 122 case ICMPV6_MGM_QUERY: 127 case ICMPV6_MGM_QUERY: 123 return ipv6_mc_check_mld_query 128 return ipv6_mc_check_mld_query(skb); 124 default: 129 default: 125 return -ENODATA; !! 130 return -ENOMSG; 126 } 131 } 127 } 132 } 128 133 129 static inline __sum16 ipv6_mc_validate_checksu 134 static inline __sum16 ipv6_mc_validate_checksum(struct sk_buff *skb) 130 { 135 { 131 return skb_checksum_validate(skb, IPPR 136 return skb_checksum_validate(skb, IPPROTO_ICMPV6, ip6_compute_pseudo); 132 } 137 } 133 138 134 static int ipv6_mc_check_icmpv6(struct sk_buff !! 139 static int __ipv6_mc_check_mld(struct sk_buff *skb, >> 140 struct sk_buff **skb_trimmed) >> 141 135 { 142 { 136 unsigned int len = skb_transport_offse !! 143 struct sk_buff *skb_chk = NULL; 137 unsigned int transport_len = ipv6_tran !! 144 unsigned int transport_len; 138 struct sk_buff *skb_chk; !! 145 unsigned int len = skb_transport_offset(skb) + sizeof(struct mld_msg); >> 146 int ret = -EINVAL; 139 147 140 if (!ipv6_mc_may_pull(skb, len)) !! 148 transport_len = ntohs(ipv6_hdr(skb)->payload_len); 141 return -EINVAL; !! 149 transport_len -= skb_transport_offset(skb) - sizeof(struct ipv6hdr); 142 150 143 skb_chk = skb_checksum_trimmed(skb, tr 151 skb_chk = skb_checksum_trimmed(skb, transport_len, 144 ipv6_mc 152 ipv6_mc_validate_checksum); 145 if (!skb_chk) 153 if (!skb_chk) 146 return -EINVAL; !! 154 goto err; 147 155 148 if (skb_chk != skb) !! 156 if (!pskb_may_pull(skb_chk, len)) >> 157 goto err; >> 158 >> 159 ret = ipv6_mc_check_mld_msg(skb_chk); >> 160 if (ret) >> 161 goto err; >> 162 >> 163 if (skb_trimmed) >> 164 *skb_trimmed = skb_chk; >> 165 /* free now unneeded clone */ >> 166 else if (skb_chk != skb) 149 kfree_skb(skb_chk); 167 kfree_skb(skb_chk); 150 168 151 return 0; !! 169 ret = 0; >> 170 >> 171 err: >> 172 if (ret && skb_chk && skb_chk != skb) >> 173 kfree_skb(skb_chk); >> 174 >> 175 return ret; 152 } 176 } 153 177 154 /** 178 /** 155 * ipv6_mc_check_mld - checks whether this is 179 * ipv6_mc_check_mld - checks whether this is a sane MLD packet 156 * @skb: the skb to validate 180 * @skb: the skb to validate >> 181 * @skb_trimmed: to store an skb pointer trimmed to IPv6 packet tail (optional) 157 * 182 * 158 * Checks whether an IPv6 packet is a valid ML 183 * Checks whether an IPv6 packet is a valid MLD packet. If so sets 159 * skb transport header accordingly and return 184 * skb transport header accordingly and returns zero. 160 * 185 * 161 * -EINVAL: A broken packet was detected, i.e. 186 * -EINVAL: A broken packet was detected, i.e. it violates some internet 162 * standard 187 * standard 163 * -ENOMSG: IP header validation succeeded but !! 188 * -ENOMSG: IP header validation succeeded but it is not an MLD packet. 164 * with a hop-by-hop option. << 165 * -ENODATA: IP+ICMPv6 header with hop-by-hop << 166 * but it is not an MLD packet. << 167 * -ENOMEM: A memory allocation failure happen 189 * -ENOMEM: A memory allocation failure happened. 168 * 190 * >> 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 * 169 * Caller needs to set the skb network header 199 * Caller needs to set the skb network header and free any returned skb if it 170 * differs from the provided skb. 200 * differs from the provided skb. 171 */ 201 */ 172 int ipv6_mc_check_mld(struct sk_buff *skb) !! 202 int ipv6_mc_check_mld(struct sk_buff *skb, struct sk_buff **skb_trimmed) 173 { 203 { 174 int ret; 204 int ret; 175 205 176 ret = ipv6_mc_check_ip6hdr(skb); 206 ret = ipv6_mc_check_ip6hdr(skb); 177 if (ret < 0) 207 if (ret < 0) 178 return ret; 208 return ret; 179 209 180 ret = ipv6_mc_check_exthdrs(skb); 210 ret = ipv6_mc_check_exthdrs(skb); 181 if (ret < 0) 211 if (ret < 0) 182 return ret; 212 return ret; 183 213 184 ret = ipv6_mc_check_icmpv6(skb); !! 214 return __ipv6_mc_check_mld(skb, skb_trimmed); 185 if (ret < 0) << 186 return ret; << 187 << 188 return ipv6_mc_check_mld_msg(skb); << 189 } 215 } 190 EXPORT_SYMBOL(ipv6_mc_check_mld); 216 EXPORT_SYMBOL(ipv6_mc_check_mld); 191 217
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.