| 1 | |
| 2 | |
| 3 | * IPv6 specific functions of netfilter core
|
| 4 | *
|
| 5 | * Rusty Russell (C) 2000
|
| 6 | * Patrick McHardy (C) 2006-2012
|
| 7 |
|
| 8 | #include <linux/kernel.h>
|
| 9 | #include <linux/init.h>
|
| 10 | #include <linux/ipv6.h>
|
| 11 | #include <linux/netfilter.h>
|
| 12 | #include <linux/netfilter_ipv6.h>
|
| 13 | #include <linux/export.h>
|
| 14 | #include <net/addrconf.h>
|
| 15 | #include <net/dst.h>
|
| 16 | #include <net/ipv6.h>
|
| 17 | #include <net/ip6_route.h>
|
| 18 | #include <net/xfrm.h>
|
| 19 | #include <net/netfilter/nf_queue.h>
|
| 20 | #include <net/netfilter/nf_conntrack_bridge.h>
|
| 21 | #include <net/netfilter/ipv6/nf_defrag_ipv6.h>
|
| 22 | #include "../bridge/br_private.h"
|
| 23 |
|
| 24 | int ip6_route_me_harder(struct net *net, struct sock *sk_partial, struct sk_buff *skb)
|
| 25 | {
|
| 26 | const struct ipv6hdr *iph = ipv6_hdr(skb);
|
| 27 | struct sock *sk = sk_to_full_sk(sk_partial);
|
| 28 | struct net_device *dev = skb_dst_dev(skb);
|
| 29 | struct flow_keys flkeys;
|
| 30 | unsigned int hh_len;
|
| 31 | struct dst_entry *dst;
|
| 32 | int strict = (ipv6_addr_type(&iph->daddr) &
|
| 33 | (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL));
|
| 34 | struct flowi6 fl6 = {
|
| 35 | .flowi6_l3mdev = l3mdev_master_ifindex(dev),
|
| 36 | .flowi6_mark = skb->mark,
|
| 37 | .flowi6_uid = sock_net_uid(net, sk),
|
| 38 | .daddr = iph->daddr,
|
| 39 | .saddr = iph->saddr,
|
| 40 | .flowlabel = ip6_flowinfo(iph),
|
| 41 | };
|
| 42 | int err;
|
| 43 |
|
| 44 | if (sk && sk->sk_bound_dev_if)
|
| 45 | fl6.flowi6_oif = sk->sk_bound_dev_if;
|
| 46 | else if (strict)
|
| 47 | fl6.flowi6_oif = dev->ifindex;
|
| 48 |
|
| 49 | fib6_rules_early_flow_dissect(net, skb, &fl6, &flkeys);
|
| 50 | dst = ip6_route_output(net, sk, &fl6);
|
| 51 | err = dst->error;
|
| 52 | if (err) {
|
| 53 | IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
|
| 54 | net_dbg_ratelimited("ip6_route_me_harder: No more route\n");
|
| 55 | dst_release(dst);
|
| 56 | return err;
|
| 57 | }
|
| 58 |
|
| 59 |
|
| 60 | skb_dst_drop(skb);
|
| 61 |
|
| 62 | skb_dst_set(skb, dst);
|
| 63 |
|
| 64 | #ifdef CONFIG_XFRM
|
| 65 | if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
|
| 66 | xfrm_decode_session(net, skb, flowi6_to_flowi(&fl6), AF_INET6) == 0) {
|
| 67 | |
| 68 | * care of dropping the refcnt if needed.
|
| 69 |
|
| 70 | skb_dstref_steal(skb);
|
| 71 | dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), sk, 0);
|
| 72 | if (IS_ERR(dst))
|
| 73 | return PTR_ERR(dst);
|
| 74 | skb_dst_set(skb, dst);
|
| 75 | }
|
| 76 | #endif
|
| 77 |
|
| 78 |
|
| 79 | hh_len = skb_dst_dev(skb)->hard_header_len;
|
| 80 | if (skb_headroom(skb) < hh_len &&
|
| 81 | pskb_expand_head(skb, HH_DATA_ALIGN(hh_len - skb_headroom(skb)),
|
| 82 | 0, GFP_ATOMIC))
|
| 83 | return -ENOMEM;
|
| 84 |
|
| 85 | return 0;
|
| 86 | }
|
| 87 | EXPORT_SYMBOL(ip6_route_me_harder);
|
| 88 |
|
| 89 | int __nf_ip6_route(struct net *net, struct dst_entry **dst,
|
| 90 | struct flowi *fl, bool strict)
|
| 91 | {
|
| 92 | static const struct ipv6_pinfo fake_pinfo;
|
| 93 | static const struct inet_sock fake_sk = {
|
| 94 |
|
| 95 | .sk.sk_bound_dev_if = 1,
|
| 96 | .pinet6 = (struct ipv6_pinfo *) &fake_pinfo,
|
| 97 | };
|
| 98 | const void *sk = strict ? &fake_sk : NULL;
|
| 99 | struct dst_entry *result;
|
| 100 | int err;
|
| 101 |
|
| 102 | result = ip6_route_output(net, sk, &fl->u.ip6);
|
| 103 | err = result->error;
|
| 104 | if (err)
|
| 105 | dst_release(result);
|
| 106 | else
|
| 107 | *dst = result;
|
| 108 | return err;
|
| 109 | }
|
| 110 | EXPORT_SYMBOL_GPL(__nf_ip6_route);
|
| 111 |
|
| 112 | int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
|
| 113 | struct nf_bridge_frag_data *data,
|
| 114 | int (*output)(struct net *, struct sock *sk,
|
| 115 | const struct nf_bridge_frag_data *data,
|
| 116 | struct sk_buff *))
|
| 117 | {
|
| 118 | int frag_max_size = BR_INPUT_SKB_CB(skb)->frag_max_size;
|
| 119 | u8 tstamp_type = skb->tstamp_type;
|
| 120 | ktime_t tstamp = skb->tstamp;
|
| 121 | struct ip6_frag_state state;
|
| 122 | u8 *prevhdr, nexthdr = 0;
|
| 123 | unsigned int mtu, hlen, nexthdr_offset;
|
| 124 | int hroom, err = 0;
|
| 125 | __be32 frag_id;
|
| 126 |
|
| 127 | err = ip6_find_1stfragopt(skb, &prevhdr);
|
| 128 | if (err < 0)
|
| 129 | goto blackhole;
|
| 130 | hlen = err;
|
| 131 | nexthdr = *prevhdr;
|
| 132 | nexthdr_offset = prevhdr - skb_network_header(skb);
|
| 133 |
|
| 134 | mtu = skb->dev->mtu;
|
| 135 | if (frag_max_size > mtu ||
|
| 136 | frag_max_size < IPV6_MIN_MTU)
|
| 137 | goto blackhole;
|
| 138 |
|
| 139 | mtu = frag_max_size;
|
| 140 | if (mtu < hlen + sizeof(struct frag_hdr) + 8)
|
| 141 | goto blackhole;
|
| 142 | mtu -= hlen + sizeof(struct frag_hdr);
|
| 143 |
|
| 144 | frag_id = ipv6_select_ident(net, &ipv6_hdr(skb)->daddr,
|
| 145 | &ipv6_hdr(skb)->saddr);
|
| 146 |
|
| 147 | if (skb->ip_summed == CHECKSUM_PARTIAL &&
|
| 148 | (err = skb_checksum_help(skb)))
|
| 149 | goto blackhole;
|
| 150 |
|
| 151 | prevhdr = skb_network_header(skb) + nexthdr_offset;
|
| 152 | hroom = LL_RESERVED_SPACE(skb->dev);
|
| 153 | if (skb_has_frag_list(skb)) {
|
| 154 | unsigned int first_len = skb_pagelen(skb);
|
| 155 | struct ip6_fraglist_iter iter;
|
| 156 | struct sk_buff *frag2;
|
| 157 |
|
| 158 | if (first_len - hlen > mtu)
|
| 159 | goto blackhole;
|
| 160 |
|
| 161 | if (skb_cloned(skb) ||
|
| 162 | skb_headroom(skb) < (hroom + sizeof(struct frag_hdr)))
|
| 163 | goto slow_path;
|
| 164 |
|
| 165 | skb_walk_frags(skb, frag2) {
|
| 166 | if (frag2->len > mtu)
|
| 167 | goto blackhole;
|
| 168 |
|
| 169 |
|
| 170 | if (skb_shared(frag2) ||
|
| 171 | skb_headroom(frag2) < (hlen + hroom + sizeof(struct frag_hdr)))
|
| 172 | goto slow_path;
|
| 173 | }
|
| 174 |
|
| 175 | err = ip6_fraglist_init(skb, hlen, prevhdr, nexthdr, frag_id,
|
| 176 | &iter);
|
| 177 | if (err < 0)
|
| 178 | goto blackhole;
|
| 179 |
|
| 180 | for (;;) {
|
| 181 | |
| 182 | * before previous one went down.
|
| 183 |
|
| 184 | if (iter.frag)
|
| 185 | ip6_fraglist_prepare(skb, &iter);
|
| 186 |
|
| 187 | skb_set_delivery_time(skb, tstamp, tstamp_type);
|
| 188 | err = output(net, sk, data, skb);
|
| 189 | if (err || !iter.frag)
|
| 190 | break;
|
| 191 |
|
| 192 | skb = ip6_fraglist_next(&iter);
|
| 193 | }
|
| 194 |
|
| 195 | kfree(iter.tmp_hdr);
|
| 196 | if (!err)
|
| 197 | return 0;
|
| 198 |
|
| 199 | kfree_skb_list(iter.frag);
|
| 200 | return err;
|
| 201 | }
|
| 202 | slow_path:
|
| 203 | |
| 204 | * This may also be a clone skbuff, we could preserve the geometry for
|
| 205 | * the copies but probably not worth the effort.
|
| 206 |
|
| 207 | ip6_frag_init(skb, hlen, mtu, skb->dev->needed_tailroom,
|
| 208 | LL_RESERVED_SPACE(skb->dev), prevhdr, nexthdr, frag_id,
|
| 209 | &state);
|
| 210 |
|
| 211 | while (state.left > 0) {
|
| 212 | struct sk_buff *skb2;
|
| 213 |
|
| 214 | skb2 = ip6_frag_next(skb, &state);
|
| 215 | if (IS_ERR(skb2)) {
|
| 216 | err = PTR_ERR(skb2);
|
| 217 | goto blackhole;
|
| 218 | }
|
| 219 |
|
| 220 | skb_set_delivery_time(skb2, tstamp, tstamp_type);
|
| 221 | err = output(net, sk, data, skb2);
|
| 222 | if (err)
|
| 223 | goto blackhole;
|
| 224 | }
|
| 225 | consume_skb(skb);
|
| 226 | return err;
|
| 227 |
|
| 228 | blackhole:
|
| 229 | kfree_skb(skb);
|
| 230 | return 0;
|
| 231 | }
|
| 232 | EXPORT_SYMBOL_GPL(br_ip6_fragment);
|