linux/net/ipv4/xfrm4_state.c
Herbert Xu dd87147eed [IPSEC]: Add XFRM_STATE_NOPMTUDISC flag
This patch adds the flag XFRM_STATE_NOPMTUDISC for xfrm states.  It is
similar to the nopmtudisc on IPIP/GRE tunnels.  It only has an effect
on IPv4 tunnel mode states.  For these states, it will ensure that the
DF flag is always cleared.

This is primarily useful to work around ICMP blackholes.

In future this flag could also allow a larger MTU to be set within the
tunnel just like IPIP/GRE tunnels.  This could be useful for short haul
tunnels where temporary fragmentation outside the tunnel is desired over
smaller fragments inside the tunnel.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Acked-by: James Morris <jmorris@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2005-06-20 13:21:43 -07:00

135 lines
3.2 KiB
C

/*
* xfrm4_state.c
*
* Changes:
* YOSHIFUJI Hideaki @USAGI
* Split up af-specific portion
*
*/
#include <net/ip.h>
#include <net/xfrm.h>
#include <linux/pfkeyv2.h>
#include <linux/ipsec.h>
static struct xfrm_state_afinfo xfrm4_state_afinfo;
static int xfrm4_init_flags(struct xfrm_state *x)
{
if (ipv4_config.no_pmtu_disc)
x->props.flags |= XFRM_STATE_NOPMTUDISC;
return 0;
}
static void
__xfrm4_init_tempsel(struct xfrm_state *x, struct flowi *fl,
struct xfrm_tmpl *tmpl,
xfrm_address_t *daddr, xfrm_address_t *saddr)
{
x->sel.daddr.a4 = fl->fl4_dst;
x->sel.saddr.a4 = fl->fl4_src;
x->sel.dport = xfrm_flowi_dport(fl);
x->sel.dport_mask = ~0;
x->sel.sport = xfrm_flowi_sport(fl);
x->sel.sport_mask = ~0;
x->sel.prefixlen_d = 32;
x->sel.prefixlen_s = 32;
x->sel.proto = fl->proto;
x->sel.ifindex = fl->oif;
x->id = tmpl->id;
if (x->id.daddr.a4 == 0)
x->id.daddr.a4 = daddr->a4;
x->props.saddr = tmpl->saddr;
if (x->props.saddr.a4 == 0)
x->props.saddr.a4 = saddr->a4;
x->props.mode = tmpl->mode;
x->props.reqid = tmpl->reqid;
x->props.family = AF_INET;
}
static struct xfrm_state *
__xfrm4_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto)
{
unsigned h = __xfrm4_spi_hash(daddr, spi, proto);
struct xfrm_state *x;
list_for_each_entry(x, xfrm4_state_afinfo.state_byspi+h, byspi) {
if (x->props.family == AF_INET &&
spi == x->id.spi &&
daddr->a4 == x->id.daddr.a4 &&
proto == x->id.proto) {
xfrm_state_hold(x);
return x;
}
}
return NULL;
}
static struct xfrm_state *
__xfrm4_find_acq(u8 mode, u32 reqid, u8 proto,
xfrm_address_t *daddr, xfrm_address_t *saddr,
int create)
{
struct xfrm_state *x, *x0;
unsigned h = __xfrm4_dst_hash(daddr);
x0 = NULL;
list_for_each_entry(x, xfrm4_state_afinfo.state_bydst+h, bydst) {
if (x->props.family == AF_INET &&
daddr->a4 == x->id.daddr.a4 &&
mode == x->props.mode &&
proto == x->id.proto &&
saddr->a4 == x->props.saddr.a4 &&
reqid == x->props.reqid &&
x->km.state == XFRM_STATE_ACQ &&
!x->id.spi) {
x0 = x;
break;
}
}
if (!x0 && create && (x0 = xfrm_state_alloc()) != NULL) {
x0->sel.daddr.a4 = daddr->a4;
x0->sel.saddr.a4 = saddr->a4;
x0->sel.prefixlen_d = 32;
x0->sel.prefixlen_s = 32;
x0->props.saddr.a4 = saddr->a4;
x0->km.state = XFRM_STATE_ACQ;
x0->id.daddr.a4 = daddr->a4;
x0->id.proto = proto;
x0->props.family = AF_INET;
x0->props.mode = mode;
x0->props.reqid = reqid;
x0->props.family = AF_INET;
x0->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
xfrm_state_hold(x0);
x0->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
add_timer(&x0->timer);
xfrm_state_hold(x0);
list_add_tail(&x0->bydst, xfrm4_state_afinfo.state_bydst+h);
wake_up(&km_waitq);
}
if (x0)
xfrm_state_hold(x0);
return x0;
}
static struct xfrm_state_afinfo xfrm4_state_afinfo = {
.family = AF_INET,
.lock = RW_LOCK_UNLOCKED,
.init_flags = xfrm4_init_flags,
.init_tempsel = __xfrm4_init_tempsel,
.state_lookup = __xfrm4_state_lookup,
.find_acq = __xfrm4_find_acq,
};
void __init xfrm4_state_init(void)
{
xfrm_state_register_afinfo(&xfrm4_state_afinfo);
}
void __exit xfrm4_state_fini(void)
{
xfrm_state_unregister_afinfo(&xfrm4_state_afinfo);
}