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