FILE
nf_reject_ipv6.c
File preview with syntax highlighting.
linux-6.19.14/net/ipv6/netfilter/nf_reject_ipv6.c
1// SPDX-License-Identifier: GPL-2.0-only 2/* (C) 1999-2001 Paul `Rusty' Russell 3 * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> 4 */ 5 6#include <linux/module.h> 7#include <net/ipv6.h> 8#include <net/ip6_route.h> 9#include <net/ip6_fib.h> 10#include <net/ip6_checksum.h> 11#include <net/netfilter/ipv6/nf_reject.h> 12#include <linux/netfilter_ipv6.h> 13#include <linux/netfilter_bridge.h> 14 15static struct ipv6hdr * 16nf_reject_ip6hdr_put(struct sk_buff *nskb, 17 const struct sk_buff *oldskb, 18 __u8 protocol, int hoplimit); 19static void 20nf_reject_ip6_tcphdr_put(struct sk_buff *nskb, 21 const struct sk_buff *oldskb, 22 const struct tcphdr *oth, unsigned int otcplen); 23static const struct tcphdr * 24nf_reject_ip6_tcphdr_get(struct sk_buff *oldskb, 25 struct tcphdr *otcph, 26 unsigned int *otcplen, int hook); 27 28static bool nf_reject_v6_csum_ok(struct sk_buff *skb, int hook) 29{ 30 const struct ipv6hdr *ip6h = ipv6_hdr(skb); 31 int thoff; 32 __be16 fo; 33 u8 proto = ip6h->nexthdr; 34 35 if (skb_csum_unnecessary(skb)) 36 return true; 37 38 if (ip6h->payload_len && 39 pskb_trim_rcsum(skb, ntohs(ip6h->payload_len) + sizeof(*ip6h))) 40 return false; 41 42 ip6h = ipv6_hdr(skb); 43 thoff = ipv6_skip_exthdr(skb, ((u8*)(ip6h+1) - skb->data), &proto, &fo); 44 if (thoff < 0 || thoff >= skb->len || (fo & htons(~0x7)) != 0) 45 return false; 46 47 if (!nf_reject_verify_csum(skb, thoff, proto)) 48 return true; 49 50 return nf_ip6_checksum(skb, hook, thoff, proto) == 0; 51} 52 53static int nf_reject_ip6hdr_validate(struct sk_buff *skb) 54{ 55 struct ipv6hdr *hdr; 56 u32 pkt_len; 57 58 if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 59 return 0; 60 61 hdr = ipv6_hdr(skb); 62 if (hdr->version != 6) 63 return 0; 64 65 pkt_len = ntohs(hdr->payload_len); 66 if (pkt_len + sizeof(struct ipv6hdr) > skb->len) 67 return 0; 68 69 return 1; 70} 71 72struct sk_buff *nf_reject_skb_v6_tcp_reset(struct net *net, 73 struct sk_buff *oldskb, 74 const struct net_device *dev, 75 int hook) 76{ 77 struct sk_buff *nskb; 78 const struct tcphdr *oth; 79 struct tcphdr _oth; 80 unsigned int otcplen; 81 struct ipv6hdr *nip6h; 82 83 if (!nf_reject_ip6hdr_validate(oldskb)) 84 return NULL; 85 86 oth = nf_reject_ip6_tcphdr_get(oldskb, &_oth, &otcplen, hook); 87 if (!oth) 88 return NULL; 89 90 nskb = alloc_skb(sizeof(struct ipv6hdr) + sizeof(struct tcphdr) + 91 LL_MAX_HEADER, GFP_ATOMIC); 92 if (!nskb) 93 return NULL; 94 95 nskb->dev = (struct net_device *)dev; 96 97 skb_reserve(nskb, LL_MAX_HEADER); 98 nip6h = nf_reject_ip6hdr_put(nskb, oldskb, IPPROTO_TCP, 99 READ_ONCE(net->ipv6.devconf_all->hop_limit)); 100 nf_reject_ip6_tcphdr_put(nskb, oldskb, oth, otcplen); 101 nip6h->payload_len = htons(nskb->len - sizeof(struct ipv6hdr)); 102 103 return nskb; 104} 105EXPORT_SYMBOL_GPL(nf_reject_skb_v6_tcp_reset); 106 107static bool nf_skb_is_icmp6_unreach(const struct sk_buff *skb) 108{ 109 const struct ipv6hdr *ip6h = ipv6_hdr(skb); 110 u8 proto = ip6h->nexthdr; 111 u8 _type, *tp; 112 int thoff; 113 __be16 fo; 114 115 thoff = ipv6_skip_exthdr(skb, ((u8 *)(ip6h + 1) - skb->data), &proto, &fo); 116 117 if (thoff < 0 || thoff >= skb->len || fo != 0) 118 return false; 119 120 if (proto != IPPROTO_ICMPV6) 121 return false; 122 123 tp = skb_header_pointer(skb, 124 thoff + offsetof(struct icmp6hdr, icmp6_type), 125 sizeof(_type), &_type); 126 127 if (!tp) 128 return false; 129 130 return *tp == ICMPV6_DEST_UNREACH; 131} 132 133struct sk_buff *nf_reject_skb_v6_unreach(struct net *net, 134 struct sk_buff *oldskb, 135 const struct net_device *dev, 136 int hook, u8 code) 137{ 138 struct sk_buff *nskb; 139 struct ipv6hdr *nip6h; 140 struct icmp6hdr *icmp6h; 141 unsigned int len; 142 143 if (!nf_reject_ip6hdr_validate(oldskb)) 144 return NULL; 145 146 /* Don't reply to ICMPV6_DEST_UNREACH with ICMPV6_DEST_UNREACH */ 147 if (nf_skb_is_icmp6_unreach(oldskb)) 148 return NULL; 149 150 /* Include "As much of invoking packet as possible without the ICMPv6 151 * packet exceeding the minimum IPv6 MTU" in the ICMP payload. 152 */ 153 len = min_t(unsigned int, 1220, oldskb->len); 154 155 if (!pskb_may_pull(oldskb, len)) 156 return NULL; 157 158 if (!nf_reject_v6_csum_ok(oldskb, hook)) 159 return NULL; 160 161 nskb = alloc_skb(sizeof(struct ipv6hdr) + sizeof(struct icmp6hdr) + 162 LL_MAX_HEADER + len, GFP_ATOMIC); 163 if (!nskb) 164 return NULL; 165 166 nskb->dev = (struct net_device *)dev; 167 168 skb_reserve(nskb, LL_MAX_HEADER); 169 nip6h = nf_reject_ip6hdr_put(nskb, oldskb, IPPROTO_ICMPV6, 170 READ_ONCE(net->ipv6.devconf_all->hop_limit)); 171 172 skb_reset_transport_header(nskb); 173 icmp6h = skb_put_zero(nskb, sizeof(struct icmp6hdr)); 174 icmp6h->icmp6_type = ICMPV6_DEST_UNREACH; 175 icmp6h->icmp6_code = code; 176 177 skb_put_data(nskb, skb_network_header(oldskb), len); 178 nip6h->payload_len = htons(nskb->len - sizeof(struct ipv6hdr)); 179 180 icmp6h->icmp6_cksum = 181 csum_ipv6_magic(&nip6h->saddr, &nip6h->daddr, 182 nskb->len - sizeof(struct ipv6hdr), 183 IPPROTO_ICMPV6, 184 csum_partial(icmp6h, 185 nskb->len - sizeof(struct ipv6hdr), 186 0)); 187 188 return nskb; 189} 190EXPORT_SYMBOL_GPL(nf_reject_skb_v6_unreach); 191 192static const struct tcphdr * 193nf_reject_ip6_tcphdr_get(struct sk_buff *oldskb, 194 struct tcphdr *otcph, 195 unsigned int *otcplen, int hook) 196{ 197 const struct ipv6hdr *oip6h = ipv6_hdr(oldskb); 198 u8 proto; 199 __be16 frag_off; 200 int tcphoff; 201 202 proto = oip6h->nexthdr; 203 tcphoff = ipv6_skip_exthdr(oldskb, ((u8 *)(oip6h + 1) - oldskb->data), 204 &proto, &frag_off); 205 206 if ((tcphoff < 0) || (tcphoff > oldskb->len)) { 207 pr_debug("Cannot get TCP header.\n"); 208 return NULL; 209 } 210 211 *otcplen = oldskb->len - tcphoff; 212 213 /* IP header checks: fragment, too short. */ 214 if (proto != IPPROTO_TCP || *otcplen < sizeof(struct tcphdr)) { 215 pr_debug("proto(%d) != IPPROTO_TCP or too short (len = %d)\n", 216 proto, *otcplen); 217 return NULL; 218 } 219 220 otcph = skb_header_pointer(oldskb, tcphoff, sizeof(struct tcphdr), 221 otcph); 222 if (otcph == NULL) 223 return NULL; 224 225 /* No RST for RST. */ 226 if (otcph->rst) { 227 pr_debug("RST is set\n"); 228 return NULL; 229 } 230 231 /* Check checksum. */ 232 if (nf_ip6_checksum(oldskb, hook, tcphoff, IPPROTO_TCP)) { 233 pr_debug("TCP checksum is invalid\n"); 234 return NULL; 235 } 236 237 return otcph; 238} 239 240static struct ipv6hdr * 241nf_reject_ip6hdr_put(struct sk_buff *nskb, 242 const struct sk_buff *oldskb, 243 __u8 protocol, int hoplimit) 244{ 245 struct ipv6hdr *ip6h; 246 const struct ipv6hdr *oip6h = ipv6_hdr(oldskb); 247#define DEFAULT_TOS_VALUE 0x0U 248 const __u8 tclass = DEFAULT_TOS_VALUE; 249 250 skb_put(nskb, sizeof(struct ipv6hdr)); 251 skb_reset_network_header(nskb); 252 ip6h = ipv6_hdr(nskb); 253 ip6_flow_hdr(ip6h, tclass, 0); 254 ip6h->hop_limit = hoplimit; 255 ip6h->nexthdr = protocol; 256 ip6h->saddr = oip6h->daddr; 257 ip6h->daddr = oip6h->saddr; 258 259 nskb->protocol = htons(ETH_P_IPV6); 260 261 return ip6h; 262} 263 264static void 265nf_reject_ip6_tcphdr_put(struct sk_buff *nskb, 266 const struct sk_buff *oldskb, 267 const struct tcphdr *oth, unsigned int otcplen) 268{ 269 struct tcphdr *tcph; 270 271 skb_reset_transport_header(nskb); 272 tcph = skb_put_zero(nskb, sizeof(struct tcphdr)); 273 /* Truncate to length (no data) */ 274 tcph->doff = sizeof(struct tcphdr)/4; 275 tcph->source = oth->dest; 276 tcph->dest = oth->source; 277 278 if (oth->ack) { 279 tcph->seq = oth->ack_seq; 280 } else { 281 tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin + 282 otcplen - (oth->doff<<2)); 283 tcph->ack = 1; 284 } 285 286 tcph->rst = 1; 287 288 /* Adjust TCP checksum */ 289 tcph->check = csum_ipv6_magic(&ipv6_hdr(nskb)->saddr, 290 &ipv6_hdr(nskb)->daddr, 291 sizeof(struct tcphdr), IPPROTO_TCP, 292 csum_partial(tcph, 293 sizeof(struct tcphdr), 0)); 294} 295 296static int nf_reject6_fill_skb_dst(struct sk_buff *skb_in) 297{ 298 struct dst_entry *dst = NULL; 299 struct flowi fl; 300 301 memset(&fl, 0, sizeof(struct flowi)); 302 fl.u.ip6.daddr = ipv6_hdr(skb_in)->saddr; 303 nf_ip6_route(dev_net(skb_in->dev), &dst, &fl, false); 304 if (!dst) 305 return -1; 306 307 skb_dst_set(skb_in, dst); 308 return 0; 309} 310 311void nf_send_reset6(struct net *net, struct sock *sk, struct sk_buff *oldskb, 312 int hook) 313{ 314 const struct ipv6hdr *oip6h = ipv6_hdr(oldskb); 315 struct dst_entry *dst = NULL; 316 const struct tcphdr *otcph; 317 struct sk_buff *nskb; 318 struct tcphdr _otcph; 319 unsigned int otcplen; 320 struct flowi6 fl6; 321 322 if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) || 323 (!(ipv6_addr_type(&oip6h->daddr) & IPV6_ADDR_UNICAST))) { 324 pr_debug("addr is not unicast.\n"); 325 return; 326 } 327 328 otcph = nf_reject_ip6_tcphdr_get(oldskb, &_otcph, &otcplen, hook); 329 if (!otcph) 330 return; 331 332 memset(&fl6, 0, sizeof(fl6)); 333 fl6.flowi6_proto = IPPROTO_TCP; 334 fl6.saddr = oip6h->daddr; 335 fl6.daddr = oip6h->saddr; 336 fl6.fl6_sport = otcph->dest; 337 fl6.fl6_dport = otcph->source; 338 339 if (!skb_dst(oldskb)) { 340 nf_ip6_route(net, &dst, flowi6_to_flowi(&fl6), false); 341 if (!dst) 342 return; 343 skb_dst_set(oldskb, dst); 344 } 345 346 fl6.flowi6_oif = l3mdev_master_ifindex(skb_dst_dev(oldskb)); 347 fl6.flowi6_mark = IP6_REPLY_MARK(net, oldskb->mark); 348 security_skb_classify_flow(oldskb, flowi6_to_flowi_common(&fl6)); 349 dst = ip6_route_output(net, NULL, &fl6); 350 if (dst->error) { 351 dst_release(dst); 352 return; 353 } 354 dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0); 355 if (IS_ERR(dst)) 356 return; 357 358 nskb = alloc_skb(LL_MAX_HEADER + sizeof(struct ipv6hdr) + 359 sizeof(struct tcphdr) + dst->trailer_len, 360 GFP_ATOMIC); 361 362 if (!nskb) { 363 net_dbg_ratelimited("cannot alloc skb\n"); 364 dst_release(dst); 365 return; 366 } 367 368 skb_dst_set(nskb, dst); 369 370 nskb->mark = fl6.flowi6_mark; 371 372 skb_reserve(nskb, LL_MAX_HEADER); 373 nf_reject_ip6hdr_put(nskb, oldskb, IPPROTO_TCP, ip6_dst_hoplimit(dst)); 374 nf_reject_ip6_tcphdr_put(nskb, oldskb, otcph, otcplen); 375 376 nf_ct_attach(nskb, oldskb); 377 nf_ct_set_closing(skb_nfct(oldskb)); 378 379#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) 380 /* If we use ip6_local_out for bridged traffic, the MAC source on 381 * the RST will be ours, instead of the destination's. This confuses 382 * some routers/firewalls, and they drop the packet. So we need to 383 * build the eth header using the original destination's MAC as the 384 * source, and send the RST packet directly. 385 */ 386 if (nf_bridge_info_exists(oldskb)) { 387 struct ethhdr *oeth = eth_hdr(oldskb); 388 struct ipv6hdr *ip6h = ipv6_hdr(nskb); 389 struct net_device *br_indev; 390 391 br_indev = nf_bridge_get_physindev(oldskb, net); 392 if (!br_indev) { 393 kfree_skb(nskb); 394 return; 395 } 396 397 nskb->dev = br_indev; 398 nskb->protocol = htons(ETH_P_IPV6); 399 ip6h->payload_len = htons(sizeof(struct tcphdr)); 400 if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol), 401 oeth->h_source, oeth->h_dest, nskb->len) < 0) { 402 kfree_skb(nskb); 403 return; 404 } 405 dev_queue_xmit(nskb); 406 } else 407#endif 408 ip6_local_out(net, sk, nskb); 409} 410EXPORT_SYMBOL_GPL(nf_send_reset6); 411 412static bool reject6_csum_ok(struct sk_buff *skb, int hook) 413{ 414 const struct ipv6hdr *ip6h = ipv6_hdr(skb); 415 int thoff; 416 __be16 fo; 417 u8 proto; 418 419 if (skb_csum_unnecessary(skb)) 420 return true; 421 422 proto = ip6h->nexthdr; 423 thoff = ipv6_skip_exthdr(skb, ((u8 *)(ip6h + 1) - skb->data), &proto, &fo); 424 425 if (thoff < 0 || thoff >= skb->len || (fo & htons(~0x7)) != 0) 426 return false; 427 428 if (!nf_reject_verify_csum(skb, thoff, proto)) 429 return true; 430 431 return nf_ip6_checksum(skb, hook, thoff, proto) == 0; 432} 433 434void nf_send_unreach6(struct net *net, struct sk_buff *skb_in, 435 unsigned char code, unsigned int hooknum) 436{ 437 if (!reject6_csum_ok(skb_in, hooknum)) 438 return; 439 440 if (hooknum == NF_INET_LOCAL_OUT && skb_in->dev == NULL) 441 skb_in->dev = net->loopback_dev; 442 443 if (!skb_dst(skb_in) && nf_reject6_fill_skb_dst(skb_in) < 0) 444 return; 445 446 icmpv6_send(skb_in, ICMPV6_DEST_UNREACH, code, 0); 447} 448EXPORT_SYMBOL_GPL(nf_send_unreach6); 449 450MODULE_LICENSE("GPL"); 451MODULE_DESCRIPTION("IPv6 packet rejection core");
~/
Read-only file preview.