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 26 if (IPCB(skb)->flags & IPSKB_XFRM_TUNNEL_SIZE) 27 goto out; 28 29 IPCB(skb)->flags |= IPSKB_XFRM_TUNNEL_SIZE; 30 31 if (!(ip_hdr(skb)->frag_off & htons(IP_DF)) || skb->local_df) 32 goto out; 33 34 dst = skb->dst; 35 mtu = dst_mtu(dst); 36 if (skb->len > mtu) { 37 icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu)); 38 ret = -EMSGSIZE; 39 } 40 out: 41 return ret; 42 } 43 44 static int xfrm4_output_one(struct sk_buff *skb) 45 { 46 struct dst_entry *dst = skb->dst; 47 struct xfrm_state *x = dst->xfrm; 48 int err; 49 50 if (skb->ip_summed == CHECKSUM_PARTIAL) { 51 err = skb_checksum_help(skb); 52 if (err) 53 goto error_nolock; 54 } 55 56 if (x->props.mode == XFRM_MODE_TUNNEL) { 57 err = xfrm4_tunnel_check_size(skb); 58 if (err) 59 goto error_nolock; 60 } 61 62 do { 63 spin_lock_bh(&x->lock); 64 err = xfrm_state_check(x, skb); 65 if (err) 66 goto error; 67 68 err = x->mode->output(x, skb); 69 if (err) 70 goto error; 71 72 err = x->type->output(x, skb); 73 if (err) 74 goto error; 75 76 x->curlft.bytes += skb->len; 77 x->curlft.packets++; 78 79 spin_unlock_bh(&x->lock); 80 81 if (!(skb->dst = dst_pop(dst))) { 82 err = -EHOSTUNREACH; 83 goto error_nolock; 84 } 85 dst = skb->dst; 86 x = dst->xfrm; 87 } while (x && (x->props.mode != XFRM_MODE_TUNNEL)); 88 89 IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED; 90 err = 0; 91 92 out_exit: 93 return err; 94 error: 95 spin_unlock_bh(&x->lock); 96 error_nolock: 97 kfree_skb(skb); 98 goto out_exit; 99 } 100 101 static int xfrm4_output_finish2(struct sk_buff *skb) 102 { 103 int err; 104 105 while (likely((err = xfrm4_output_one(skb)) == 0)) { 106 nf_reset(skb); 107 108 err = nf_hook(PF_INET, NF_IP_LOCAL_OUT, &skb, NULL, 109 skb->dst->dev, dst_output); 110 if (unlikely(err != 1)) 111 break; 112 113 if (!skb->dst->xfrm) 114 return dst_output(skb); 115 116 err = nf_hook(PF_INET, NF_IP_POST_ROUTING, &skb, NULL, 117 skb->dst->dev, xfrm4_output_finish2); 118 if (unlikely(err != 1)) 119 break; 120 } 121 122 return err; 123 } 124 125 static int xfrm4_output_finish(struct sk_buff *skb) 126 { 127 struct sk_buff *segs; 128 129 #ifdef CONFIG_NETFILTER 130 if (!skb->dst->xfrm) { 131 IPCB(skb)->flags |= IPSKB_REROUTED; 132 return dst_output(skb); 133 } 134 #endif 135 136 if (!skb_is_gso(skb)) 137 return xfrm4_output_finish2(skb); 138 139 skb->protocol = htons(ETH_P_IP); 140 segs = skb_gso_segment(skb, 0); 141 kfree_skb(skb); 142 if (unlikely(IS_ERR(segs))) 143 return PTR_ERR(segs); 144 145 do { 146 struct sk_buff *nskb = segs->next; 147 int err; 148 149 segs->next = NULL; 150 err = xfrm4_output_finish2(segs); 151 152 if (unlikely(err)) { 153 while ((segs = nskb)) { 154 nskb = segs->next; 155 segs->next = NULL; 156 kfree_skb(segs); 157 } 158 return err; 159 } 160 161 segs = nskb; 162 } while (segs); 163 164 return 0; 165 } 166 167 int xfrm4_output(struct sk_buff *skb) 168 { 169 return NF_HOOK_COND(PF_INET, NF_IP_POST_ROUTING, skb, NULL, skb->dst->dev, 170 xfrm4_output_finish, 171 !(IPCB(skb)->flags & IPSKB_REROUTED)); 172 } 173