diff options
Diffstat (limited to 'net/ipv4/esp4.c')
| -rw-r--r-- | net/ipv4/esp4.c | 185 |
1 files changed, 66 insertions, 119 deletions
diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index 73bfcae8af9c..09590f356086 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c | |||
| @@ -12,13 +12,6 @@ | |||
| 12 | #include <net/protocol.h> | 12 | #include <net/protocol.h> |
| 13 | #include <net/udp.h> | 13 | #include <net/udp.h> |
| 14 | 14 | ||
| 15 | /* decapsulation data for use when post-processing */ | ||
| 16 | struct esp_decap_data { | ||
| 17 | xfrm_address_t saddr; | ||
| 18 | __u16 sport; | ||
| 19 | __u8 proto; | ||
| 20 | }; | ||
| 21 | |||
| 22 | static int esp_output(struct xfrm_state *x, struct sk_buff *skb) | 15 | static int esp_output(struct xfrm_state *x, struct sk_buff *skb) |
| 23 | { | 16 | { |
| 24 | int err; | 17 | int err; |
| @@ -150,6 +143,10 @@ static int esp_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struc | |||
| 150 | int elen = skb->len - sizeof(struct ip_esp_hdr) - esp->conf.ivlen - alen; | 143 | int elen = skb->len - sizeof(struct ip_esp_hdr) - esp->conf.ivlen - alen; |
| 151 | int nfrags; | 144 | int nfrags; |
| 152 | int encap_len = 0; | 145 | int encap_len = 0; |
| 146 | u8 nexthdr[2]; | ||
| 147 | struct scatterlist *sg; | ||
| 148 | u8 workbuf[60]; | ||
| 149 | int padlen; | ||
| 153 | 150 | ||
| 154 | if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr))) | 151 | if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr))) |
| 155 | goto out; | 152 | goto out; |
| @@ -185,122 +182,82 @@ static int esp_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struc | |||
| 185 | if (esp->conf.ivlen) | 182 | if (esp->conf.ivlen) |
| 186 | crypto_cipher_set_iv(esp->conf.tfm, esph->enc_data, crypto_tfm_alg_ivsize(esp->conf.tfm)); | 183 | crypto_cipher_set_iv(esp->conf.tfm, esph->enc_data, crypto_tfm_alg_ivsize(esp->conf.tfm)); |
| 187 | 184 | ||
| 188 | { | 185 | sg = &esp->sgbuf[0]; |
| 189 | u8 nexthdr[2]; | ||
| 190 | struct scatterlist *sg = &esp->sgbuf[0]; | ||
| 191 | u8 workbuf[60]; | ||
| 192 | int padlen; | ||
| 193 | |||
| 194 | if (unlikely(nfrags > ESP_NUM_FAST_SG)) { | ||
| 195 | sg = kmalloc(sizeof(struct scatterlist)*nfrags, GFP_ATOMIC); | ||
| 196 | if (!sg) | ||
| 197 | goto out; | ||
| 198 | } | ||
| 199 | skb_to_sgvec(skb, sg, sizeof(struct ip_esp_hdr) + esp->conf.ivlen, elen); | ||
| 200 | crypto_cipher_decrypt(esp->conf.tfm, sg, sg, elen); | ||
| 201 | if (unlikely(sg != &esp->sgbuf[0])) | ||
| 202 | kfree(sg); | ||
| 203 | |||
| 204 | if (skb_copy_bits(skb, skb->len-alen-2, nexthdr, 2)) | ||
| 205 | BUG(); | ||
| 206 | 186 | ||
| 207 | padlen = nexthdr[0]; | 187 | if (unlikely(nfrags > ESP_NUM_FAST_SG)) { |
| 208 | if (padlen+2 >= elen) | 188 | sg = kmalloc(sizeof(struct scatterlist)*nfrags, GFP_ATOMIC); |
| 189 | if (!sg) | ||
| 209 | goto out; | 190 | goto out; |
| 210 | |||
| 211 | /* ... check padding bits here. Silly. :-) */ | ||
| 212 | |||
| 213 | if (x->encap && decap && decap->decap_type) { | ||
| 214 | struct esp_decap_data *encap_data; | ||
| 215 | struct udphdr *uh = (struct udphdr *) (iph+1); | ||
| 216 | |||
| 217 | encap_data = (struct esp_decap_data *) (decap->decap_data); | ||
| 218 | encap_data->proto = 0; | ||
| 219 | |||
| 220 | switch (decap->decap_type) { | ||
| 221 | case UDP_ENCAP_ESPINUDP: | ||
| 222 | case UDP_ENCAP_ESPINUDP_NON_IKE: | ||
| 223 | encap_data->proto = AF_INET; | ||
| 224 | encap_data->saddr.a4 = iph->saddr; | ||
| 225 | encap_data->sport = uh->source; | ||
| 226 | encap_len = (void*)esph - (void*)uh; | ||
| 227 | break; | ||
| 228 | |||
| 229 | default: | ||
| 230 | goto out; | ||
| 231 | } | ||
| 232 | } | ||
| 233 | |||
| 234 | iph->protocol = nexthdr[1]; | ||
| 235 | pskb_trim(skb, skb->len - alen - padlen - 2); | ||
| 236 | memcpy(workbuf, skb->nh.raw, iph->ihl*4); | ||
| 237 | skb->h.raw = skb_pull(skb, sizeof(struct ip_esp_hdr) + esp->conf.ivlen); | ||
| 238 | skb->nh.raw += encap_len + sizeof(struct ip_esp_hdr) + esp->conf.ivlen; | ||
| 239 | memcpy(skb->nh.raw, workbuf, iph->ihl*4); | ||
| 240 | skb->nh.iph->tot_len = htons(skb->len); | ||
| 241 | } | 191 | } |
| 192 | skb_to_sgvec(skb, sg, sizeof(struct ip_esp_hdr) + esp->conf.ivlen, elen); | ||
| 193 | crypto_cipher_decrypt(esp->conf.tfm, sg, sg, elen); | ||
| 194 | if (unlikely(sg != &esp->sgbuf[0])) | ||
| 195 | kfree(sg); | ||
| 242 | 196 | ||
| 243 | return 0; | 197 | if (skb_copy_bits(skb, skb->len-alen-2, nexthdr, 2)) |
| 198 | BUG(); | ||
| 244 | 199 | ||
| 245 | out: | 200 | padlen = nexthdr[0]; |
| 246 | return -EINVAL; | 201 | if (padlen+2 >= elen) |
| 247 | } | 202 | goto out; |
| 248 | 203 | ||
| 249 | static int esp_post_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_buff *skb) | 204 | /* ... check padding bits here. Silly. :-) */ |
| 250 | { | ||
| 251 | |||
| 252 | if (x->encap) { | ||
| 253 | struct xfrm_encap_tmpl *encap; | ||
| 254 | struct esp_decap_data *decap_data; | ||
| 255 | 205 | ||
| 256 | encap = x->encap; | 206 | if (x->encap) { |
| 257 | decap_data = (struct esp_decap_data *)(decap->decap_data); | 207 | struct xfrm_encap_tmpl *encap = x->encap; |
| 208 | struct udphdr *uh; | ||
| 258 | 209 | ||
| 259 | /* first, make sure that the decap type == the encap type */ | ||
| 260 | if (encap->encap_type != decap->decap_type) | 210 | if (encap->encap_type != decap->decap_type) |
| 261 | return -EINVAL; | 211 | goto out; |
| 262 | 212 | ||
| 263 | switch (encap->encap_type) { | 213 | uh = (struct udphdr *)(iph + 1); |
| 264 | default: | 214 | encap_len = (void*)esph - (void*)uh; |
| 265 | case UDP_ENCAP_ESPINUDP: | 215 | |
| 266 | case UDP_ENCAP_ESPINUDP_NON_IKE: | 216 | /* |
| 267 | /* | 217 | * 1) if the NAT-T peer's IP or port changed then |
| 268 | * 1) if the NAT-T peer's IP or port changed then | 218 | * advertize the change to the keying daemon. |
| 269 | * advertize the change to the keying daemon. | 219 | * This is an inbound SA, so just compare |
| 270 | * This is an inbound SA, so just compare | 220 | * SRC ports. |
| 271 | * SRC ports. | 221 | */ |
| 272 | */ | 222 | if (iph->saddr != x->props.saddr.a4 || |
| 273 | if (decap_data->proto == AF_INET && | 223 | uh->source != encap->encap_sport) { |
| 274 | (decap_data->saddr.a4 != x->props.saddr.a4 || | 224 | xfrm_address_t ipaddr; |
| 275 | decap_data->sport != encap->encap_sport)) { | 225 | |
| 276 | xfrm_address_t ipaddr; | 226 | ipaddr.a4 = iph->saddr; |
| 277 | 227 | km_new_mapping(x, &ipaddr, uh->source); | |
| 278 | ipaddr.a4 = decap_data->saddr.a4; | 228 | |
| 279 | km_new_mapping(x, &ipaddr, decap_data->sport); | 229 | /* XXX: perhaps add an extra |
| 280 | 230 | * policy check here, to see | |
| 281 | /* XXX: perhaps add an extra | 231 | * if we should allow or |
| 282 | * policy check here, to see | 232 | * reject a packet from a |
| 283 | * if we should allow or | 233 | * different source |
| 284 | * reject a packet from a | 234 | * address/port. |
| 285 | * different source | ||
| 286 | * address/port. | ||
| 287 | */ | ||
| 288 | } | ||
| 289 | |||
| 290 | /* | ||
| 291 | * 2) ignore UDP/TCP checksums in case | ||
| 292 | * of NAT-T in Transport Mode, or | ||
| 293 | * perform other post-processing fixes | ||
| 294 | * as per * draft-ietf-ipsec-udp-encaps-06, | ||
| 295 | * section 3.1.2 | ||
| 296 | */ | 235 | */ |
| 297 | if (!x->props.mode) | ||
| 298 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
| 299 | |||
| 300 | break; | ||
| 301 | } | 236 | } |
| 237 | |||
| 238 | /* | ||
| 239 | * 2) ignore UDP/TCP checksums in case | ||
| 240 | * of NAT-T in Transport Mode, or | ||
| 241 | * perform other post-processing fixes | ||
| 242 | * as per draft-ietf-ipsec-udp-encaps-06, | ||
| 243 | * section 3.1.2 | ||
| 244 | */ | ||
| 245 | if (!x->props.mode) | ||
| 246 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
| 302 | } | 247 | } |
| 248 | |||
| 249 | iph->protocol = nexthdr[1]; | ||
| 250 | pskb_trim(skb, skb->len - alen - padlen - 2); | ||
| 251 | memcpy(workbuf, skb->nh.raw, iph->ihl*4); | ||
| 252 | skb->h.raw = skb_pull(skb, sizeof(struct ip_esp_hdr) + esp->conf.ivlen); | ||
| 253 | skb->nh.raw += encap_len + sizeof(struct ip_esp_hdr) + esp->conf.ivlen; | ||
| 254 | memcpy(skb->nh.raw, workbuf, iph->ihl*4); | ||
| 255 | skb->nh.iph->tot_len = htons(skb->len); | ||
| 256 | |||
| 303 | return 0; | 257 | return 0; |
| 258 | |||
| 259 | out: | ||
| 260 | return -EINVAL; | ||
| 304 | } | 261 | } |
| 305 | 262 | ||
| 306 | static u32 esp4_get_max_size(struct xfrm_state *x, int mtu) | 263 | static u32 esp4_get_max_size(struct xfrm_state *x, int mtu) |
| @@ -458,7 +415,6 @@ static struct xfrm_type esp_type = | |||
| 458 | .destructor = esp_destroy, | 415 | .destructor = esp_destroy, |
| 459 | .get_max_size = esp4_get_max_size, | 416 | .get_max_size = esp4_get_max_size, |
| 460 | .input = esp_input, | 417 | .input = esp_input, |
| 461 | .post_input = esp_post_input, | ||
| 462 | .output = esp_output | 418 | .output = esp_output |
| 463 | }; | 419 | }; |
| 464 | 420 | ||
| @@ -470,15 +426,6 @@ static struct net_protocol esp4_protocol = { | |||
| 470 | 426 | ||
| 471 | static int __init esp4_init(void) | 427 | static int __init esp4_init(void) |
| 472 | { | 428 | { |
| 473 | struct xfrm_decap_state decap; | ||
| 474 | |||
| 475 | if (sizeof(struct esp_decap_data) > | ||
| 476 | sizeof(decap.decap_data)) { | ||
| 477 | extern void decap_data_too_small(void); | ||
| 478 | |||
| 479 | decap_data_too_small(); | ||
| 480 | } | ||
| 481 | |||
| 482 | if (xfrm_register_type(&esp_type, AF_INET) < 0) { | 429 | if (xfrm_register_type(&esp_type, AF_INET) < 0) { |
| 483 | printk(KERN_INFO "ip esp init: can't add xfrm type\n"); | 430 | printk(KERN_INFO "ip esp init: can't add xfrm type\n"); |
| 484 | return -EAGAIN; | 431 | return -EAGAIN; |
