Branch data Line data Source code
1 : : /* 2 : : * IPv6 specific functions of netfilter core 3 : : * 4 : : * Rusty Russell (C) 2000 -- This code is GPL. 5 : : * Patrick McHardy (C) 2006-2012 6 : : */ 7 : : #include <linux/kernel.h> 8 : : #include <linux/init.h> 9 : : #include <linux/ipv6.h> 10 : : #include <linux/netfilter.h> 11 : : #include <linux/netfilter_ipv6.h> 12 : : #include <linux/export.h> 13 : : #include <net/addrconf.h> 14 : : #include <net/dst.h> 15 : : #include <net/ipv6.h> 16 : : #include <net/ip6_route.h> 17 : : #include <net/xfrm.h> 18 : : #include <net/netfilter/nf_queue.h> 19 : : #include <net/netfilter/nf_conntrack_bridge.h> 20 : : #include <net/netfilter/ipv6/nf_defrag_ipv6.h> 21 : : #include "../bridge/br_private.h" 22 : : 23 : 0 : int ip6_route_me_harder(struct net *net, struct sk_buff *skb) 24 : : { 25 : : const struct ipv6hdr *iph = ipv6_hdr(skb); 26 : 0 : struct sock *sk = sk_to_full_sk(skb->sk); 27 : : unsigned int hh_len; 28 : : struct dst_entry *dst; 29 : 0 : int strict = (ipv6_addr_type(&iph->daddr) & 30 : : (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)); 31 : 0 : struct flowi6 fl6 = { 32 : 0 : .flowi6_oif = sk && sk->sk_bound_dev_if ? sk->sk_bound_dev_if : 33 : 0 : strict ? skb_dst(skb)->dev->ifindex : 0, 34 : 0 : .flowi6_mark = skb->mark, 35 : : .flowi6_uid = sock_net_uid(net, sk), 36 : : .daddr = iph->daddr, 37 : : .saddr = iph->saddr, 38 : : }; 39 : : int err; 40 : : 41 : : dst = ip6_route_output(net, sk, &fl6); 42 : 0 : err = dst->error; 43 : 0 : if (err) { 44 : 0 : IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES); 45 : : net_dbg_ratelimited("ip6_route_me_harder: No more route\n"); 46 : 0 : dst_release(dst); 47 : 0 : return err; 48 : : } 49 : : 50 : : /* Drop old route. */ 51 : 0 : skb_dst_drop(skb); 52 : : 53 : : skb_dst_set(skb, dst); 54 : : 55 : : #ifdef CONFIG_XFRM 56 : 0 : if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) && 57 : : xfrm_decode_session(skb, flowi6_to_flowi(&fl6), AF_INET6) == 0) { 58 : : skb_dst_set(skb, NULL); 59 : 0 : dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), sk, 0); 60 : 0 : if (IS_ERR(dst)) 61 : 0 : return PTR_ERR(dst); 62 : : skb_dst_set(skb, dst); 63 : : } 64 : : #endif 65 : : 66 : : /* Change in oif may mean change in hh_len. */ 67 : 0 : hh_len = skb_dst(skb)->dev->hard_header_len; 68 : 0 : if (skb_headroom(skb) < hh_len && 69 : 0 : pskb_expand_head(skb, HH_DATA_ALIGN(hh_len - skb_headroom(skb)), 70 : : 0, GFP_ATOMIC)) 71 : : return -ENOMEM; 72 : : 73 : : return 0; 74 : : } 75 : : EXPORT_SYMBOL(ip6_route_me_harder); 76 : : 77 : 0 : static int nf_ip6_reroute(struct sk_buff *skb, 78 : : const struct nf_queue_entry *entry) 79 : : { 80 : : struct ip6_rt_info *rt_info = nf_queue_entry_reroute(entry); 81 : : 82 : 0 : if (entry->state.hook == NF_INET_LOCAL_OUT) { 83 : : const struct ipv6hdr *iph = ipv6_hdr(skb); 84 : 0 : if (!ipv6_addr_equal(&iph->daddr, &rt_info->daddr) || 85 : 0 : !ipv6_addr_equal(&iph->saddr, &rt_info->saddr) || 86 : 0 : skb->mark != rt_info->mark) 87 : 0 : return ip6_route_me_harder(entry->state.net, skb); 88 : : } 89 : : return 0; 90 : : } 91 : : 92 : 0 : int __nf_ip6_route(struct net *net, struct dst_entry **dst, 93 : : struct flowi *fl, bool strict) 94 : : { 95 : : static const struct ipv6_pinfo fake_pinfo; 96 : : static const struct inet_sock fake_sk = { 97 : : /* makes ip6_route_output set RT6_LOOKUP_F_IFACE: */ 98 : : .sk.sk_bound_dev_if = 1, 99 : : .pinet6 = (struct ipv6_pinfo *) &fake_pinfo, 100 : : }; 101 : 0 : const void *sk = strict ? &fake_sk : NULL; 102 : : struct dst_entry *result; 103 : : int err; 104 : : 105 : 0 : result = ip6_route_output(net, sk, &fl->u.ip6); 106 : 0 : err = result->error; 107 : 0 : if (err) 108 : 0 : dst_release(result); 109 : : else 110 : 0 : *dst = result; 111 : 0 : return err; 112 : : } 113 : : EXPORT_SYMBOL_GPL(__nf_ip6_route); 114 : : 115 : 0 : int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, 116 : : struct nf_bridge_frag_data *data, 117 : : int (*output)(struct net *, struct sock *sk, 118 : : const struct nf_bridge_frag_data *data, 119 : : struct sk_buff *)) 120 : : { 121 : 0 : int frag_max_size = BR_INPUT_SKB_CB(skb)->frag_max_size; 122 : 0 : ktime_t tstamp = skb->tstamp; 123 : : struct ip6_frag_state state; 124 : : u8 *prevhdr, nexthdr = 0; 125 : : unsigned int mtu, hlen; 126 : : int hroom, err = 0; 127 : : __be32 frag_id; 128 : : 129 : 0 : err = ip6_find_1stfragopt(skb, &prevhdr); 130 : 0 : if (err < 0) 131 : : goto blackhole; 132 : 0 : hlen = err; 133 : 0 : nexthdr = *prevhdr; 134 : : 135 : 0 : mtu = skb->dev->mtu; 136 : 0 : if (frag_max_size > mtu || 137 : 0 : frag_max_size < IPV6_MIN_MTU) 138 : : goto blackhole; 139 : : 140 : : mtu = frag_max_size; 141 : 0 : if (mtu < hlen + sizeof(struct frag_hdr) + 8) 142 : : goto blackhole; 143 : 0 : mtu -= hlen + sizeof(struct frag_hdr); 144 : : 145 : 0 : frag_id = ipv6_select_ident(net, &ipv6_hdr(skb)->daddr, 146 : 0 : &ipv6_hdr(skb)->saddr); 147 : : 148 : 0 : if (skb->ip_summed == CHECKSUM_PARTIAL && 149 : : (err = skb_checksum_help(skb))) 150 : : goto blackhole; 151 : : 152 : 0 : hroom = LL_RESERVED_SPACE(skb->dev); 153 : 0 : 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 : 0 : if (first_len - hlen > mtu || 159 : 0 : skb_headroom(skb) < (hroom + sizeof(struct frag_hdr))) 160 : : goto blackhole; 161 : : 162 : 0 : if (skb_cloned(skb)) 163 : : goto slow_path; 164 : : 165 : 0 : skb_walk_frags(skb, frag2) { 166 : 0 : if (frag2->len > mtu || 167 : 0 : skb_headroom(frag2) < (hlen + hroom + sizeof(struct frag_hdr))) 168 : : goto blackhole; 169 : : 170 : : /* Partially cloned skb? */ 171 : 0 : if (skb_shared(frag2)) 172 : : goto slow_path; 173 : : } 174 : : 175 : 0 : err = ip6_fraglist_init(skb, hlen, prevhdr, nexthdr, frag_id, 176 : : &iter); 177 : 0 : if (err < 0) 178 : : goto blackhole; 179 : : 180 : : for (;;) { 181 : : /* Prepare header of the next frame, 182 : : * before previous one went down. 183 : : */ 184 : 0 : if (iter.frag) 185 : 0 : ip6_fraglist_prepare(skb, &iter); 186 : : 187 : 0 : skb->tstamp = tstamp; 188 : 0 : err = output(net, sk, data, skb); 189 : 0 : if (err || !iter.frag) 190 : : break; 191 : : 192 : : skb = ip6_fraglist_next(&iter); 193 : : } 194 : : 195 : 0 : kfree(iter.tmp_hdr); 196 : 0 : if (!err) 197 : 0 : return 0; 198 : : 199 : 0 : kfree_skb_list(iter.frag); 200 : 0 : return err; 201 : : } 202 : : slow_path: 203 : : /* This is a linearized skbuff, the original geometry is lost for us. 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 : 0 : ip6_frag_init(skb, hlen, mtu, skb->dev->needed_tailroom, 208 : : LL_RESERVED_SPACE(skb->dev), prevhdr, nexthdr, frag_id, 209 : : &state); 210 : : 211 : 0 : while (state.left > 0) { 212 : : struct sk_buff *skb2; 213 : : 214 : 0 : skb2 = ip6_frag_next(skb, &state); 215 : 0 : if (IS_ERR(skb2)) { 216 : : err = PTR_ERR(skb2); 217 : : goto blackhole; 218 : : } 219 : : 220 : 0 : skb2->tstamp = tstamp; 221 : 0 : err = output(net, sk, data, skb2); 222 : 0 : if (err) 223 : : goto blackhole; 224 : : } 225 : 0 : consume_skb(skb); 226 : 0 : return err; 227 : : 228 : : blackhole: 229 : 0 : kfree_skb(skb); 230 : 0 : return 0; 231 : : } 232 : : EXPORT_SYMBOL_GPL(br_ip6_fragment); 233 : : 234 : : static const struct nf_ipv6_ops ipv6ops = { 235 : : #if IS_MODULE(CONFIG_IPV6) 236 : : .chk_addr = ipv6_chk_addr, 237 : : .route_me_harder = ip6_route_me_harder, 238 : : .dev_get_saddr = ipv6_dev_get_saddr, 239 : : .route = __nf_ip6_route, 240 : : #if IS_ENABLED(CONFIG_SYN_COOKIES) 241 : : .cookie_init_sequence = __cookie_v6_init_sequence, 242 : : .cookie_v6_check = __cookie_v6_check, 243 : : #endif 244 : : #endif 245 : : .route_input = ip6_route_input, 246 : : .fragment = ip6_fragment, 247 : : .reroute = nf_ip6_reroute, 248 : : #if IS_MODULE(CONFIG_IPV6) && IS_ENABLED(CONFIG_NF_DEFRAG_IPV6) 249 : : .br_defrag = nf_ct_frag6_gather, 250 : : #endif 251 : : #if IS_MODULE(CONFIG_IPV6) 252 : : .br_fragment = br_ip6_fragment, 253 : : #endif 254 : : }; 255 : : 256 : 3 : int __init ipv6_netfilter_init(void) 257 : : { 258 : 3 : RCU_INIT_POINTER(nf_ipv6_ops, &ipv6ops); 259 : 3 : return 0; 260 : : } 261 : : 262 : : /* This can be called from inet6_init() on errors, so it cannot 263 : : * be marked __exit. -DaveM 264 : : */ 265 : 0 : void ipv6_netfilter_fini(void) 266 : : { 267 : : RCU_INIT_POINTER(nf_ipv6_ops, NULL); 268 : 0 : }