diff options
author | Patrick McHardy <kaber@trash.net> | 2007-04-09 14:47:18 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-04-26 01:28:38 -0400 |
commit | c5c2523893747f88a83376abad310c8ad13f7197 (patch) | |
tree | 58f1ab25ac9f7ca7460abbd24e9bab9c8683f6fd | |
parent | 557922584d9c5b6b990bcfb2fec3134f0e73a05d (diff) |
[XFRM]: Optimize MTU calculation
Replace the probing based MTU estimation, which usually takes 2-3 iterations
to find a fitting value and may underestimate the MTU, by an exact calculation.
Also fix underestimation of the XFRM trailer_len, which causes unnecessary
reallocations.
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/xfrm.h | 2 | ||||
-rw-r--r-- | net/ipv4/esp4.c | 30 | ||||
-rw-r--r-- | net/ipv6/esp6.c | 22 | ||||
-rw-r--r-- | net/xfrm/xfrm_state.c | 36 |
4 files changed, 39 insertions, 51 deletions
diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 5a00aa85b756..e144a25814bd 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h | |||
@@ -279,7 +279,7 @@ struct xfrm_type | |||
279 | xfrm_address_t *(*local_addr)(struct xfrm_state *, xfrm_address_t *); | 279 | xfrm_address_t *(*local_addr)(struct xfrm_state *, xfrm_address_t *); |
280 | xfrm_address_t *(*remote_addr)(struct xfrm_state *, xfrm_address_t *); | 280 | xfrm_address_t *(*remote_addr)(struct xfrm_state *, xfrm_address_t *); |
281 | /* Estimate maximal size of result of transformation of a dgram */ | 281 | /* Estimate maximal size of result of transformation of a dgram */ |
282 | u32 (*get_max_size)(struct xfrm_state *, int size); | 282 | u32 (*get_mtu)(struct xfrm_state *, int size); |
283 | }; | 283 | }; |
284 | 284 | ||
285 | extern int xfrm_register_type(struct xfrm_type *type, unsigned short family); | 285 | extern int xfrm_register_type(struct xfrm_type *type, unsigned short family); |
diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index bdc65d8af181..a315d5d22764 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c | |||
@@ -272,32 +272,34 @@ out: | |||
272 | return -EINVAL; | 272 | return -EINVAL; |
273 | } | 273 | } |
274 | 274 | ||
275 | static u32 esp4_get_max_size(struct xfrm_state *x, int mtu) | 275 | static u32 esp4_get_mtu(struct xfrm_state *x, int mtu) |
276 | { | 276 | { |
277 | struct esp_data *esp = x->data; | 277 | struct esp_data *esp = x->data; |
278 | u32 blksize = ALIGN(crypto_blkcipher_blocksize(esp->conf.tfm), 4); | 278 | u32 blksize = ALIGN(crypto_blkcipher_blocksize(esp->conf.tfm), 4); |
279 | int enclen = 0; | 279 | u32 align = max_t(u32, blksize, esp->conf.padlen); |
280 | u32 rem; | ||
281 | |||
282 | mtu -= x->props.header_len + esp->auth.icv_trunc_len; | ||
283 | rem = mtu & (align - 1); | ||
284 | mtu &= ~(align - 1); | ||
280 | 285 | ||
281 | switch (x->props.mode) { | 286 | switch (x->props.mode) { |
282 | case XFRM_MODE_TUNNEL: | 287 | case XFRM_MODE_TUNNEL: |
283 | mtu = ALIGN(mtu +2, blksize); | ||
284 | break; | 288 | break; |
285 | default: | 289 | default: |
286 | case XFRM_MODE_TRANSPORT: | 290 | case XFRM_MODE_TRANSPORT: |
287 | /* The worst case */ | 291 | /* The worst case */ |
288 | mtu = ALIGN(mtu + 2, 4) + blksize - 4; | 292 | mtu -= blksize - 4; |
293 | mtu += min_t(u32, blksize - 4, rem); | ||
289 | break; | 294 | break; |
290 | case XFRM_MODE_BEET: | 295 | case XFRM_MODE_BEET: |
291 | /* The worst case. */ | 296 | /* The worst case. */ |
292 | enclen = IPV4_BEET_PHMAXLEN; | 297 | mtu -= IPV4_BEET_PHMAXLEN; |
293 | mtu = ALIGN(mtu + enclen + 2, blksize); | 298 | mtu += min_t(u32, IPV4_BEET_PHMAXLEN, rem); |
294 | break; | 299 | break; |
295 | } | 300 | } |
296 | 301 | ||
297 | if (esp->conf.padlen) | 302 | return mtu - 2; |
298 | mtu = ALIGN(mtu, esp->conf.padlen); | ||
299 | |||
300 | return mtu + x->props.header_len + esp->auth.icv_trunc_len - enclen; | ||
301 | } | 303 | } |
302 | 304 | ||
303 | static void esp4_err(struct sk_buff *skb, u32 info) | 305 | static void esp4_err(struct sk_buff *skb, u32 info) |
@@ -340,6 +342,7 @@ static int esp_init_state(struct xfrm_state *x) | |||
340 | { | 342 | { |
341 | struct esp_data *esp = NULL; | 343 | struct esp_data *esp = NULL; |
342 | struct crypto_blkcipher *tfm; | 344 | struct crypto_blkcipher *tfm; |
345 | u32 align; | ||
343 | 346 | ||
344 | /* null auth and encryption can have zero length keys */ | 347 | /* null auth and encryption can have zero length keys */ |
345 | if (x->aalg) { | 348 | if (x->aalg) { |
@@ -421,7 +424,10 @@ static int esp_init_state(struct xfrm_state *x) | |||
421 | } | 424 | } |
422 | } | 425 | } |
423 | x->data = esp; | 426 | x->data = esp; |
424 | x->props.trailer_len = esp4_get_max_size(x, 0) - x->props.header_len; | 427 | align = ALIGN(crypto_blkcipher_blocksize(esp->conf.tfm), 4); |
428 | if (esp->conf.padlen) | ||
429 | align = max_t(u32, align, esp->conf.padlen); | ||
430 | x->props.trailer_len = align + 1 + esp->auth.icv_trunc_len; | ||
425 | return 0; | 431 | return 0; |
426 | 432 | ||
427 | error: | 433 | error: |
@@ -438,7 +444,7 @@ static struct xfrm_type esp_type = | |||
438 | .proto = IPPROTO_ESP, | 444 | .proto = IPPROTO_ESP, |
439 | .init_state = esp_init_state, | 445 | .init_state = esp_init_state, |
440 | .destructor = esp_destroy, | 446 | .destructor = esp_destroy, |
441 | .get_max_size = esp4_get_max_size, | 447 | .get_mtu = esp4_get_mtu, |
442 | .input = esp_input, | 448 | .input = esp_input, |
443 | .output = esp_output | 449 | .output = esp_output |
444 | }; | 450 | }; |
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index 6b76c4c31137..7107bb7e2e62 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c | |||
@@ -235,22 +235,24 @@ out: | |||
235 | return ret; | 235 | return ret; |
236 | } | 236 | } |
237 | 237 | ||
238 | static u32 esp6_get_max_size(struct xfrm_state *x, int mtu) | 238 | static u32 esp6_get_mtu(struct xfrm_state *x, int mtu) |
239 | { | 239 | { |
240 | struct esp_data *esp = x->data; | 240 | struct esp_data *esp = x->data; |
241 | u32 blksize = ALIGN(crypto_blkcipher_blocksize(esp->conf.tfm), 4); | 241 | u32 blksize = ALIGN(crypto_blkcipher_blocksize(esp->conf.tfm), 4); |
242 | u32 align = max_t(u32, blksize, esp->conf.padlen); | ||
243 | u32 rem; | ||
242 | 244 | ||
243 | if (x->props.mode == XFRM_MODE_TUNNEL) { | 245 | mtu -= x->props.header_len + esp->auth.icv_trunc_len; |
244 | mtu = ALIGN(mtu + 2, blksize); | 246 | rem = mtu & (align - 1); |
245 | } else { | 247 | mtu &= ~(align - 1); |
246 | /* The worst case. */ | 248 | |
249 | if (x->props.mode != XFRM_MODE_TUNNEL) { | ||
247 | u32 padsize = ((blksize - 1) & 7) + 1; | 250 | u32 padsize = ((blksize - 1) & 7) + 1; |
248 | mtu = ALIGN(mtu + 2, padsize) + blksize - padsize; | 251 | mtu -= blksize - padsize; |
252 | mtu += min_t(u32, blksize - padsize, rem); | ||
249 | } | 253 | } |
250 | if (esp->conf.padlen) | ||
251 | mtu = ALIGN(mtu, esp->conf.padlen); | ||
252 | 254 | ||
253 | return mtu + x->props.header_len + esp->auth.icv_trunc_len; | 255 | return mtu - 2; |
254 | } | 256 | } |
255 | 257 | ||
256 | static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | 258 | static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, |
@@ -380,7 +382,7 @@ static struct xfrm_type esp6_type = | |||
380 | .proto = IPPROTO_ESP, | 382 | .proto = IPPROTO_ESP, |
381 | .init_state = esp6_init_state, | 383 | .init_state = esp6_init_state, |
382 | .destructor = esp6_destroy, | 384 | .destructor = esp6_destroy, |
383 | .get_max_size = esp6_get_max_size, | 385 | .get_mtu = esp6_get_mtu, |
384 | .input = esp6_input, | 386 | .input = esp6_input, |
385 | .output = esp6_output, | 387 | .output = esp6_output, |
386 | .hdr_offset = xfrm6_find_1stfragopt, | 388 | .hdr_offset = xfrm6_find_1stfragopt, |
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 63a20e818164..69a3600afd9d 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c | |||
@@ -1667,37 +1667,17 @@ void xfrm_state_delete_tunnel(struct xfrm_state *x) | |||
1667 | } | 1667 | } |
1668 | EXPORT_SYMBOL(xfrm_state_delete_tunnel); | 1668 | EXPORT_SYMBOL(xfrm_state_delete_tunnel); |
1669 | 1669 | ||
1670 | /* | ||
1671 | * This function is NOT optimal. For example, with ESP it will give an | ||
1672 | * MTU that's usually two bytes short of being optimal. However, it will | ||
1673 | * usually give an answer that's a multiple of 4 provided the input is | ||
1674 | * also a multiple of 4. | ||
1675 | */ | ||
1676 | int xfrm_state_mtu(struct xfrm_state *x, int mtu) | 1670 | int xfrm_state_mtu(struct xfrm_state *x, int mtu) |
1677 | { | 1671 | { |
1678 | int res = mtu; | 1672 | int res; |
1679 | |||
1680 | res -= x->props.header_len; | ||
1681 | |||
1682 | for (;;) { | ||
1683 | int m = res; | ||
1684 | |||
1685 | if (m < 68) | ||
1686 | return 68; | ||
1687 | |||
1688 | spin_lock_bh(&x->lock); | ||
1689 | if (x->km.state == XFRM_STATE_VALID && | ||
1690 | x->type && x->type->get_max_size) | ||
1691 | m = x->type->get_max_size(x, m); | ||
1692 | else | ||
1693 | m += x->props.header_len; | ||
1694 | spin_unlock_bh(&x->lock); | ||
1695 | |||
1696 | if (m <= mtu) | ||
1697 | break; | ||
1698 | res -= (m - mtu); | ||
1699 | } | ||
1700 | 1673 | ||
1674 | spin_lock_bh(&x->lock); | ||
1675 | if (x->km.state == XFRM_STATE_VALID && | ||
1676 | x->type && x->type->get_mtu) | ||
1677 | res = x->type->get_mtu(x, mtu); | ||
1678 | else | ||
1679 | res = mtu; | ||
1680 | spin_unlock_bh(&x->lock); | ||
1701 | return res; | 1681 | return res; |
1702 | } | 1682 | } |
1703 | 1683 | ||