} __attribute__((packed));
/* sizeof(struct sadb_x_ipsecrequest) == 16 */
+/* This defines the TYPE of Nat Traversal in use. Currently only one
+ * type of NAT-T is supported, draft-ietf-ipsec-udp-encaps-06
+ */
+struct sadb_x_nat_t_type {
+ uint16_t sadb_x_nat_t_type_len;
+ uint16_t sadb_x_nat_t_type_exttype;
+ uint8_t sadb_x_nat_t_type_type;
+ uint8_t sadb_x_nat_t_type_reserved[3];
+} __attribute__((packed));
+/* sizeof(struct sadb_x_nat_t_type) == 8 */
+
+/* Pass a NAT Traversal port (Source or Dest port) */
+struct sadb_x_nat_t_port {
+ uint16_t sadb_x_nat_t_port_len;
+ uint16_t sadb_x_nat_t_port_exttype;
+ uint16_t sadb_x_nat_t_port_port;
+ uint16_t sadb_x_nat_t_port_reserved;
+} __attribute__((packed));
+/* sizeof(struct sadb_x_nat_t_port) == 8 */
+
/* Message types */
#define SADB_RESERVED 0
#define SADB_GETSPI 1
#define SADB_X_SPDSETIDX 20
#define SADB_X_SPDEXPIRE 21
#define SADB_X_SPDDELETE2 22
-#define SADB_MAX 22
+#define SADB_X_NAT_T_NEW_MAPPING 23
+#define SADB_MAX 23
/* Security Association flags */
#define SADB_SAFLAGS_PFS 1
#define SADB_X_EXT_KMPRIVATE 17
#define SADB_X_EXT_POLICY 18
#define SADB_X_EXT_SA2 19
-#define SADB_EXT_MAX 19
+/* The next four entries are for setting up NAT Traversal */
+#define SADB_X_EXT_NAT_T_TYPE 20
+#define SADB_X_EXT_NAT_T_SPORT 21
+#define SADB_X_EXT_NAT_T_DPORT 22
+#define SADB_X_EXT_NAT_T_OA 23
+#define SADB_EXT_MAX 23
/* Identity Extension values */
#define SADB_IDENTTYPE_RESERVED 0
/* UDP socket options */
#define UDP_CORK 1 /* Never send partially complete segments */
+#define UDP_ENCAP 100 /* Set the socket to accept encapsulated packets */
+
+/* UDP encapsulation types */
+#define UDP_ENCAP_ESPINUDP 2 /* draft-ietf-ipsec-udp-encaps-06 */
struct udp_opt {
int pending; /* Any pending frames ? */
unsigned int corkflag; /* Cork is required */
+ __u16 encap_type; /* Is this an Encapsulation socket? */
/*
* Following members retains the infomation to create a UDP header
* when the socket is uncorked.
__u32 calgos;
};
+struct xfrm_encap_tmpl {
+ __u16 encap_type;
+ __u16 encap_sport;
+ __u16 encap_dport;
+};
+
/* Netlink message attributes. */
enum xfrm_attr_type_t {
XFRMA_UNSPEC,
XFRMA_ALG_AUTH, /* struct xfrm_algo */
XFRMA_ALG_CRYPT, /* struct xfrm_algo */
XFRMA_ALG_COMP, /* struct xfrm_algo */
+ XFRMA_ENCAP, /* struct xfrm_algo + struct xfrm_encap_tmpl */
XFRMA_TMPL, /* 1 or more struct xfrm_user_tmpl */
#define XFRMA_MAX XFRMA_TMPL
struct xfrm_algo *aalg;
struct xfrm_algo *ealg;
struct xfrm_algo *calg;
+ struct xfrm_algo *encap_alg;
/* State for replay detection */
struct xfrm_replay_state replay;
extern struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family);
extern void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
+struct xfrm_decap_state;
struct xfrm_type
{
char *description;
int (*init_state)(struct xfrm_state *x, void *args);
void (*destructor)(struct xfrm_state *);
- int (*input)(struct xfrm_state *, struct sk_buff *skb);
+ int (*input)(struct xfrm_state *, struct xfrm_decap_state *, struct sk_buff *skb);
+ int (*post_input)(struct xfrm_state *, struct xfrm_decap_state *, struct sk_buff *skb);
int (*output)(struct sk_buff *skb);
/* Estimate maximal size of result of transformation of a dgram */
u32 (*get_max_size)(struct xfrm_state *, int size);
__u32 calgos;
};
-#define XFRM_MAX_DEPTH 3
+#define XFRM_MAX_DEPTH 4
struct xfrm_policy
{
int (*notify)(struct xfrm_state *x, int event);
int (*acquire)(struct xfrm_state *x, struct xfrm_tmpl *, struct xfrm_policy *xp, int dir);
struct xfrm_policy *(*compile_policy)(u16 family, int opt, u8 *data, int len, int *dir);
+ int (*new_mapping)(struct xfrm_state *x, xfrm_address_t *ipaddr, u16 sport);
};
extern int xfrm_register_km(struct xfrm_mgr *km);
} u;
};
+/* Decapsulation state, used by the input to store data during
+ * decapsulation procedure, to be used later (during the policy
+ * check
+ */
+struct xfrm_decap_state {
+ __u16 decap_type;
+ char decap_data[30];
+};
+
+struct sec_decap_state {
+ struct xfrm_state *xvec;
+ struct xfrm_decap_state decap;
+};
+
struct sec_path
{
kmem_cache_t *pool;
atomic_t refcnt;
int len;
- struct xfrm_state *xvec[XFRM_MAX_DEPTH];
+ struct sec_decap_state x[XFRM_MAX_DEPTH];
};
static inline struct sec_path *
extern void xfrm_replay_advance(struct xfrm_state *x, u32 seq);
extern int xfrm_check_selectors(struct xfrm_state **x, int n, struct flowi *fl);
extern int xfrm4_rcv(struct sk_buff *skb);
+extern int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type);
extern int xfrm6_rcv(struct sk_buff **pskb);
extern int xfrm6_clear_mutable_options(struct sk_buff *skb, u16 *nh_offset, int dir);
extern int xfrm_user_policy(struct sock *sk, int optname, u8 *optval, int optlen);
extern void km_warn_expired(struct xfrm_state *x);
extern void km_expired(struct xfrm_state *x);
extern int km_query(struct xfrm_state *x, struct xfrm_tmpl *, struct xfrm_policy *pol);
+extern int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, u16 sport);
extern void xfrm4_input_init(void);
extern void xfrm6_input_init(void);
return err;
}
-int ah_input(struct xfrm_state *x, struct sk_buff *skb)
+int ah_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_buff *skb)
{
int ah_hlen;
struct iphdr *iph;
#include <linux/pfkeyv2.h>
#include <linux/random.h>
#include <net/icmp.h>
-
+#include <net/udp.h>
#define MAX_SG_ONSTACK 4
+/* encapsulation data for use when post-processing the decapsulation */
+struct esp_encap_data {
+ __u8 proto;
+ xfrm_address_t saddr;
+ __u16 sport;
+};
+
int esp_output(struct sk_buff *skb)
{
int err;
struct crypto_tfm *tfm;
struct esp_data *esp;
struct sk_buff *trailer;
+ struct udphdr *uh = NULL;
+ struct xfrm_encap_tmpl *encap = NULL;
int blksize;
int clen;
int alen;
*(u8*)(trailer->tail + clen-skb->len - 2) = (clen - skb->len)-2;
pskb_put(skb, trailer, clen - skb->len);
+ if (x->encap_alg)
+ encap = (struct xfrm_encap_tmpl *) (x->encap_alg->alg_key);
+
iph = skb->nh.iph;
if (x->props.mode) {
top_iph = (struct iphdr*)skb_push(skb, x->props.header_len);
esph = (struct ip_esp_hdr*)(top_iph+1);
+ if (encap && encap->encap_type) {
+ switch (encap->encap_type) {
+ case UDP_ENCAP_ESPINUDP:
+ uh = (struct udphdr*) esph;
+ esph = (struct ip_esp_hdr*)(uh+1);
+ top_iph->protocol = IPPROTO_UDP;
+ break;
+ default:
+ printk(KERN_INFO
+ "esp_output(): Unhandled encap: %u\n",
+ encap->encap_type);
+ top_iph->protocol = IPPROTO_ESP;
+ break;
+ }
+ } else
+ top_iph->protocol = IPPROTO_ESP;
*(u8*)(trailer->tail - 1) = IPPROTO_IPIP;
top_iph->ihl = 5;
top_iph->version = 4;
if (!(top_iph->frag_off))
ip_select_ident(top_iph, dst, 0);
top_iph->ttl = iph->ttl; /* TTL disclosed */
- top_iph->protocol = IPPROTO_ESP;
top_iph->check = 0;
top_iph->saddr = x->props.saddr.a4;
top_iph->daddr = x->id.daddr.a4;
esph = (struct ip_esp_hdr*)skb_push(skb, x->props.header_len);
top_iph = (struct iphdr*)skb_push(skb, iph->ihl*4);
memcpy(top_iph, &tmp_iph, iph->ihl*4);
+ if (encap && encap->encap_type) {
+ switch (encap->encap_type) {
+ case UDP_ENCAP_ESPINUDP:
+ uh = (struct udphdr*) esph;
+ esph = (struct ip_esp_hdr*)(uh+1);
+ top_iph->protocol = IPPROTO_UDP;
+ break;
+ default:
+ printk(KERN_INFO
+ "esp_output(): Unhandled encap: %u\n",
+ encap->encap_type);
+ top_iph->protocol = IPPROTO_ESP;
+ break;
+ }
+ } else
+ top_iph->protocol = IPPROTO_ESP;
iph = &tmp_iph.iph;
top_iph->tot_len = htons(skb->len + alen);
- top_iph->protocol = IPPROTO_ESP;
top_iph->check = 0;
top_iph->frag_off = iph->frag_off;
*(u8*)(trailer->tail - 1) = iph->protocol;
}
+ /* this is non-NULL only with UDP Encapsulation */
+ if (encap && uh) {
+ uh->source = encap->encap_sport;
+ uh->dest = encap->encap_dport;
+ uh->len = htons(skb->len + alen - sizeof(struct iphdr));
+ uh->check = 0;
+ }
+
esph->spi = x->id.spi;
esph->seq_no = htonl(++x->replay.oseq);
* expensive, so we only support truncated data, which is the recommended
* and common case.
*/
-int esp_input(struct xfrm_state *x, struct sk_buff *skb)
+int esp_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_buff *skb)
{
struct iphdr *iph;
struct ip_esp_hdr *esph;
int alen = esp->auth.icv_trunc_len;
int elen = skb->len - sizeof(struct ip_esp_hdr) - esp->conf.ivlen - alen;
int nfrags;
+ int encap_len = 0;
if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr)))
goto out;
/* ... check padding bits here. Silly. :-) */
+ if (x->encap_alg && decap && decap->decap_type) {
+ struct esp_encap_data *encap_data;
+ struct udphdr *uh = (struct udphdr *) (iph+1);
+
+ encap_data = (struct esp_encap_data *) (decap->decap_data);
+ encap_data->proto = 0;
+
+ switch (decap->decap_type) {
+ case UDP_ENCAP_ESPINUDP:
+
+ if ((void*)uh == (void*)esph) {
+ printk(KERN_DEBUG
+ "esp_input(): Got ESP; expecting ESPinUDP\n");
+ break;
+ }
+
+ encap_data->proto = AF_INET;
+ encap_data->saddr.a4 = iph->saddr;
+ encap_data->sport = uh->source;
+ encap_len = (void*)esph - (void*)uh;
+ if (encap_len != sizeof(*uh))
+ printk(KERN_DEBUG
+ "esp_input(): UDP -> ESP: too much room: %d\n",
+ encap_len);
+ break;
+
+ default:
+ printk(KERN_INFO
+ "esp_input(): processing unknown encap type: %u\n",
+ decap->decap_type);
+ break;
+ }
+ }
+
iph->protocol = nexthdr[1];
pskb_trim(skb, skb->len - alen - padlen - 2);
memcpy(workbuf, skb->nh.raw, iph->ihl*4);
skb->h.raw = skb_pull(skb, sizeof(struct ip_esp_hdr) + esp->conf.ivlen);
- skb->nh.raw += sizeof(struct ip_esp_hdr) + esp->conf.ivlen;
+ skb->nh.raw += encap_len + sizeof(struct ip_esp_hdr) + esp->conf.ivlen;
memcpy(skb->nh.raw, workbuf, iph->ihl*4);
skb->nh.iph->tot_len = htons(skb->len);
}
return -EINVAL;
}
+int esp_post_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_buff *skb)
+{
+
+ if (x->encap_alg) {
+ struct xfrm_encap_tmpl *encap;
+ struct esp_encap_data *decap_data;
+
+ encap = (struct xfrm_encap_tmpl *) (x->encap_alg->alg_key);
+ decap_data = (struct esp_encap_data *)(decap->decap_data);
+
+ /* first, make sure that the decap type == the encap type */
+ if (encap->encap_type != decap->decap_type)
+ return -EINVAL;
+
+ /* Next, if we don't have an encap type, then ignore it */
+ if (!encap->encap_type)
+ return 0;
+
+ switch (encap->encap_type) {
+ case UDP_ENCAP_ESPINUDP:
+ /*
+ * 1) if the NAT-T peer's IP or port changed then
+ * advertize the change to the keying daemon.
+ * This is an inbound SA, so just compare
+ * SRC ports.
+ */
+ if (decap_data->proto == AF_INET &&
+ (decap_data->saddr.a4 != x->props.saddr.a4 ||
+ decap_data->sport != encap->encap_sport)) {
+ xfrm_address_t ipaddr;
+
+ ipaddr.a4 = decap_data->saddr.a4;
+ km_new_mapping(x, &ipaddr, decap_data->sport);
+
+ /* XXX: perhaps add an extra
+ * policy check here, to see
+ * if we should allow or
+ * reject a packet from a
+ * different source
+ * address/port.
+ */
+ }
+
+ /*
+ * 2) ignore UDP/TCP checksums in case
+ * of NAT-T in Transport Mode, or
+ * perform other post-processing fixes
+ * as per * draft-ietf-ipsec-udp-encaps-06,
+ * section 3.1.2
+ */
+ if (!x->props.mode)
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ break;
+ default:
+ printk(KERN_INFO
+ "esp4_post_input(): Unhandled encap type: %u\n",
+ encap->encap_type);
+ break;
+ }
+ }
+ return 0;
+}
+
static u32 esp4_get_max_size(struct xfrm_state *x, int mtu)
{
struct esp_data *esp = x->data;
x->props.header_len = sizeof(struct ip_esp_hdr) + esp->conf.ivlen;
if (x->props.mode)
x->props.header_len += sizeof(struct iphdr);
+ if (x->encap_alg) {
+ struct xfrm_encap_tmpl *encap = (struct xfrm_encap_tmpl *)
+ (x->encap_alg->alg_key);
+ if (encap->encap_type) {
+ switch (encap->encap_type) {
+ case UDP_ENCAP_ESPINUDP:
+ x->props.header_len += sizeof(struct udphdr);
+ break;
+ default:
+ printk (KERN_INFO
+ "esp_init_state(): Unhandled encap type: %u\n",
+ encap->encap_type);
+ break;
+ }
+ }
+ }
x->data = esp;
x->props.trailer_len = esp4_get_max_size(x, 0) - x->props.header_len;
return 0;
.destructor = esp_destroy,
.get_max_size = esp4_get_max_size,
.input = esp_input,
+ .post_input = esp_post_input,
.output = esp_output
};
* YOSHIFUJI Hideaki @USAGI and: Support IPV6_V6ONLY socket option, which
* Alexey Kuznetsov: allow both IPv4 and IPv6 sockets to bind
* a single port at the same time.
+ * Derek Atkins <derek@ihtfp.com>: Add Encapulation Support
*
*
* This program is free software; you can redistribute it and/or
inet_sock_release(sk);
}
+/* return:
+ * 1 if the the UDP system should process it
+ * 0 if we should drop this packet
+ * -1 if it should get processed by xfrm4_rcv_encap
+ */
+static int udp_encap_rcv(struct sock * sk, struct sk_buff *skb)
+{
+ struct udp_opt *up = udp_sk(sk);
+ struct udphdr *uh = skb->h.uh;
+ struct iphdr *iph;
+ int iphlen, len;
+
+ __u8 *udpdata = (__u8 *)uh + sizeof(struct udphdr);
+ __u32 *udpdata32 = (__u32 *)udpdata;
+ __u16 encap_type = up->encap_type;
+
+ /* if we're overly short, let UDP handle it */
+ if (udpdata > skb->tail)
+ return 1;
+
+ /* if this is not encapsulated socket, then just return now */
+ if (!encap_type)
+ return 1;
+
+ len = skb->tail - udpdata;
+
+ switch (encap_type) {
+ case UDP_ENCAP_ESPINUDP:
+ /* Check if this is a keepalive packet. If so, eat it. */
+ if (len == 1 && udpdata[0] == 0xff) {
+ return 0;
+ } else if (len > sizeof(struct ip_esp_hdr) && udpdata32[0] != 0 ) {
+ /* ESP Packet without Non-ESP header */
+ len = sizeof(struct udphdr);
+ } else
+ /* Must be an IKE packet.. pass it through */
+ return 1;
+
+ /* At this point we are sure that this is an ESPinUDP packet,
+ * so we need to remove 'len' bytes from the packet (the UDP
+ * header and optional ESP marker bytes) and then modify the
+ * protocol to ESP, and then call into the transform receiver.
+ */
+
+ /* Now we can update and verify the packet length... */
+ iph = skb->nh.iph;
+ iphlen = iph->ihl << 2;
+ iph->tot_len = htons(ntohs(iph->tot_len) - len);
+ if (skb->len < iphlen + len) {
+ /* packet is too small!?! */
+ return 0;
+ }
+
+ /* pull the data buffer up to the ESP header and set the
+ * transport header to point to ESP. Keep UDP on the stack
+ * for later.
+ */
+ skb->h.raw = skb_pull(skb, len);
+
+ /* modify the protocol (it's ESP!) */
+ iph->protocol = IPPROTO_ESP;
+
+ /* and let the caller know to send this into the ESP processor... */
+ return -1;
+
+ default:
+ printk(KERN_INFO "udp_encap_rcv(): Unhandled UDP encap type: %u\n",
+ encap_type);
+ return 1;
+ }
+}
+
+/* returns:
+ * -1: error
+ * 0: success
+ * >0: "udp encap" protocol resubmission
+ *
+ * Note that in the success and error cases, the skb is assumed to
+ * have either been requeued or freed.
+ */
static int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
{
+ struct udp_opt *up = udp_sk(sk);
+
/*
* Charge it to the socket, dropping if the queue is full.
*/
return -1;
}
+ if (up->encap_type) {
+ /*
+ * This is an encapsulation socket, so let's see if this is
+ * an encapsulated packet.
+ * If it's a keepalive packet, then just eat it.
+ * If it's an encapsulateed packet, then pass it to the
+ * IPsec xfrm input and return the response
+ * appropriately. Otherwise, just fall through and
+ * pass this up the UDP socket.
+ */
+ int ret;
+
+ ret = udp_encap_rcv(sk, skb);
+ if (ret == 0) {
+ /* Eat the packet .. */
+ kfree_skb(skb);
+ return 0;
+ }
+ if (ret < 0) {
+ /* process the ESP packet */
+ ret = xfrm4_rcv_encap(skb, up->encap_type);
+ UDP_INC_STATS_BH(UdpInDatagrams);
+ return -ret;
+ }
+ /* FALLTHROUGH -- it's a UDP Packet */
+ }
+
if (sk->filter && skb->ip_summed != CHECKSUM_UNNECESSARY) {
if (__udp_checksum_complete(skb)) {
UDP_INC_STATS_BH(UdpInErrors);
if(sknext)
skb1 = skb_clone(skb, GFP_ATOMIC);
- if(skb1)
- udp_queue_rcv_skb(sk, skb1);
+ if(skb1) {
+ int ret = udp_queue_rcv_skb(sk, skb1);
+ if (ret > 0)
+ /* we should probably re-process instead
+ * of dropping packets here. */
+ kfree_skb(skb1);
+ }
sk = sknext;
} while(sknext);
} else
sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest, skb->dev->ifindex);
if (sk != NULL) {
- udp_queue_rcv_skb(sk, skb);
+ int ret = udp_queue_rcv_skb(sk, skb);
sock_put(sk);
+
+ /* a return value > 0 means to resubmit the input, but
+ * it it wants the return to be -protocol, or 0
+ */
+ if (ret > 0)
+ return -ret;
return 0;
}
}
break;
+ case UDP_ENCAP:
+ up->encap_type = val;
+ break;
+
default:
err = -ENOPROTOOPT;
break;
val = up->corkflag;
break;
+ case UDP_ENCAP:
+ val = up->encap_type;
+ break;
+
default:
return -ENOPROTOOPT;
};
* Changes:
* YOSHIFUJI Hideaki @USAGI
* Split up af-specific portion
+ * Derek Atkins <derek@ihtfp.com>
+ * Add Encapsulation support
*
*/
static kmem_cache_t *secpath_cachep;
int xfrm4_rcv(struct sk_buff *skb)
+{
+ return xfrm4_rcv_encap(skb, 0);
+}
+
+int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type)
{
int err;
u32 spi, seq;
- struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH];
+ struct sec_decap_state xfrm_vec[XFRM_MAX_DEPTH];
struct xfrm_state *x;
int xfrm_nr = 0;
int decaps = 0;
if (x->props.replay_window && xfrm_replay_check(x, seq))
goto drop_unlock;
- if (x->type->input(x, skb))
+ xfrm_vec[xfrm_nr].decap.decap_type = encap_type;
+ if (x->type->input(x, &(xfrm_vec[xfrm_nr].decap), skb))
goto drop_unlock;
+ /* only the first xfrm gets the encap type */
+ encap_type = 0;
+
if (x->props.replay_window)
xfrm_replay_advance(x, seq);
spin_unlock(&x->lock);
- xfrm_vec[xfrm_nr++] = x;
+ xfrm_vec[xfrm_nr++].xvec = x;
iph = skb->nh.iph;
if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH)
goto drop;
- memcpy(skb->sp->xvec+skb->sp->len, xfrm_vec, xfrm_nr*sizeof(void*));
+ memcpy(skb->sp->x+skb->sp->len, xfrm_vec, xfrm_nr*sizeof(struct sec_decap_state));
skb->sp->len += xfrm_nr;
if (decaps) {
xfrm_state_put(x);
drop:
while (--xfrm_nr >= 0)
- xfrm_state_put(xfrm_vec[xfrm_nr]);
+ xfrm_state_put(xfrm_vec[xfrm_nr].xvec);
+
kfree_skb(skb);
return 0;
}
return err;
}
-int ah6_input(struct xfrm_state *x, struct sk_buff *skb)
+int ah6_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_buff *skb)
{
int ah_hlen;
struct ipv6hdr *iph;
return err;
}
-int esp6_input(struct xfrm_state *x, struct sk_buff *skb)
+int esp6_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_buff *skb)
{
struct ipv6hdr *iph;
struct ipv6_esp_hdr *esph;
struct sk_buff *skb = *pskb;
int err;
u32 spi, seq;
- struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH];
+ struct sec_decap_state xfrm_vec[XFRM_MAX_DEPTH];
struct xfrm_state *x;
int xfrm_nr = 0;
int decaps = 0;
if (x->props.replay_window && xfrm_replay_check(x, seq))
goto drop_unlock;
- nexthdr = x->type->input(x, skb);
+ nexthdr = x->type->input(x, &(xfrm_vec[xfrm_nr].decap), skb);
if (nexthdr <= 0)
goto drop_unlock;
spin_unlock(&x->lock);
- xfrm_vec[xfrm_nr++] = x;
+ xfrm_vec[xfrm_nr++].xvec = x;
iph = skb->nh.ipv6h;
if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH)
goto drop;
- memcpy(skb->sp->xvec+skb->sp->len, xfrm_vec, xfrm_nr*sizeof(void*));
+ memcpy(skb->sp->x+skb->sp->len, xfrm_vec, xfrm_nr*sizeof(struct sec_decap_state));
skb->sp->len += xfrm_nr;
if (decaps) {
drop:
if (tmp_hdr) kfree(tmp_hdr);
while (--xfrm_nr >= 0)
- xfrm_state_put(xfrm_vec[xfrm_nr]);
+ xfrm_state_put(xfrm_vec[xfrm_nr].xvec);
kfree_skb(skb);
return 0;
}
* Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
* Kunihiro Ishiguro <kunihiro@ipinfusion.com>
* Kazunori MIYAZAWA / USAGI Project <miyazawa@linux-ipv6.org>
+ * Derek Atkins <derek@ihtfp.com>
*/
#include <linux/config.h>
[SADB_X_EXT_KMPRIVATE] = (u8) sizeof(struct sadb_x_kmprivate),
[SADB_X_EXT_POLICY] = (u8) sizeof(struct sadb_x_policy),
[SADB_X_EXT_SA2] = (u8) sizeof(struct sadb_x_sa2),
+ [SADB_X_EXT_NAT_T_TYPE] = (u8) sizeof(struct sadb_x_nat_t_type),
+ [SADB_X_EXT_NAT_T_SPORT] = (u8) sizeof(struct sadb_x_nat_t_port),
+ [SADB_X_EXT_NAT_T_DPORT] = (u8) sizeof(struct sadb_x_nat_t_port),
+ [SADB_X_EXT_NAT_T_OA] = (u8) sizeof(struct sadb_address),
};
/* Verify sadb_address_{len,prefixlen} against sa_family. */
return -EINVAL;
if (ext_type == SADB_EXT_ADDRESS_SRC ||
ext_type == SADB_EXT_ADDRESS_DST ||
- ext_type == SADB_EXT_ADDRESS_PROXY) {
+ ext_type == SADB_EXT_ADDRESS_PROXY ||
+ ext_type == SADB_X_EXT_NAT_T_OA) {
if (verify_address_len(p))
return -EINVAL;
}
int auth_key_size = 0;
int encrypt_key_size = 0;
int sockaddr_size;
+ struct xfrm_encap_tmpl *natt = NULL;
/* address family check */
sockaddr_size = pfkey_sockaddr_size(x->props.family);
size += sizeof(struct sadb_key) + encrypt_key_size;
}
}
+ if (x->encap_alg)
+ natt = (struct xfrm_encap_tmpl *) (x->encap_alg->alg_key);
+
+ if (natt && natt->encap_type) {
+ size += sizeof(struct sadb_x_nat_t_type);
+ size += sizeof(struct sadb_x_nat_t_port);
+ size += sizeof(struct sadb_x_nat_t_port);
+ }
+
skb = alloc_skb(size + 16, GFP_ATOMIC);
if (skb == NULL)
return ERR_PTR(-ENOBUFS);
sa2->sadb_x_sa2_sequence = 0;
sa2->sadb_x_sa2_reqid = x->props.reqid;
+ if (natt && natt->encap_type) {
+ struct sadb_x_nat_t_type *n_type;
+ struct sadb_x_nat_t_port *n_port;
+
+ /* type */
+ n_type = (struct sadb_x_nat_t_type*) skb_put(skb, sizeof(*n_type));
+ n_type->sadb_x_nat_t_type_len = sizeof(*n_type)/sizeof(uint64_t);
+ n_type->sadb_x_nat_t_type_exttype = SADB_X_EXT_NAT_T_TYPE;
+ n_type->sadb_x_nat_t_type_type = natt->encap_type;
+ n_type->sadb_x_nat_t_type_reserved[0] = 0;
+ n_type->sadb_x_nat_t_type_reserved[1] = 0;
+ n_type->sadb_x_nat_t_type_reserved[2] = 0;
+
+ /* source port */
+ n_port = (struct sadb_x_nat_t_port*) skb_put(skb, sizeof (*n_port));
+ n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
+ n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_SPORT;
+ n_port->sadb_x_nat_t_port_port = natt->encap_sport;
+ n_port->sadb_x_nat_t_port_reserved = 0;
+
+ /* dest port */
+ n_port = (struct sadb_x_nat_t_port*) skb_put(skb, sizeof (*n_port));
+ n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
+ n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_DPORT;
+ n_port->sadb_x_nat_t_port_port = natt->encap_dport;
+ n_port->sadb_x_nat_t_port_reserved = 0;
+ }
+
return skb;
}
x->sel.prefixlen_s = addr->sadb_address_prefixlen;
}
+ if (ext_hdrs[SADB_X_EXT_NAT_T_TYPE-1]) {
+ struct sadb_x_nat_t_type* n_type;
+ struct xfrm_encap_tmpl *natt;
+ int obits = (sizeof *natt);
+
+ x->encap_alg = kmalloc(sizeof(*x->encap_alg) + obits, GFP_KERNEL);
+ if (!x->encap_alg)
+ goto out;
+
+ strcpy(x->encap_alg->alg_name, "NAT-T");
+ natt = (struct xfrm_encap_tmpl *) (x->encap_alg->alg_key);
+ n_type = ext_hdrs[SADB_X_EXT_NAT_T_TYPE-1];
+ natt->encap_type = n_type->sadb_x_nat_t_type_type;
+
+ if (ext_hdrs[SADB_X_EXT_NAT_T_SPORT-1]) {
+ struct sadb_x_nat_t_port* n_port =
+ ext_hdrs[SADB_X_EXT_NAT_T_SPORT-1];
+ natt->encap_sport = n_port->sadb_x_nat_t_port_port;
+ }
+ if (ext_hdrs[SADB_X_EXT_NAT_T_DPORT-1]) {
+ struct sadb_x_nat_t_port* n_port =
+ ext_hdrs[SADB_X_EXT_NAT_T_DPORT-1];
+ natt->encap_dport = n_port->sadb_x_nat_t_port_port;
+ }
+ }
+
x->type = xfrm_get_type(proto, x->props.family);
if (x->type == NULL)
goto out;
kfree(x->ealg);
if (x->calg)
kfree(x->calg);
+ if (x->encap_alg)
+ kfree(x->encap_alg);
kfree(x);
return ERR_PTR(-ENOBUFS);
}
return 0;
}
-
typedef int (*pfkey_handler)(struct sock *sk, struct sk_buff *skb,
struct sadb_msg *hdr, void **ext_hdrs);
static pfkey_handler pfkey_funcs[SADB_MAX + 1] = {
return NULL;
}
+static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, u16 sport)
+{
+ struct sk_buff *skb;
+ struct sadb_msg *hdr;
+ struct sadb_sa *sa;
+ struct sadb_address *addr;
+ struct sadb_x_nat_t_port *n_port;
+ struct sockaddr_in *sin;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ struct sockaddr_in6 *sin6;
+#endif
+ int sockaddr_size;
+ int size;
+ __u8 satype = (x->id.proto == IPPROTO_ESP ? SADB_SATYPE_ESP : 0);
+ struct xfrm_encap_tmpl *natt = NULL;
+
+ sockaddr_size = pfkey_sockaddr_size(x->props.family);
+ if (!sockaddr_size)
+ return -EINVAL;
+
+ if (!satype)
+ return -EINVAL;
+
+ if (!x->encap_alg)
+ return -EINVAL;
+
+ natt = (struct xfrm_encap_tmpl *) (x->encap_alg->alg_key);
+
+ /* Build an SADB_X_NAT_T_NEW_MAPPING message:
+ *
+ * HDR | SA | ADDRESS_SRC (old addr) | NAT_T_SPORT (old port) |
+ * ADDRESS_DST (new addr) | NAT_T_DPORT (new port)
+ */
+
+ size = sizeof(struct sadb_msg) +
+ sizeof(struct sadb_sa) +
+ (sizeof(struct sadb_address) * 2) +
+ (sockaddr_size * 2) +
+ (sizeof(struct sadb_x_nat_t_port) * 2);
+
+ skb = alloc_skb(size + 16, GFP_ATOMIC);
+ if (skb == NULL)
+ return -ENOMEM;
+
+ hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
+ hdr->sadb_msg_version = PF_KEY_V2;
+ hdr->sadb_msg_type = SADB_X_NAT_T_NEW_MAPPING;
+ hdr->sadb_msg_satype = satype;
+ hdr->sadb_msg_len = size / sizeof(uint64_t);
+ hdr->sadb_msg_errno = 0;
+ hdr->sadb_msg_reserved = 0;
+ hdr->sadb_msg_seq = x->km.seq = get_acqseq();
+ hdr->sadb_msg_pid = 0;
+
+ /* SA */
+ sa = (struct sadb_sa *) skb_put(skb, sizeof(struct sadb_sa));
+ sa->sadb_sa_len = sizeof(struct sadb_sa)/sizeof(uint64_t);
+ sa->sadb_sa_exttype = SADB_EXT_SA;
+ sa->sadb_sa_spi = x->id.spi;
+ sa->sadb_sa_replay = 0;
+ sa->sadb_sa_state = 0;
+ sa->sadb_sa_auth = 0;
+ sa->sadb_sa_encrypt = 0;
+ sa->sadb_sa_flags = 0;
+
+ /* ADDRESS_SRC (old 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_SRC;
+ addr->sadb_address_proto = 0;
+ addr->sadb_address_reserved = 0;
+ 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.a4;
+ 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();
+
+ /* NAT_T_SPORT (old port) */
+ n_port = (struct sadb_x_nat_t_port*) skb_put(skb, sizeof (*n_port));
+ n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
+ n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_SPORT;
+ n_port->sadb_x_nat_t_port_port = natt->encap_sport;
+ n_port->sadb_x_nat_t_port_reserved = 0;
+
+ /* ADDRESS_DST (new 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_SRC;
+ addr->sadb_address_proto = 0;
+ addr->sadb_address_reserved = 0;
+ 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 = ipaddr->a4;
+ 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, &ipaddr->a6, sizeof(struct in6_addr));
+ sin6->sin6_scope_id = 0;
+ }
+#endif
+ else
+ BUG();
+
+ /* NAT_T_DPORT (new port) */
+ n_port = (struct sadb_x_nat_t_port*) skb_put(skb, sizeof (*n_port));
+ n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
+ n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_DPORT;
+ n_port->sadb_x_nat_t_port_port = sport;
+ n_port->sadb_x_nat_t_port_reserved = 0;
+
+ return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL);
+}
+
static int pfkey_sendmsg(struct kiocb *kiocb,
struct socket *sock, struct msghdr *msg, int len)
{
.notify = pfkey_send_notify,
.acquire = pfkey_send_acquire,
.compile_policy = pfkey_compile_policy,
+ .new_mapping = pfkey_send_new_mapping,
};
static void __exit ipsec_pfkey_exit(void)
EXPORT_SYMBOL(xfrm_user_policy);
EXPORT_SYMBOL(km_waitq);
+EXPORT_SYMBOL(km_new_mapping);
EXPORT_SYMBOL(xfrm_cfg_sem);
EXPORT_SYMBOL(xfrm_policy_alloc);
EXPORT_SYMBOL(__xfrm_policy_destroy);
{
int i;
for (i = 0; i < sp->len; i++)
- xfrm_state_put(sp->xvec[i]);
+ xfrm_state_put(sp->x[i].xvec);
kmem_cache_free(sp->pool, sp);
}
* Kazunori MIYAZAWA @USAGI
* YOSHIFUJI Hideaki
* Split up af-specific portion
+ * Derek Atkins <derek@ihtfp.com> Add the post_input processor
*
*/
unsigned short family)
{
for (; idx < sp->len; idx++) {
- if (xfrm_state_ok(tmpl, sp->xvec[idx], family))
+ if (xfrm_state_ok(tmpl, sp->x[idx].xvec, family))
return ++idx;
}
return -1;
int i;
for (i=skb->sp->len-1; i>=0; i--) {
- if (!xfrm_selector_match(&skb->sp->xvec[i]->sel, &fl, family))
+ struct sec_decap_state *xvec = &(skb->sp->x[i]);
+ if (!xfrm_selector_match(&xvec->xvec->sel, &fl, family))
+ return 0;
+
+ /* If there is a post_input processor, try running it */
+ if (xvec->xvec->type->post_input &&
+ (xvec->xvec->type->post_input)(xvec->xvec,
+ &(xvec->decap),
+ skb) != 0)
return 0;
}
}
* IPv6 support
* YOSHIFUJI Hideaki @USAGI
* Split up af-specific functions
+ * Derek Atkins <derek@ihtfp.com>
+ * Add UDP Encapsulation
*
*/
kfree(x->ealg);
if (x->calg)
kfree(x->calg);
+ if (x->encap_alg)
+ kfree(x->encap_alg);
if (x->type)
xfrm_put_type(x->type);
kfree(x);
return err;
}
+int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, u16 sport)
+{
+ int err = -EINVAL;
+ struct xfrm_mgr *km;
+
+ read_lock(&xfrm_km_lock);
+ list_for_each_entry(km, &xfrm_km_list, list) {
+ if (km->new_mapping)
+ err = km->new_mapping(x, ipaddr, sport);
+ if (!err)
+ break;
+ }
+ read_unlock(&xfrm_km_lock);
+ return err;
+}
+
int xfrm_user_policy(struct sock *sk, int optname, u8 *optval, int optlen)
{
int err;