1 /* 2 * xfrm4_output.c - Common IPsec encapsulation code for IPv4. 3 * Copyright (c) 2004 Herbert Xu <herbert@gondor.apana.org.au> 4 * 5 * This program is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU General Public License 7 * as published by the Free Software Foundation; either version 8 * 2 of the License, or (at your option) any later version. 9 */ 10 11 #include <linux/compiler.h> 12 #include <linux/if_ether.h> 13 #include <linux/kernel.h> 14 #include <linux/skbuff.h> 15 #include <linux/spinlock.h> 16 #include <linux/netfilter_ipv4.h> 17 #include <net/ip.h> 18 #include <net/xfrm.h> 19 #include <net/icmp.h> 20 21 static int xfrm4_tunnel_check_size(struct sk_buff *skb) 22 { 23 int mtu, ret = 0; 24 struct dst_entry *dst; 25 struct iphdr *iph = skb->nh.iph; 26 27 if (IPCB(skb)->flags & IPSKB_XFRM_TUNNEL_SIZE) 28 goto out; 29 30 IPCB(skb)->flags |= IPSKB_XFRM_TUNNEL_SIZE; 31 32 if (!(iph->frag_off & htons(IP_DF)) || skb->local_df) 33 goto out; 34 35 dst = skb->dst; 36 mtu = dst_mtu(dst); 37 if (skb->len > mtu) { 38 icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu)); 39 ret = -EMSGSIZE; 40 } 41 out: 42 return ret; 43 } 44 45 static int xfrm4_output_one(struct sk_buff *skb) 46 { 47 struct dst_entry *dst = skb->dst; 48 struct xfrm_state *x = dst->xfrm; 49 int err; 50 51 if (skb->ip_summed == CHECKSUM_PARTIAL) { 52 err = skb_checksum_help(skb); 53 if (err) 54 goto error_nolock; 55 } 56 57 if (x->props.mode) { 58 err = xfrm4_tunnel_check_size(skb); 59 if (err) 60 goto error_nolock; 61 } 62 63 do { 64 spin_lock_bh(&x->lock); 65 err = xfrm_state_check(x, skb); 66 if (err) 67 goto error; 68 69 err = x->mode->output(skb); 70 if (err) 71 goto error; 72 73 err = x->type->output(x, skb); 74 if (err) 75 goto error; 76 77 x->curlft.bytes += skb->len; 78 x->curlft.packets++; 79 80 spin_unlock_bh(&x->lock); 81 82 if (!(skb->dst = dst_pop(dst))) { 83 err = -EHOSTUNREACH; 84 goto error_nolock; 85 } 86 dst = skb->dst; 87 x = dst->xfrm; 88 } while (x && !x->props.mode); 89 90 IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED; 91 err = 0; 92 93 out_exit: 94 return err; 95 error: 96 spin_unlock_bh(&x->lock); 97 error_nolock: 98 kfree_skb(skb); 99 goto out_exit; 100 } 101 102 static int xfrm4_output_finish2(struct sk_buff *skb) 103 { 104 int err; 105 106 while (likely((err = xfrm4_output_one(skb)) == 0)) { 107 nf_reset(skb); 108 109 err = nf_hook(PF_INET, NF_IP_LOCAL_OUT, &skb, NULL, 110 skb->dst->dev, dst_output); 111 if (unlikely(err != 1)) 112 break; 113 114 if (!skb->dst->xfrm) 115 return dst_output(skb); 116 117 err = nf_hook(PF_INET, NF_IP_POST_ROUTING, &skb, NULL, 118 skb->dst->dev, xfrm4_output_finish2); 119 if (unlikely(err != 1)) 120 break; 121 } 122 123 return err; 124 } 125 126 static int xfrm4_output_finish(struct sk_buff *skb) 127 { 128 struct sk_buff *segs; 129 130 #ifdef CONFIG_NETFILTER 131 if (!skb->dst->xfrm) { 132 IPCB(skb)->flags |= IPSKB_REROUTED; 133 return dst_output(skb); 134 } 135 #endif 136 137 if (!skb_is_gso(skb)) 138 return xfrm4_output_finish2(skb); 139 140 skb->protocol = htons(ETH_P_IP); 141 segs = skb_gso_segment(skb, 0); 142 kfree_skb(skb); 143 if (unlikely(IS_ERR(segs))) 144 return PTR_ERR(segs); 145 146 do { 147 struct sk_buff *nskb = segs->next; 148 int err; 149 150 segs->next = NULL; 151 err = xfrm4_output_finish2(segs); 152 153 if (unlikely(err)) { 154 while ((segs = nskb)) { 155 nskb = segs->next; 156 segs->next = NULL; 157 kfree_skb(segs); 158 } 159 return err; 160 } 161 162 segs = nskb; 163 } while (segs); 164 165 return 0; 166 } 167 168 int xfrm4_output(struct sk_buff *skb) 169 { 170 return NF_HOOK_COND(PF_INET, NF_IP_POST_ROUTING, skb, NULL, skb->dst->dev, 171 xfrm4_output_finish, 172 !(IPCB(skb)->flags & IPSKB_REROUTED)); 173 } 174