| Diego Beltrami | 0a69452 | 2006-10-03 23:47:05 -0700 | [diff] [blame] | 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 |  | 
| Herbert Xu | 227620e | 2007-11-13 21:41:28 -0800 | [diff] [blame] | 22 | static void xfrm6_beet_make_header(struct sk_buff *skb) | 
|  | 23 | { | 
|  | 24 | struct ipv6hdr *iph = ipv6_hdr(skb); | 
|  | 25 |  | 
|  | 26 | iph->version = 6; | 
|  | 27 |  | 
|  | 28 | memcpy(iph->flow_lbl, XFRM_MODE_SKB_CB(skb)->flow_lbl, | 
|  | 29 | sizeof(iph->flow_lbl)); | 
|  | 30 | iph->nexthdr = XFRM_MODE_SKB_CB(skb)->protocol; | 
|  | 31 |  | 
|  | 32 | ipv6_change_dsfield(iph, 0, XFRM_MODE_SKB_CB(skb)->tos); | 
|  | 33 | iph->hop_limit = XFRM_MODE_SKB_CB(skb)->ttl; | 
|  | 34 | } | 
|  | 35 |  | 
| Diego Beltrami | 0a69452 | 2006-10-03 23:47:05 -0700 | [diff] [blame] | 36 | /* Add encapsulation header. | 
|  | 37 | * | 
|  | 38 | * The top IP header will be constructed per draft-nikander-esp-beet-mode-06.txt. | 
| Diego Beltrami | 0a69452 | 2006-10-03 23:47:05 -0700 | [diff] [blame] | 39 | */ | 
|  | 40 | static int xfrm6_beet_output(struct xfrm_state *x, struct sk_buff *skb) | 
|  | 41 | { | 
| Herbert Xu | 36cf9ac | 2007-11-13 21:40:52 -0800 | [diff] [blame] | 42 | struct ipv6hdr *top_iph; | 
| Joakim Koskela | abf5cdb | 2008-08-06 02:40:25 -0700 | [diff] [blame] | 43 | struct ip_beet_phdr *ph; | 
| Joakim Koskela | abf5cdb | 2008-08-06 02:40:25 -0700 | [diff] [blame] | 44 | int optlen, hdr_len; | 
| Diego Beltrami | 0a69452 | 2006-10-03 23:47:05 -0700 | [diff] [blame] | 45 |  | 
| Joakim Koskela | abf5cdb | 2008-08-06 02:40:25 -0700 | [diff] [blame] | 46 | hdr_len = 0; | 
|  | 47 | optlen = XFRM_MODE_SKB_CB(skb)->optlen; | 
|  | 48 | if (unlikely(optlen)) | 
|  | 49 | hdr_len += IPV4_BEET_PHMAXLEN - (optlen & 4); | 
|  | 50 |  | 
|  | 51 | skb_set_network_header(skb, -x->props.header_len - hdr_len); | 
|  | 52 | if (x->sel.family != AF_INET6) | 
|  | 53 | skb->network_header += IPV4_BEET_PHMAXLEN; | 
| Herbert Xu | 36cf9ac | 2007-11-13 21:40:52 -0800 | [diff] [blame] | 54 | skb->mac_header = skb->network_header + | 
|  | 55 | offsetof(struct ipv6hdr, nexthdr); | 
|  | 56 | skb->transport_header = skb->network_header + sizeof(*top_iph); | 
| Joakim Koskela | abf5cdb | 2008-08-06 02:40:25 -0700 | [diff] [blame] | 57 | ph = (struct ip_beet_phdr *)__skb_pull(skb, XFRM_MODE_SKB_CB(skb)->ihl-hdr_len); | 
| Herbert Xu | 227620e | 2007-11-13 21:41:28 -0800 | [diff] [blame] | 58 |  | 
|  | 59 | xfrm6_beet_make_header(skb); | 
|  | 60 |  | 
| Arnaldo Carvalho de Melo | 0660e03 | 2007-04-25 17:54:47 -0700 | [diff] [blame] | 61 | top_iph = ipv6_hdr(skb); | 
| Joakim Koskela | abf5cdb | 2008-08-06 02:40:25 -0700 | [diff] [blame] | 62 | if (unlikely(optlen)) { | 
|  | 63 |  | 
|  | 64 | BUG_ON(optlen < 0); | 
|  | 65 |  | 
|  | 66 | ph->padlen = 4 - (optlen & 4); | 
|  | 67 | ph->hdrlen = optlen / 8; | 
|  | 68 | ph->nexthdr = top_iph->nexthdr; | 
|  | 69 | if (ph->padlen) | 
|  | 70 | memset(ph + 1, IPOPT_NOP, ph->padlen); | 
|  | 71 |  | 
|  | 72 | top_iph->nexthdr = IPPROTO_BEETPH; | 
|  | 73 | } | 
| Diego Beltrami | 0a69452 | 2006-10-03 23:47:05 -0700 | [diff] [blame] | 74 |  | 
| Alexey Dobriyan | 4e3fd7a | 2011-11-21 03:39:03 +0000 | [diff] [blame] | 75 | top_iph->saddr = *(struct in6_addr *)&x->props.saddr; | 
|  | 76 | top_iph->daddr = *(struct in6_addr *)&x->id.daddr; | 
| Diego Beltrami | 0a69452 | 2006-10-03 23:47:05 -0700 | [diff] [blame] | 77 | return 0; | 
|  | 78 | } | 
|  | 79 |  | 
|  | 80 | static int xfrm6_beet_input(struct xfrm_state *x, struct sk_buff *skb) | 
|  | 81 | { | 
|  | 82 | struct ipv6hdr *ip6h; | 
|  | 83 | int size = sizeof(struct ipv6hdr); | 
| Herbert Xu | 227620e | 2007-11-13 21:41:28 -0800 | [diff] [blame] | 84 | int err; | 
| Diego Beltrami | 0a69452 | 2006-10-03 23:47:05 -0700 | [diff] [blame] | 85 |  | 
| Herbert Xu | 227620e | 2007-11-13 21:41:28 -0800 | [diff] [blame] | 86 | err = skb_cow_head(skb, size + skb->mac_len); | 
|  | 87 | if (err) | 
| Diego Beltrami | 0a69452 | 2006-10-03 23:47:05 -0700 | [diff] [blame] | 88 | goto out; | 
|  | 89 |  | 
| Herbert Xu | 227620e | 2007-11-13 21:41:28 -0800 | [diff] [blame] | 90 | __skb_push(skb, size); | 
| Arnaldo Carvalho de Melo | c1d2bbe | 2007-04-10 20:45:18 -0700 | [diff] [blame] | 91 | skb_reset_network_header(skb); | 
| Eric Dumazet | 0360689 | 2012-02-23 10:55:02 +0000 | [diff] [blame] | 92 | skb_mac_header_rebuild(skb); | 
| Diego Beltrami | 0a69452 | 2006-10-03 23:47:05 -0700 | [diff] [blame] | 93 |  | 
| Herbert Xu | 227620e | 2007-11-13 21:41:28 -0800 | [diff] [blame] | 94 | xfrm6_beet_make_header(skb); | 
|  | 95 |  | 
| Arnaldo Carvalho de Melo | 0660e03 | 2007-04-25 17:54:47 -0700 | [diff] [blame] | 96 | ip6h = ipv6_hdr(skb); | 
| Diego Beltrami | 0a69452 | 2006-10-03 23:47:05 -0700 | [diff] [blame] | 97 | ip6h->payload_len = htons(skb->len - size); | 
| Alexey Dobriyan | 4e3fd7a | 2011-11-21 03:39:03 +0000 | [diff] [blame] | 98 | ip6h->daddr = *(struct in6_addr *)&x->sel.daddr.a6; | 
|  | 99 | ip6h->saddr = *(struct in6_addr *)&x->sel.saddr.a6; | 
| Diego Beltrami | 0a69452 | 2006-10-03 23:47:05 -0700 | [diff] [blame] | 100 | err = 0; | 
|  | 101 | out: | 
|  | 102 | return err; | 
|  | 103 | } | 
|  | 104 |  | 
|  | 105 | static struct xfrm_mode xfrm6_beet_mode = { | 
| Herbert Xu | 227620e | 2007-11-13 21:41:28 -0800 | [diff] [blame] | 106 | .input2 = xfrm6_beet_input, | 
|  | 107 | .input = xfrm_prepare_input, | 
| Herbert Xu | 36cf9ac | 2007-11-13 21:40:52 -0800 | [diff] [blame] | 108 | .output2 = xfrm6_beet_output, | 
|  | 109 | .output = xfrm6_prepare_output, | 
| Diego Beltrami | 0a69452 | 2006-10-03 23:47:05 -0700 | [diff] [blame] | 110 | .owner = THIS_MODULE, | 
|  | 111 | .encap = XFRM_MODE_BEET, | 
| Herbert Xu | 1bfcb10 | 2007-10-17 21:31:50 -0700 | [diff] [blame] | 112 | .flags = XFRM_MODE_FLAG_TUNNEL, | 
| Diego Beltrami | 0a69452 | 2006-10-03 23:47:05 -0700 | [diff] [blame] | 113 | }; | 
|  | 114 |  | 
|  | 115 | static int __init xfrm6_beet_init(void) | 
|  | 116 | { | 
|  | 117 | return xfrm_register_mode(&xfrm6_beet_mode, AF_INET6); | 
|  | 118 | } | 
|  | 119 |  | 
|  | 120 | static void __exit xfrm6_beet_exit(void) | 
|  | 121 | { | 
|  | 122 | int err; | 
|  | 123 |  | 
|  | 124 | err = xfrm_unregister_mode(&xfrm6_beet_mode, AF_INET6); | 
|  | 125 | BUG_ON(err); | 
|  | 126 | } | 
|  | 127 |  | 
|  | 128 | module_init(xfrm6_beet_init); | 
|  | 129 | module_exit(xfrm6_beet_exit); | 
|  | 130 | MODULE_LICENSE("GPL"); | 
|  | 131 | MODULE_ALIAS_XFRM_MODE(AF_INET6, XFRM_MODE_BEET); |