#include <net/xfrm.h>
#include <linux/pfkeyv2.h>
#include <linux/ipsec.h>
+#include <net/ipv6.h>
/* Each xfrm_state may be linked to two tables:
spin_lock_bh(&xfrm_state_lock);
list_for_each_entry(x, xfrm_state_bydst+h, bydst) {
- if (daddr == x->id.daddr.xfrm4_addr &&
+ if (x->props.family == AF_INET &&
+ daddr == x->id.daddr.xfrm4_addr &&
x->props.reqid == tmpl->reqid &&
(saddr == x->props.saddr.xfrm4_addr || !saddr || !x->props.saddr.xfrm4_addr) &&
tmpl->mode == x->props.mode &&
x->id = tmpl->id;
if (x->id.daddr.xfrm4_addr == 0)
x->id.daddr.xfrm4_addr = daddr;
+ x->props.family = AF_INET;
x->props.saddr = tmpl->saddr;
if (x->props.saddr.xfrm4_addr == 0)
x->props.saddr.xfrm4_addr = saddr;
void xfrm_state_insert(struct xfrm_state *x)
{
- unsigned h = ntohl(x->id.daddr.xfrm4_addr);
+ unsigned h = 0;
+
+ if (x->props.family == AF_INET)
+ h = ntohl(x->id.daddr.xfrm4_addr);
+ else if (x->props.family == AF_INET6)
+ h = ntohl(x->id.daddr.a6[2]^x->id.daddr.a6[3]);
h = (h ^ (h>>16)) % XFRM_DST_HSIZE;
list_add(&x->bydst, xfrm_state_bydst+h);
atomic_inc(&x->refcnt);
- h = ntohl(x->id.daddr.xfrm4_addr^x->id.spi^x->id.proto);
+ if (x->props.family == AF_INET)
+ h = ntohl(x->id.daddr.xfrm4_addr^x->id.spi^x->id.proto);
+ else
+ h = ntohl(x->id.daddr.a6[2]^x->id.daddr.a6[3]^x->id.spi^x->id.proto);
h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE;
list_add(&x->byspi, xfrm_state_byspi+h);
atomic_inc(&x->refcnt);
spin_lock_bh(&xfrm_state_lock);
list_for_each_entry(x, xfrm_state_byspi+h, byspi) {
- if (spi == x->id.spi &&
+ if (x->props.family == AF_INET &&
+ spi == x->id.spi &&
daddr == x->id.daddr.xfrm4_addr &&
proto == x->id.proto) {
atomic_inc(&x->refcnt);
spin_lock_bh(&xfrm_state_lock);
list_for_each_entry(x, xfrm_state_bydst+h, bydst) {
- if (daddr == x->id.daddr.xfrm4_addr &&
+ if (x->props.family == AF_INET &&
+ daddr == x->id.daddr.xfrm4_addr &&
mode == x->props.mode &&
proto == x->id.proto &&
saddr == x->props.saddr.xfrm4_addr &&
x0->km.state = XFRM_STATE_ACQ;
x0->id.daddr.xfrm4_addr = daddr;
x0->id.proto = proto;
+ x0->props.family = AF_INET;
x0->props.mode = mode;
x0->props.reqid = reqid;
x0->lft.hard_add_expires_seconds = ACQ_EXPIRES;
int i;
for (i=0; i<n; i++) {
- if (!xfrm4_selector_match(&x[i]->sel, fl))
+ if (x[i]->props.family == AF_INET &&
+ !xfrm4_selector_match(&x[i]->sel, fl))
+ return -EINVAL;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ if (x[i]->props.family == AF_INET6 &&
+ !xfrm6_selector_match(&x[i]->sel, fl))
return -EINVAL;
+#endif
}
return 0;
}
INIT_LIST_HEAD(&xfrm_state_byspi[i]);
}
}
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+struct xfrm_state *
+xfrm6_state_lookup(struct in6_addr *daddr, u32 spi, u8 proto)
+{
+ unsigned h = ntohl(daddr->s6_addr32[2]^daddr->s6_addr32[3]^spi^proto);
+ struct xfrm_state *x;
+
+ h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE;
+
+ spin_lock_bh(&xfrm_state_lock);
+ list_for_each_entry(x, xfrm_state_byspi+h, byspi) {
+ if (x->props.family == AF_INET6 &&
+ spi == x->id.spi &&
+ !ipv6_addr_cmp(daddr, (struct in6_addr *)x->id.daddr.a6) &&
+ proto == x->id.proto) {
+ atomic_inc(&x->refcnt);
+ spin_unlock_bh(&xfrm_state_lock);
+ return x;
+ }
+ }
+ spin_unlock_bh(&xfrm_state_lock);
+ return NULL;
+}
+
+struct xfrm_state *
+xfrm6_find_acq(u8 mode, u16 reqid, u8 proto, struct in6_addr *daddr, struct in6_addr *saddr, int create)
+{
+ struct xfrm_state *x, *x0;
+ unsigned h = ntohl(daddr->s6_addr32[2]^daddr->s6_addr32[3]);
+
+ h = (h ^ (h>>16)) % XFRM_DST_HSIZE;
+ x0 = NULL;
+
+ spin_lock_bh(&xfrm_state_lock);
+ list_for_each_entry(x, xfrm_state_bydst+h, bydst) {
+ if (x->props.family == AF_INET6 &&
+ !memcmp(daddr, x->id.daddr.a6, sizeof(struct in6_addr)) &&
+ mode == x->props.mode &&
+ proto == x->id.proto &&
+ !memcmp(saddr, x->props.saddr.a6, sizeof(struct in6_addr)) &&
+ reqid == x->props.reqid &&
+ x->km.state == XFRM_STATE_ACQ) {
+ if (!x0)
+ x0 = x;
+ if (x->id.spi)
+ continue;
+ x0 = x;
+ break;
+ }
+ }
+ if (x0) {
+ atomic_inc(&x0->refcnt);
+ } else if (create && (x0 = xfrm_state_alloc()) != NULL) {
+ memcpy(x0->sel.daddr.a6, daddr, sizeof(struct in6_addr));
+ memcpy(x0->sel.saddr.a6, saddr, sizeof(struct in6_addr));
+ x0->sel.prefixlen_d = 128;
+ x0->sel.prefixlen_s = 128;
+ memcpy(x0->props.saddr.a6, saddr, sizeof(struct in6_addr));
+ x0->km.state = XFRM_STATE_ACQ;
+ memcpy(x0->id.daddr.a6, daddr, sizeof(struct in6_addr));
+ x0->id.proto = proto;
+ x0->props.family = AF_INET6;
+ x0->props.mode = mode;
+ x0->props.reqid = reqid;
+ x0->lft.hard_add_expires_seconds = ACQ_EXPIRES;
+ atomic_inc(&x0->refcnt);
+ mod_timer(&x0->timer, jiffies + ACQ_EXPIRES*HZ);
+ atomic_inc(&x0->refcnt);
+ list_add_tail(&x0->bydst, xfrm_state_bydst+h);
+ wake_up(&km_waitq);
+ }
+ spin_unlock_bh(&xfrm_state_lock);
+ return x0;
+}
+
+void
+xfrm6_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi)
+{
+ u32 h;
+ struct xfrm_state *x0;
+
+ if (x->id.spi)
+ return;
+
+ if (minspi == maxspi) {
+ x0 = xfrm6_state_lookup((struct in6_addr*)x->id.daddr.a6, minspi, x->id.proto);
+ if (x0) {
+ xfrm_state_put(x0);
+ return;
+ }
+ x->id.spi = minspi;
+ } else {
+ u32 spi = 0;
+ minspi = ntohl(minspi);
+ maxspi = ntohl(maxspi);
+ for (h=0; h<maxspi-minspi+1; h++) {
+ spi = minspi + net_random()%(maxspi-minspi+1);
+ x0 = xfrm6_state_lookup((struct in6_addr*)x->id.daddr.a6, htonl(spi), x->id.proto);
+ if (x0 == NULL)
+ break;
+ xfrm_state_put(x0);
+ }
+ x->id.spi = htonl(spi);
+ }
+ if (x->id.spi) {
+ spin_lock_bh(&xfrm_state_lock);
+ h = ntohl(x->id.daddr.a6[2]^x->id.daddr.a6[3]^x->id.spi^x->id.proto);
+ h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE;
+ list_add(&x->byspi, xfrm_state_byspi+h);
+ atomic_inc(&x->refcnt);
+ spin_unlock_bh(&xfrm_state_lock);
+ wake_up(&km_waitq);
+ }
+}
+#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
* Authors: Maxim Giryaev <gem@asplinux.ru>
* David S. Miller <davem@redhat.com>
* Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
+ * Kunihiro Ishiguro <kunihiro@ipinfusion.com>
*/
#include <linux/config.h>
struct sadb_address *sp = p;
struct sockaddr *addr = (struct sockaddr *)(sp + 1);
struct sockaddr_in *sin;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
struct sockaddr_in6 *sin6;
+#endif
int len;
switch (addr->sa_family) {
sp->sadb_address_prefixlen > 32)
return -EINVAL;
break;
-
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6:
len = sizeof(*sp) + sizeof(*sin6) + (sizeof(uint64_t) - 1);
len /= sizeof(uint64_t);
sp->sadb_address_prefixlen > 128)
return -EINVAL;
break;
-
+#endif
default:
/* It is user using kernel to keep track of security
* associations for another protocol, such as
d_addr = (struct sockaddr *)(dst + 1);
if (s_addr->sa_family != d_addr->sa_family)
return 0;
- if (s_addr->sa_family != AF_INET)
+ if (s_addr->sa_family != AF_INET
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ && s_addr->sa_family != AF_INET6
+#endif
+ )
return 0;
return 1;
return (proto ? proto : IPSEC_PROTO_ANY);
}
-static xfrm_address_t *pfkey_sadb_addr2xfrm_addr(struct sadb_address *addr,
- xfrm_address_t *xaddr)
+int pfkey_sadb_addr2xfrm_addr(struct sadb_address *addr,
+ xfrm_address_t *xaddr)
{
switch (((struct sockaddr*)(addr + 1))->sa_family) {
case AF_INET:
xaddr->xfrm4_addr =
- ((struct sockaddr_in*)(addr + 1))->sin_addr.s_addr;
+ ((struct sockaddr_in *)(addr + 1))->sin_addr.s_addr;
if (addr->sadb_address_prefixlen)
xaddr->xfrm4_mask = htonl(~0 << (32 - addr->sadb_address_prefixlen));
- break;
+ return AF_INET;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6:
memcpy(xaddr->a6,
- &((struct sockaddr_in6*)(addr + 1))->sin6_addr,
- sizeof(xaddr->a6));
+ &((struct sockaddr_in6 *)(addr + 1))->sin6_addr,
+ sizeof(struct in6_addr));
+ return AF_INET6;
+#endif
default:
- return NULL;
+ return 0;
}
-
- return xaddr;
+ /* NOTREACHED */
}
static struct xfrm_state *pfkey_xfrm_state_lookup(struct sadb_msg *hdr, void **ext_hdrs)
switch (((struct sockaddr *)(addr + 1))->sa_family) {
case AF_INET:
- x = xfrm_state_lookup(((struct sockaddr_in*)(addr + 1))->sin_addr.s_addr,
+ x = xfrm_state_lookup(((struct sockaddr_in *)(addr + 1))->sin_addr.s_addr,
sa->sadb_sa_spi, proto);
break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6:
- /* XXX handle IPv6 */
+ x = xfrm6_state_lookup(&((struct sockaddr_in6 *)(addr + 1))->sin6_addr,
+ sa->sadb_sa_spi, proto);
+ break;
+#endif
default:
x = NULL;
break;
}
#define PFKEY_ALIGN8(a) (1 + (((a) - 1) | (8 - 1)))
+static int
+pfkey_sockaddr_size(sa_family_t family)
+{
+ switch (family) {
+ case AF_INET:
+ return PFKEY_ALIGN8(sizeof(struct sockaddr_in));
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case AF_INET6:
+ return PFKEY_ALIGN8(sizeof(struct sockaddr_in6));
+#endif
+ default:
+ return 0;
+ }
+ /* NOTREACHED */
+}
+
static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys, int hsc)
{
struct sk_buff *skb;
struct sadb_key *key;
struct sadb_x_sa2 *sa2;
struct sockaddr_in *sin;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ struct sockaddr_in6 *sin6;
+#endif
int size;
int auth_key_size = 0;
int encrypt_key_size = 0;
+ int sockaddr_size;
+
+ /* address family check */
+ sockaddr_size = pfkey_sockaddr_size(x->props.family);
+ if (!sockaddr_size)
+ ERR_PTR(-EINVAL);
/* base, SA, (lifetime (HSC),) address(SD), (address(P),)
key(AE), (identity(SD),) (sensitivity)> */
((hsc & 1) ? sizeof(struct sadb_lifetime) : 0) +
((hsc & 2) ? sizeof(struct sadb_lifetime) : 0) +
sizeof(struct sadb_address)*2 +
- sizeof(struct sockaddr_in)*2 + /* XXX */
+ sockaddr_size*2 +
sizeof(struct sadb_x_sa2);
- /* XXX identity & sensitivity */
+ /* identity & sensitivity */
- if (x->sel.saddr.xfrm4_addr != x->props.saddr.xfrm4_addr)
- size += sizeof(struct sadb_address) +
- sizeof(struct sockaddr_in); /* XXX */
+ if ((x->props.family == AF_INET &&
+ x->sel.saddr.xfrm4_addr != x->props.saddr.xfrm4_addr) ||
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ || (x->props.family == AF_INET6 &&
+ memcmp (x->sel.saddr.a6, x->props.saddr.a6, sizeof (struct in6_addr)))
+#endif
+ )
+ size += sizeof(struct sadb_address) + sockaddr_size;
if (add_keys) {
if (x->aalg && x->aalg->alg_key_len) {
lifetime->sadb_lifetime_usetime = x->curlft.use_time;
/* src address */
addr = (struct sadb_address*) skb_put(skb,
- sizeof(struct sadb_address)+sizeof(struct sockaddr_in));
+ sizeof(struct sadb_address)+sockaddr_size);
addr->sadb_address_len =
- (sizeof(struct sadb_address)+sizeof(struct sockaddr_in))/
+ (sizeof(struct sadb_address)+sockaddr_size)/
sizeof(uint64_t);
addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
/* "if the ports are non-zero, then the sadb_address_proto field,
normally zero, MUST be filled in with the transport
protocol's number." - RFC2367 */
addr->sadb_address_proto = 0;
- addr->sadb_address_prefixlen = 32; /* XXX */
addr->sadb_address_reserved = 0;
- sin = (struct sockaddr_in *) (addr + 1);
- sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = x->props.saddr.xfrm4_addr;
- sin->sin_port = 0;
- memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+ if (x->props.family == AF_INET) {
+ addr->sadb_address_prefixlen = 32;
+
+ sin = (struct sockaddr_in *) (addr + 1);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = x->props.saddr.xfrm4_addr;
+ sin->sin_port = 0;
+ memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+ }
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ else if (x->props.family == AF_INET6) {
+ addr->sadb_address_prefixlen = 128;
+
+ sin6 = (struct sockaddr_in6 *) (addr + 1);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = 0;
+ sin6->sin6_flowinfo = 0;
+ memcpy(&sin6->sin6_addr, x->props.saddr.a6,
+ sizeof(struct in6_addr));
+ sin6->sin6_scope_id = 0;
+ }
+#endif
+ else
+ BUG();
+
/* dst address */
addr = (struct sadb_address*) skb_put(skb,
- sizeof(struct sadb_address)+sizeof(struct sockaddr_in));
+ sizeof(struct sadb_address)+sockaddr_size);
addr->sadb_address_len =
- (sizeof(struct sadb_address)+sizeof(struct sockaddr_in))/
+ (sizeof(struct sadb_address)+sockaddr_size)/
sizeof(uint64_t);
addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
addr->sadb_address_proto = 0;
addr->sadb_address_prefixlen = 32; /* XXX */
addr->sadb_address_reserved = 0;
- sin = (struct sockaddr_in *) (addr + 1);
- sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = x->id.daddr.xfrm4_addr;
- sin->sin_port = 0;
- memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
-
- if (x->sel.saddr.xfrm4_addr != x->props.saddr.xfrm4_addr) {
- addr = (struct sadb_address*) skb_put(skb,
- sizeof(struct sadb_address)+sizeof(struct sockaddr_in));
- addr->sadb_address_len =
- (sizeof(struct sadb_address)+sizeof(struct sockaddr_in))/
- sizeof(uint64_t);
- addr->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
- addr->sadb_address_proto = pfkey_proto_from_xfrm(x->sel.proto);
- addr->sadb_address_prefixlen = x->sel.prefixlen_s;
- addr->sadb_address_reserved = 0;
- sin = (struct sockaddr_in*)(addr + 1);
+ if (x->props.family == AF_INET) {
+ sin = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = x->sel.saddr.xfrm4_addr;
- sin->sin_port = x->sel.sport;
+ sin->sin_addr.s_addr = x->id.daddr.xfrm4_addr;
+ sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+
+ if (x->sel.saddr.xfrm4_addr != x->props.saddr.xfrm4_addr) {
+ addr = (struct sadb_address*) skb_put(skb,
+ sizeof(struct sadb_address)+sockaddr_size);
+ addr->sadb_address_len =
+ (sizeof(struct sadb_address)+sockaddr_size)/
+ sizeof(uint64_t);
+ addr->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
+ addr->sadb_address_proto =
+ pfkey_proto_from_xfrm(x->sel.proto);
+ addr->sadb_address_prefixlen = x->sel.prefixlen_s;
+ addr->sadb_address_reserved = 0;
+
+ sin = (struct sockaddr_in *) (addr + 1);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = x->sel.saddr.xfrm4_addr;
+ sin->sin_port = x->sel.sport;
+ memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+ }
+ }
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ else if (x->props.family == AF_INET6) {
+ addr->sadb_address_prefixlen = 128;
+
+ sin6 = (struct sockaddr_in6 *) (addr + 1);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = 0;
+ sin6->sin6_flowinfo = 0;
+ memcpy(&sin6->sin6_addr, x->id.daddr.a6, sizeof(struct in6_addr));
+ sin6->sin6_scope_id = 0;
+
+ if (memcmp (x->sel.saddr.a6, x->props.saddr.a6,
+ sizeof(struct in6_addr))) {
+ addr = (struct sadb_address *) skb_put(skb,
+ sizeof(struct sadb_address)+sockaddr_size);
+ addr->sadb_address_len =
+ (sizeof(struct sadb_address)+sockaddr_size)/
+ sizeof(uint64_t);
+ addr->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
+ addr->sadb_address_proto =
+ pfkey_proto_from_xfrm(x->sel.proto);
+ addr->sadb_address_prefixlen = x->sel.prefixlen_s;
+ addr->sadb_address_reserved = 0;
+
+ sin6 = (struct sockaddr_in6 *) (addr + 1);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = x->sel.sport;
+ sin6->sin6_flowinfo = 0;
+ memcpy(&sin6->sin6_addr, x->sel.saddr.a6,
+ sizeof(struct in6_addr));
+ sin6->sin6_scope_id = 0;
+ }
}
+#endif
+ else
+ BUG();
/* auth key */
if (add_keys && auth_key_size) {
}
/* x->algo.flags = sa->sadb_sa_flags; */
- pfkey_sadb_addr2xfrm_addr((struct sadb_address *) ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
- &x->props.saddr);
+ x->props.family = pfkey_sadb_addr2xfrm_addr((struct sadb_address *) ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
+ &x->props.saddr);
+ if (!x->props.family)
+ goto out;
pfkey_sadb_addr2xfrm_addr((struct sadb_address *) ext_hdrs[SADB_EXT_ADDRESS_DST-1],
&x->id.daddr);
x->sel.prefixlen_s = addr->sadb_address_prefixlen;
}
- x->type = xfrm_get_type(proto);
+ switch (x->props.family) {
+ case AF_INET:
+ x->type = xfrm_get_type(proto);
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case AF_INET6:
+ x->type = xfrm6_get_type(proto);
+ break;
+#endif
+ default:
+ x->type = NULL;
+ break;
+ }
+
if (x->type == NULL)
goto out;
if (x->type->init_state(x, NULL))
{
struct sk_buff *resp_skb;
struct sadb_x_sa2 *sa2;
- struct sadb_address *addr;
- struct sockaddr_in *saddr, *daddr;
+ struct sadb_address *saddr, *daddr;
struct sadb_msg *out_hdr;
struct xfrm_state *x;
u8 mode;
reqid = 0;
}
- addr = ext_hdrs[SADB_EXT_ADDRESS_SRC-1];
- saddr = (struct sockaddr_in*)(addr + 1);
- addr = ext_hdrs[SADB_EXT_ADDRESS_DST-1];
- daddr = (struct sockaddr_in*)(addr + 1);
+ saddr = ext_hdrs[SADB_EXT_ADDRESS_SRC-1];
+ daddr = ext_hdrs[SADB_EXT_ADDRESS_DST-1];
+
+ switch (((struct sockaddr *)(saddr + 1))->sa_family) {
+ case AF_INET:
+ x = xfrm_find_acq(mode, reqid, proto,
+ ((struct sockaddr_in *)(daddr + 1))->sin_addr.s_addr,
+ ((struct sockaddr_in *)(saddr + 1))->sin_addr.s_addr, 1);
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case AF_INET6:
+ x = xfrm6_find_acq(mode, reqid, proto,
+ &((struct sockaddr_in6 *)(daddr + 1))->sin6_addr,
+ &((struct sockaddr_in6 *)(saddr + 1))->sin6_addr, 1);
+ break;
+#endif
+ default:
+ x = NULL;
+ break;
+ }
- x = xfrm_find_acq(mode, reqid, proto, daddr->sin_addr.s_addr,
- saddr->sin_addr.s_addr, 1);
if (x == NULL)
return -ENOENT;
min_spi = htonl(0x100);
max_spi = htonl(0x0fffffff);
}
- xfrm_alloc_spi(x, min_spi, max_spi);
+ switch (x->props.family) {
+ case AF_INET:
+ xfrm_alloc_spi(x, min_spi, max_spi);
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case AF_INET6:
+ xfrm6_alloc_spi(x, min_spi, max_spi);
+ break;
+#endif
+ default:
+ break;
+ }
if (x->id.spi)
resp_skb = pfkey_xfrm_state2msg(x, 0, 3);
}
/* XXX there is race condition */
x1 = pfkey_xfrm_state_lookup(hdr, ext_hdrs);
if (!x1) {
- x1 = xfrm_find_acq(x->props.mode, x->props.reqid, x->id.proto,
- x->id.daddr.xfrm4_addr,
- x->props.saddr.xfrm4_addr, 0);
+ switch (x->props.family) {
+ case AF_INET:
+ x1 = xfrm_find_acq(x->props.mode, x->props.reqid, x->id.proto,
+ x->id.daddr.xfrm4_addr,
+ x->props.saddr.xfrm4_addr, 0);
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case AF_INET6:
+ x1 = xfrm6_find_acq(x->props.mode, x->props.reqid, x->id.proto,
+ (struct in6_addr*)x->id.daddr.a6,
+ (struct in6_addr*)x->props.saddr.a6, 0);
+ break;
+#endif
+ default:
+ x1 = NULL;
+ break;
+ }
if (x1 && x1->id.spi != x->id.spi && x1->id.spi) {
xfrm_state_put(x1);
x1 = NULL;
parse_ipsecrequest(struct xfrm_policy *xp, struct sadb_x_ipsecrequest *rq)
{
struct xfrm_tmpl *t = xp->xfrm_vec + xp->xfrm_nr;
- struct sockaddr_in *addr;
+ struct sockaddr_in *sin;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ struct sockaddr_in6 *sin6;
+#endif
if (xp->xfrm_nr >= XFRM_MAX_DEPTH)
return -ELOOP;
/* addresses present only in tunnel mode */
if (t->mode) {
- addr = (void*)(rq+1);
- t->saddr.xfrm4_addr = addr->sin_addr.s_addr;
- addr++;
- t->id.daddr.xfrm4_addr = addr->sin_addr.s_addr;
+ switch (xp->family) {
+ case AF_INET:
+ sin = (void*)(rq+1);
+ if (sin->sin_family != AF_INET)
+ return -EINVAL;
+ t->saddr.xfrm4_addr = sin->sin_addr.s_addr;
+ sin++;
+ if (sin->sin_family != AF_INET)
+ return -EINVAL;
+ t->id.daddr.xfrm4_addr = sin->sin_addr.s_addr;
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case AF_INET6:
+ sin6 = (void *)(rq+1);
+ if (sin6->sin6_family != AF_INET6)
+ return -EINVAL;
+ memcpy(t->saddr.a6, &sin6->sin6_addr, sizeof(struct in6_addr));
+ sin6++;
+ if (sin6->sin6_family != AF_INET6)
+ return -EINVAL;
+ memcpy(t->id.daddr.a6, &sin6->sin6_addr, sizeof(struct in6_addr));
+ break;
+#endif
+ default:
+ return -EINVAL;
+ }
}
/* No way to set this via kame pfkey */
t->aalgos = t->ealgos = t->calgos = ~0;
static int pfkey_xfrm_policy2msg_size(struct xfrm_policy *xp)
{
+ int sockaddr_size = pfkey_sockaddr_size(xp->family);
+ int socklen = (xp->family == AF_INET ?
+ sizeof(struct sockaddr_in) :
+ sizeof(struct sockaddr_in6));
+
return sizeof(struct sadb_msg) +
(sizeof(struct sadb_lifetime) * 3) +
(sizeof(struct sadb_address) * 2) +
- (sizeof(struct sockaddr_in) * 2) + /* XXX */
+ (sockaddr_size * 2) +
sizeof(struct sadb_x_policy) +
(xp->xfrm_nr * (sizeof(struct sadb_x_ipsecrequest) +
- (sizeof(struct sockaddr_in) * 2)));
+ (socklen * 2)));
}
static struct sk_buff * pfkey_xfrm_policy2msg_prep(struct xfrm_policy *xp)
/* src address */
addr = (struct sadb_address*) skb_put(skb,
- sizeof(struct sadb_address)+sizeof(struct sockaddr_in));
+ sizeof(struct sadb_address)+sockaddr_size);
addr->sadb_address_len =
- (sizeof(struct sadb_address)+sizeof(struct sockaddr_in))/
+ (sizeof(struct sadb_address)+sockaddr_size)/
sizeof(uint64_t);
addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
addr->sadb_address_proto = pfkey_proto_from_xfrm(xp->selector.proto);
addr->sadb_address_prefixlen = xp->selector.prefixlen_s;
addr->sadb_address_reserved = 0;
/* src address */
- sin = (struct sockaddr_in*)(addr + 1);
- sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = xp->selector.saddr.xfrm4_addr;
- sin->sin_port = xp->selector.sport;
- memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+ if (xp->family == AF_INET) {
+ sin = (struct sockaddr_in *) (addr + 1);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = xp->selector.saddr.xfrm4_addr;
+ sin->sin_port = xp->selector.sport;
+ memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+ }
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ else if (xp->family == AF_INET6) {
+ sin6 = (struct sockaddr_in6 *) (addr + 1);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = xp->selector.sport;
+ sin6->sin6_flowinfo = 0;
+ memcpy(&sin6->sin6_addr, xp->selector.saddr.a6,
+ sizeof(struct in6_addr));;
+ sin6->sin6_scope_id = 0;
+ }
+#endif
+ else
+ BUG();
+
/* dst address */
addr = (struct sadb_address*) skb_put(skb,
- sizeof(struct sadb_address)+sizeof(struct sockaddr_in));
+ sizeof(struct sadb_address)+sockaddr_size);
addr->sadb_address_len =
- (sizeof(struct sadb_address)+sizeof(struct sockaddr_in))/
+ (sizeof(struct sadb_address)+sockaddr_size)/
sizeof(uint64_t);
addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
addr->sadb_address_proto = pfkey_proto_from_xfrm(xp->selector.proto);
addr->sadb_address_prefixlen = xp->selector.prefixlen_d;
addr->sadb_address_reserved = 0;
- sin = (struct sockaddr_in*)(addr + 1);
- sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = xp->selector.daddr.xfrm4_addr;
- sin->sin_port = xp->selector.dport;
- memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+ if (xp->family == AF_INET) {
+ sin = (struct sockaddr_in *) (addr + 1);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = xp->selector.daddr.xfrm4_addr;
+ sin->sin_port = xp->selector.dport;
+ memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+ }
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ else if (xp->family == AF_INET6) {
+ sin6 = (struct sockaddr_in6 *) (addr + 1);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = xp->selector.dport;
+ sin6->sin6_flowinfo = 0;
+ memcpy(&sin6->sin6_addr, xp->selector.daddr.a6,
+ sizeof(struct in6_addr));
+ sin6->sin6_scope_id = 0;
+ }
+#endif
+ else
+ BUG();
/* hard time */
lifetime = (struct sadb_lifetime *) skb_put(skb,
req_size = sizeof(struct sadb_x_ipsecrequest);
if (t->mode)
- req_size += 2*sizeof(struct sockaddr_in);
- else
- size -= 2*sizeof(struct sockaddr_in);
+ req_size += 2*socklen;
+ else
+ size -= 2*socklen;
rq = (void*)skb_put(skb, req_size);
pol->sadb_x_policy_len += req_size/8;
rq->sadb_x_ipsecrequest_len = req_size;
rq->sadb_x_ipsecrequest_level = IPSEC_LEVEL_USE;
rq->sadb_x_ipsecrequest_reqid = t->reqid;
if (t->mode) {
- sin = (void*)(rq+1);
- sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = t->saddr.xfrm4_addr;
- sin->sin_port = 0;
- memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
- sin++;
- sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = t->id.daddr.xfrm4_addr;
- sin->sin_port = 0;
- memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+ switch (xp->family) {
+ case AF_INET:
+ sin = (void*)(rq+1);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = t->saddr.xfrm4_addr;
+ sin->sin_port = 0;
+ memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+ sin++;
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = t->id.daddr.xfrm4_addr;
+ sin->sin_port = 0;
+ memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case AF_INET6:
+ sin6 = (void*)(rq+1);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = 0;
+ sin6->sin6_flowinfo = 0;
+ memcpy(&sin6->sin6_addr, t->saddr.a6,
+ sizeof(struct in6_addr));
+ sin6->sin6_scope_id = 0;
+
+ sin6++;
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = 0;
+ sin6->sin6_flowinfo = 0;
+ memcpy(&sin6->sin6_addr, t->id.daddr.a6,
+ sizeof(struct in6_addr));
+ sin6->sin6_scope_id = 0;
+ break;
+#endif
+ default:
+ break;
+ }
}
}
hdr->sadb_msg_len = size / sizeof(uint64_t);
XFRM_POLICY_BLOCK : XFRM_POLICY_ALLOW);
sa = ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
- pfkey_sadb_addr2xfrm_addr(sa, &xp->selector.saddr);
+ xp->family = pfkey_sadb_addr2xfrm_addr(sa, &xp->selector.saddr);
+ if (!xp->family) {
+ err = -EINVAL;
+ goto out;
+ }
xp->selector.prefixlen_s = sa->sadb_address_prefixlen;
xp->selector.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
- xp->selector.sport = ((struct sockaddr_in*)(sa+1))->sin_port;
+ xp->selector.sport = ((struct sockaddr_in *)(sa+1))->sin_port;
if (xp->selector.sport)
xp->selector.sport_mask = ~0;
*/
xp->selector.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
- xp->selector.dport = ((struct sockaddr_in*)(sa+1))->sin_port;
+ xp->selector.dport = ((struct sockaddr_in *)(sa+1))->sin_port;
if (xp->selector.dport)
xp->selector.dport_mask = ~0;
pfkey_sadb_addr2xfrm_addr(sa, &sel.saddr);
sel.prefixlen_s = sa->sadb_address_prefixlen;
sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
- sel.sport = ((struct sockaddr_in*)(sa+1))->sin_port;
+ sel.sport = ((struct sockaddr_in *)(sa+1))->sin_port;
if (sel.sport)
sel.sport_mask = ~0;
pfkey_sadb_addr2xfrm_addr(sa, &sel.daddr);
sel.prefixlen_d = sa->sadb_address_prefixlen;
sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
- sel.dport = ((struct sockaddr_in*)(sa+1))->sin_port;
+ sel.dport = ((struct sockaddr_in *)(sa+1))->sin_port;
if (sel.dport)
sel.dport_mask = ~0;
struct sadb_address *addr;
struct sadb_x_policy *pol;
struct sockaddr_in *sin;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ struct sockaddr_in6 *sin6;
+#endif
+ int sockaddr_size;
int size;
+ sockaddr_size = pfkey_sockaddr_size(x->props.family);
+ if (!sockaddr_size)
+ return -EINVAL;
+
size = sizeof(struct sadb_msg) +
- sizeof(struct sadb_address)*2 +
- sizeof(struct sockaddr_in)*2 + /* XXX */
- sizeof(struct sadb_x_policy);
+ (sizeof(struct sadb_address) * 2) +
+ (sockaddr_size * 2) +
+ sizeof(struct sadb_x_policy);
if (x->id.proto == IPPROTO_AH)
size += count_ah_combs(t);
/* src address */
addr = (struct sadb_address*) skb_put(skb,
- sizeof(struct sadb_address)+sizeof(struct sockaddr_in));
+ sizeof(struct sadb_address)+sockaddr_size);
addr->sadb_address_len =
- (sizeof(struct sadb_address)+sizeof(struct sockaddr_in))/
+ (sizeof(struct sadb_address)+sockaddr_size)/
sizeof(uint64_t);
addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
addr->sadb_address_proto = 0;
- addr->sadb_address_prefixlen = 32;
addr->sadb_address_reserved = 0;
- sin = (struct sockaddr_in*)(addr + 1);
- sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = x->props.saddr.xfrm4_addr;
- sin->sin_port = 0;
- memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+ if (x->props.family == AF_INET) {
+ addr->sadb_address_prefixlen = 32;
+
+ sin = (struct sockaddr_in *) (addr + 1);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = x->props.saddr.xfrm4_addr;
+ sin->sin_port = 0;
+ memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+ }
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ else if (x->props.family == AF_INET6) {
+ addr->sadb_address_prefixlen = 128;
+
+ sin6 = (struct sockaddr_in6 *) (addr + 1);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = 0;
+ sin6->sin6_flowinfo = 0;
+ memcpy(&sin6->sin6_addr,
+ x->props.saddr.a6, sizeof(struct in6_addr));
+ sin6->sin6_scope_id = 0;
+ }
+#endif
+ else
+ BUG();
/* dst address */
addr = (struct sadb_address*) skb_put(skb,
- sizeof(struct sadb_address)+sizeof(struct sockaddr_in));
+ sizeof(struct sadb_address)+sockaddr_size);
addr->sadb_address_len =
- (sizeof(struct sadb_address)+sizeof(struct sockaddr_in))/
+ (sizeof(struct sadb_address)+sockaddr_size)/
sizeof(uint64_t);
addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
addr->sadb_address_proto = 0;
- addr->sadb_address_prefixlen = 32;
addr->sadb_address_reserved = 0;
- sin = (struct sockaddr_in*)(addr + 1);
- sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = x->id.daddr.xfrm4_addr;
- sin->sin_port = 0;
- memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+ if (x->props.family == AF_INET) {
+ addr->sadb_address_prefixlen = 32;
+
+ sin = (struct sockaddr_in *) (addr + 1);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = x->id.daddr.xfrm4_addr;
+ sin->sin_port = 0;
+ memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+ }
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ else if (x->props.family == AF_INET6) {
+ addr->sadb_address_prefixlen = 128;
+
+ sin6 = (struct sockaddr_in6 *) (addr + 1);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = 0;
+ sin6->sin6_flowinfo = 0;
+ memcpy(&sin6->sin6_addr,
+ x->id.daddr.a6, sizeof(struct in6_addr));
+ sin6->sin6_scope_id = 0;
+ }
+#endif
+ else
+ BUG();
pol = (struct sadb_x_policy *) skb_put(skb, sizeof(struct sadb_x_policy));
pol->sadb_x_policy_len = sizeof(struct sadb_x_policy)/sizeof(uint64_t);