aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2007-04-09 14:47:18 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2007-04-26 01:28:38 -0400
commitc5c2523893747f88a83376abad310c8ad13f7197 (patch)
tree58f1ab25ac9f7ca7460abbd24e9bab9c8683f6fd /net
parent557922584d9c5b6b990bcfb2fec3134f0e73a05d (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>
Diffstat (limited to 'net')
-rw-r--r--net/ipv4/esp4.c30
-rw-r--r--net/ipv6/esp6.c22
-rw-r--r--net/xfrm/xfrm_state.c36
3 files changed, 38 insertions, 50 deletions
diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c
index bdc65d8af18..a315d5d2276 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
275static u32 esp4_get_max_size(struct xfrm_state *x, int mtu) 275static 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
303static void esp4_err(struct sk_buff *skb, u32 info) 305static 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
427error: 433error:
@@ -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 6b76c4c3113..7107bb7e2e6 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
238static u32 esp6_get_max_size(struct xfrm_state *x, int mtu) 238static 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
256static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 258static 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 63a20e81816..69a3600afd9 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}
1668EXPORT_SYMBOL(xfrm_state_delete_tunnel); 1668EXPORT_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 */
1676int xfrm_state_mtu(struct xfrm_state *x, int mtu) 1670int 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