1/* 2 * xfrm6_mode_beet.c - BEET mode encapsulation for IPv6. 3 * 4 * Copyright (c) 2006 Diego Beltrami <diego.beltrami@gmail.com> 5 * Miika Komu <miika@iki.fi> 6 * Herbert Xu <herbert@gondor.apana.org.au> 7 * Abhinav Pathak <abhinav.pathak@hiit.fi> 8 * Jeff Ahrenholz <ahrenholz@gmail.com> 9 */ 10 11#include <linux/init.h> 12#include <linux/kernel.h> 13#include <linux/module.h> 14#include <linux/skbuff.h> 15#include <linux/stringify.h> 16#include <net/dsfield.h> 17#include <net/dst.h> 18#include <net/inet_ecn.h> 19#include <net/ipv6.h> 20#include <net/xfrm.h> 21 22/* Add encapsulation header. 23 * 24 * The top IP header will be constructed per draft-nikander-esp-beet-mode-06.txt. 25 * The following fields in it shall be filled in by x->type->output: 26 * payload_len 27 * 28 * On exit, skb->h will be set to the start of the encapsulation header to be 29 * filled in by x->type->output and skb->nh will be set to the nextheader field 30 * of the extension header directly preceding the encapsulation header, or in 31 * its absence, that of the top IP header. The value of skb->data will always 32 * point to the top IP header. 33 */ 34static int xfrm6_beet_output(struct xfrm_state *x, struct sk_buff *skb) 35{ 36 struct ipv6hdr *iph, *top_iph; 37 u8 *prevhdr; 38 int hdr_len; 39 40 skb_push(skb, x->props.header_len); 41 iph = ipv6_hdr(skb); 42 43 hdr_len = ip6_find_1stfragopt(skb, &prevhdr); 44 skb_set_network_header(skb, 45 (prevhdr - x->props.header_len) - skb->data); 46 skb_set_transport_header(skb, hdr_len); 47 memmove(skb->data, iph, hdr_len); 48 49 skb_reset_network_header(skb); 50 top_iph = ipv6_hdr(skb); 51 skb->transport_header = skb->network_header + sizeof(struct ipv6hdr); 52 skb->network_header += offsetof(struct ipv6hdr, nexthdr); 53 54 ipv6_addr_copy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr); 55 ipv6_addr_copy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr); 56 57 return 0; 58} 59 60static int xfrm6_beet_input(struct xfrm_state *x, struct sk_buff *skb) 61{ 62 struct ipv6hdr *ip6h; 63 const unsigned char *old_mac; 64 int size = sizeof(struct ipv6hdr); 65 int err = -EINVAL; 66 67 if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 68 goto out; 69 70 skb_push(skb, size); 71 memmove(skb->data, skb_network_header(skb), size); 72 skb_reset_network_header(skb); 73 74 old_mac = skb_mac_header(skb); 75 skb_set_mac_header(skb, -skb->mac_len); 76 memmove(skb_mac_header(skb), old_mac, skb->mac_len); 77 78 ip6h = ipv6_hdr(skb); 79 ip6h->payload_len = htons(skb->len - size); 80 ipv6_addr_copy(&ip6h->daddr, (struct in6_addr *) &x->sel.daddr.a6); 81 ipv6_addr_copy(&ip6h->saddr, (struct in6_addr *) &x->sel.saddr.a6); 82 err = 0; 83out: 84 return err; 85} 86 87static struct xfrm_mode xfrm6_beet_mode = { 88 .input = xfrm6_beet_input, 89 .output = xfrm6_beet_output, 90 .owner = THIS_MODULE, 91 .encap = XFRM_MODE_BEET, 92}; 93 94static int __init xfrm6_beet_init(void) 95{ 96 return xfrm_register_mode(&xfrm6_beet_mode, AF_INET6); 97} 98 99static void __exit xfrm6_beet_exit(void) 100{ 101 int err; 102 103 err = xfrm_unregister_mode(&xfrm6_beet_mode, AF_INET6); 104 BUG_ON(err); 105} 106 107module_init(xfrm6_beet_init); 108module_exit(xfrm6_beet_exit); 109MODULE_LICENSE("GPL"); 110MODULE_ALIAS_XFRM_MODE(AF_INET6, XFRM_MODE_BEET); 111