diff options
author | Noriaki TAKAMIYA <takamiya@po.ntts.co.jp> | 2006-08-23 23:32:34 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-09-22 18:06:58 -0400 |
commit | 3d126890dd67beffec27c1b6f51c040fc8d0b526 (patch) | |
tree | c796ef93eb61fe1a93c42dd7965fe21a5b142b66 /net/ipv6/mip6.c | |
parent | 2c8d7ca0f76103855ad1f2a930e05683b64a00eb (diff) |
[IPV6] MIP6: Add destination options header transformation.
Add destination options header transformation for Mobile IPv6.
Based on MIPL2 kernel patch.
This patch was also written by: Ville Nuorvala <vnuorval@tcs.hut.fi>
Signed-off-by: Noriaki TAKAMIYA <takamiya@po.ntts.co.jp>
Signed-off-by: Masahide NAKAMURA <nakam@linux-ipv6.org>
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/mip6.c')
-rw-r--r-- | net/ipv6/mip6.c | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/net/ipv6/mip6.c b/net/ipv6/mip6.c index 63e548b6f81e..a8adf891fe0e 100644 --- a/net/ipv6/mip6.c +++ b/net/ipv6/mip6.c | |||
@@ -35,6 +35,165 @@ static xfrm_address_t *mip6_xfrm_addr(struct xfrm_state *x, xfrm_address_t *addr | |||
35 | return x->coaddr; | 35 | return x->coaddr; |
36 | } | 36 | } |
37 | 37 | ||
38 | static inline unsigned int calc_padlen(unsigned int len, unsigned int n) | ||
39 | { | ||
40 | return (n - len + 16) & 0x7; | ||
41 | } | ||
42 | |||
43 | static inline void *mip6_padn(__u8 *data, __u8 padlen) | ||
44 | { | ||
45 | if (!data) | ||
46 | return NULL; | ||
47 | if (padlen == 1) { | ||
48 | data[0] = MIP6_OPT_PAD_1; | ||
49 | } else if (padlen > 1) { | ||
50 | data[0] = MIP6_OPT_PAD_N; | ||
51 | data[1] = padlen - 2; | ||
52 | if (padlen > 2) | ||
53 | memset(data+2, 0, data[1]); | ||
54 | } | ||
55 | return data + padlen; | ||
56 | } | ||
57 | |||
58 | static int mip6_destopt_input(struct xfrm_state *x, struct sk_buff *skb) | ||
59 | { | ||
60 | struct ipv6hdr *iph = skb->nh.ipv6h; | ||
61 | struct ipv6_destopt_hdr *destopt = (struct ipv6_destopt_hdr *)skb->data; | ||
62 | |||
63 | if (!ipv6_addr_equal(&iph->saddr, (struct in6_addr *)x->coaddr) && | ||
64 | !ipv6_addr_any((struct in6_addr *)x->coaddr)) | ||
65 | return -ENOENT; | ||
66 | |||
67 | return destopt->nexthdr; | ||
68 | } | ||
69 | |||
70 | /* Destination Option Header is inserted. | ||
71 | * IP Header's src address is replaced with Home Address Option in | ||
72 | * Destination Option Header. | ||
73 | */ | ||
74 | static int mip6_destopt_output(struct xfrm_state *x, struct sk_buff *skb) | ||
75 | { | ||
76 | struct ipv6hdr *iph; | ||
77 | struct ipv6_destopt_hdr *dstopt; | ||
78 | struct ipv6_destopt_hao *hao; | ||
79 | u8 nexthdr; | ||
80 | int len; | ||
81 | |||
82 | iph = (struct ipv6hdr *)skb->data; | ||
83 | iph->payload_len = htons(skb->len - sizeof(*iph)); | ||
84 | |||
85 | nexthdr = *skb->nh.raw; | ||
86 | *skb->nh.raw = IPPROTO_DSTOPTS; | ||
87 | |||
88 | dstopt = (struct ipv6_destopt_hdr *)skb->h.raw; | ||
89 | dstopt->nexthdr = nexthdr; | ||
90 | |||
91 | hao = mip6_padn((char *)(dstopt + 1), | ||
92 | calc_padlen(sizeof(*dstopt), 6)); | ||
93 | |||
94 | hao->type = IPV6_TLV_HAO; | ||
95 | hao->length = sizeof(*hao) - 2; | ||
96 | BUG_TRAP(hao->length == 16); | ||
97 | |||
98 | len = ((char *)hao - (char *)dstopt) + sizeof(*hao); | ||
99 | |||
100 | memcpy(&hao->addr, &iph->saddr, sizeof(hao->addr)); | ||
101 | memcpy(&iph->saddr, x->coaddr, sizeof(iph->saddr)); | ||
102 | |||
103 | BUG_TRAP(len == x->props.header_len); | ||
104 | dstopt->hdrlen = (x->props.header_len >> 3) - 1; | ||
105 | |||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | static int mip6_destopt_offset(struct xfrm_state *x, struct sk_buff *skb, | ||
110 | u8 **nexthdr) | ||
111 | { | ||
112 | u16 offset = sizeof(struct ipv6hdr); | ||
113 | struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.ipv6h + 1); | ||
114 | unsigned int packet_len = skb->tail - skb->nh.raw; | ||
115 | int found_rhdr = 0; | ||
116 | |||
117 | *nexthdr = &skb->nh.ipv6h->nexthdr; | ||
118 | |||
119 | while (offset + 1 <= packet_len) { | ||
120 | |||
121 | switch (**nexthdr) { | ||
122 | case NEXTHDR_HOP: | ||
123 | break; | ||
124 | case NEXTHDR_ROUTING: | ||
125 | found_rhdr = 1; | ||
126 | break; | ||
127 | case NEXTHDR_DEST: | ||
128 | /* | ||
129 | * HAO MUST NOT appear more than once. | ||
130 | * XXX: It is better to try to find by the end of | ||
131 | * XXX: packet if HAO exists. | ||
132 | */ | ||
133 | if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0) { | ||
134 | LIMIT_NETDEBUG(KERN_WARNING "mip6: hao exists already, override\n"); | ||
135 | return offset; | ||
136 | } | ||
137 | |||
138 | if (found_rhdr) | ||
139 | return offset; | ||
140 | |||
141 | break; | ||
142 | default: | ||
143 | return offset; | ||
144 | } | ||
145 | |||
146 | offset += ipv6_optlen(exthdr); | ||
147 | *nexthdr = &exthdr->nexthdr; | ||
148 | exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); | ||
149 | } | ||
150 | |||
151 | return offset; | ||
152 | } | ||
153 | |||
154 | static int mip6_destopt_init_state(struct xfrm_state *x) | ||
155 | { | ||
156 | if (x->id.spi) { | ||
157 | printk(KERN_INFO "%s: spi is not 0: %u\n", __FUNCTION__, | ||
158 | x->id.spi); | ||
159 | return -EINVAL; | ||
160 | } | ||
161 | if (x->props.mode != XFRM_MODE_ROUTEOPTIMIZATION) { | ||
162 | printk(KERN_INFO "%s: state's mode is not %u: %u\n", | ||
163 | __FUNCTION__, XFRM_MODE_ROUTEOPTIMIZATION, x->props.mode); | ||
164 | return -EINVAL; | ||
165 | } | ||
166 | |||
167 | x->props.header_len = sizeof(struct ipv6_destopt_hdr) + | ||
168 | calc_padlen(sizeof(struct ipv6_destopt_hdr), 6) + | ||
169 | sizeof(struct ipv6_destopt_hao); | ||
170 | BUG_TRAP(x->props.header_len == 24); | ||
171 | |||
172 | return 0; | ||
173 | } | ||
174 | |||
175 | /* | ||
176 | * Do nothing about destroying since it has no specific operation for | ||
177 | * destination options header unlike IPsec protocols. | ||
178 | */ | ||
179 | static void mip6_destopt_destroy(struct xfrm_state *x) | ||
180 | { | ||
181 | } | ||
182 | |||
183 | static struct xfrm_type mip6_destopt_type = | ||
184 | { | ||
185 | .description = "MIP6DESTOPT", | ||
186 | .owner = THIS_MODULE, | ||
187 | .proto = IPPROTO_DSTOPTS, | ||
188 | .flags = XFRM_TYPE_NON_FRAGMENT, | ||
189 | .init_state = mip6_destopt_init_state, | ||
190 | .destructor = mip6_destopt_destroy, | ||
191 | .input = mip6_destopt_input, | ||
192 | .output = mip6_destopt_output, | ||
193 | .hdr_offset = mip6_destopt_offset, | ||
194 | .local_addr = mip6_xfrm_addr, | ||
195 | }; | ||
196 | |||
38 | static int mip6_rthdr_input(struct xfrm_state *x, struct sk_buff *skb) | 197 | static int mip6_rthdr_input(struct xfrm_state *x, struct sk_buff *skb) |
39 | { | 198 | { |
40 | struct rt2_hdr *rt2 = (struct rt2_hdr *)skb->data; | 199 | struct rt2_hdr *rt2 = (struct rt2_hdr *)skb->data; |
@@ -164,6 +323,10 @@ int __init mip6_init(void) | |||
164 | { | 323 | { |
165 | printk(KERN_INFO "Mobile IPv6\n"); | 324 | printk(KERN_INFO "Mobile IPv6\n"); |
166 | 325 | ||
326 | if (xfrm_register_type(&mip6_destopt_type, AF_INET6) < 0) { | ||
327 | printk(KERN_INFO "%s: can't add xfrm type(destopt)\n", __FUNCTION__); | ||
328 | goto mip6_destopt_xfrm_fail; | ||
329 | } | ||
167 | if (xfrm_register_type(&mip6_rthdr_type, AF_INET6) < 0) { | 330 | if (xfrm_register_type(&mip6_rthdr_type, AF_INET6) < 0) { |
168 | printk(KERN_INFO "%s: can't add xfrm type(rthdr)\n", __FUNCTION__); | 331 | printk(KERN_INFO "%s: can't add xfrm type(rthdr)\n", __FUNCTION__); |
169 | goto mip6_rthdr_xfrm_fail; | 332 | goto mip6_rthdr_xfrm_fail; |
@@ -171,6 +334,8 @@ int __init mip6_init(void) | |||
171 | return 0; | 334 | return 0; |
172 | 335 | ||
173 | mip6_rthdr_xfrm_fail: | 336 | mip6_rthdr_xfrm_fail: |
337 | xfrm_unregister_type(&mip6_destopt_type, AF_INET6); | ||
338 | mip6_destopt_xfrm_fail: | ||
174 | return -EAGAIN; | 339 | return -EAGAIN; |
175 | } | 340 | } |
176 | 341 | ||
@@ -178,4 +343,6 @@ void __exit mip6_fini(void) | |||
178 | { | 343 | { |
179 | if (xfrm_unregister_type(&mip6_rthdr_type, AF_INET6) < 0) | 344 | if (xfrm_unregister_type(&mip6_rthdr_type, AF_INET6) < 0) |
180 | printk(KERN_INFO "%s: can't remove xfrm type(rthdr)\n", __FUNCTION__); | 345 | printk(KERN_INFO "%s: can't remove xfrm type(rthdr)\n", __FUNCTION__); |
346 | if (xfrm_unregister_type(&mip6_destopt_type, AF_INET6) < 0) | ||
347 | printk(KERN_INFO "%s: can't remove xfrm type(destopt)\n", __FUNCTION__); | ||
181 | } | 348 | } |