Commit cbc34886 authored by Derek Atkins's avatar Derek Atkins Committed by David S. Miller

[IPSEC]: Implement UDP Encapsulation framework.

In particular, implement ESPinUDP encapsulation for IPsec
Nat Traversal.
parent 450609e5
......@@ -194,6 +194,26 @@ struct sadb_x_ipsecrequest {
} __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
......@@ -218,7 +238,8 @@ struct sadb_x_ipsecrequest {
#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
......@@ -291,7 +312,12 @@ struct sadb_x_ipsecrequest {
#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
......
......@@ -30,10 +30,15 @@ struct udphdr {
/* 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.
......
......@@ -130,12 +130,19 @@ struct xfrm_user_tmpl {
__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
......
......@@ -118,6 +118,7 @@ struct xfrm_state
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;
......@@ -192,6 +193,7 @@ extern int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo);
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;
......@@ -200,7 +202,8 @@ struct xfrm_type
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);
......@@ -246,7 +249,7 @@ struct xfrm_tmpl
__u32 calgos;
};
#define XFRM_MAX_DEPTH 3
#define XFRM_MAX_DEPTH 4
struct xfrm_policy
{
......@@ -278,6 +281,7 @@ struct xfrm_mgr
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);
......@@ -498,12 +502,26 @@ struct xfrm_dst
} 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 *
......@@ -730,6 +748,7 @@ extern int xfrm_replay_check(struct xfrm_state *x, u32 seq);
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);
......@@ -760,6 +779,7 @@ extern wait_queue_head_t km_waitq;
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);
......
......@@ -152,7 +152,7 @@ static int ah_output(struct sk_buff *skb)
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;
......
......@@ -8,10 +8,17 @@
#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;
......@@ -22,6 +29,8 @@ int esp_output(struct sk_buff *skb)
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;
......@@ -76,10 +85,29 @@ int esp_output(struct sk_buff *skb)
*(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;
......@@ -89,7 +117,6 @@ int esp_output(struct sk_buff *skb)
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;
......@@ -98,14 +125,37 @@ int esp_output(struct sk_buff *skb)
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);
......@@ -163,7 +213,7 @@ int esp_output(struct sk_buff *skb)
* 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;
......@@ -173,6 +223,7 @@ int esp_input(struct xfrm_state *x, struct sk_buff *skb)
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;
......@@ -234,11 +285,45 @@ int esp_input(struct xfrm_state *x, struct sk_buff *skb)
/* ... 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);
}
......@@ -249,6 +334,70 @@ int esp_input(struct xfrm_state *x, struct sk_buff *skb)
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;
......@@ -368,6 +517,22 @@ int esp_init_state(struct xfrm_state *x, void *args)
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;
......@@ -393,6 +558,7 @@ static struct xfrm_type esp_type =
.destructor = esp_destroy,
.get_max_size = esp4_get_max_size,
.input = esp_input,
.post_input = esp_post_input,
.output = esp_output
};
......
......@@ -69,6 +69,7 @@
* 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
......@@ -941,8 +942,90 @@ static void udp_close(struct sock *sk, long timeout)
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.
*/
......@@ -951,6 +1034,33 @@ static int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
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);
......@@ -996,8 +1106,13 @@ static int udp_v4_mcast_deliver(struct sk_buff *skb, struct udphdr *uh,
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
......@@ -1070,8 +1185,14 @@ int udp_rcv(struct sk_buff *skb)
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;
}
......@@ -1163,6 +1284,10 @@ static int udp_setsockopt(struct sock *sk, int level, int optname,
}
break;
case UDP_ENCAP:
up->encap_type = val;
break;
default:
err = -ENOPROTOOPT;
break;
......@@ -1193,6 +1318,10 @@ static int udp_getsockopt(struct sock *sk, int level, int optname,
val = up->corkflag;
break;
case UDP_ENCAP:
val = up->encap_type;
break;
default:
return -ENOPROTOOPT;
};
......
......@@ -4,6 +4,8 @@
* Changes:
* YOSHIFUJI Hideaki @USAGI
* Split up af-specific portion
* Derek Atkins <derek@ihtfp.com>
* Add Encapsulation support
*
*/
......@@ -13,10 +15,15 @@
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;
......@@ -41,9 +48,13 @@ int xfrm4_rcv(struct sk_buff *skb)
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);
......@@ -53,7 +64,7 @@ int xfrm4_rcv(struct sk_buff *skb)
spin_unlock(&x->lock);
xfrm_vec[xfrm_nr++] = x;
xfrm_vec[xfrm_nr++].xvec = x;
iph = skb->nh.iph;
......@@ -92,7 +103,7 @@ int xfrm4_rcv(struct sk_buff *skb)
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) {
......@@ -111,7 +122,8 @@ int xfrm4_rcv(struct sk_buff *skb)
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;
}
......
......@@ -146,7 +146,7 @@ int ah6_output(struct sk_buff *skb)
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;
......
......@@ -254,7 +254,7 @@ int esp6_output(struct sk_buff *skb)
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;
......
......@@ -128,7 +128,7 @@ int xfrm6_rcv(struct sk_buff **pskb)
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;
......@@ -172,7 +172,7 @@ int xfrm6_rcv(struct sk_buff **pskb)
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;
......@@ -185,7 +185,7 @@ int xfrm6_rcv(struct sk_buff **pskb)
spin_unlock(&x->lock);
xfrm_vec[xfrm_nr++] = x;
xfrm_vec[xfrm_nr++].xvec = x;
iph = skb->nh.ipv6h;
......@@ -229,7 +229,7 @@ int xfrm6_rcv(struct sk_buff **pskb)
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) {
......@@ -249,7 +249,7 @@ int xfrm6_rcv(struct sk_buff **pskb)
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;
}
......
......@@ -11,6 +11,7 @@
* 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>
......@@ -345,6 +346,10 @@ static u8 sadb_ext_min_len[] = {
[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. */
......@@ -442,7 +447,8 @@ static int parse_exthdrs(struct sk_buff *skb, struct sadb_msg *hdr, void **ext_h
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;
}
......@@ -601,6 +607,7 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys,
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);
......@@ -639,6 +646,15 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys,
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);
......@@ -858,6 +874,34 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys,
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;
}
......@@ -1017,6 +1061,32 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr,
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;
......@@ -1033,6 +1103,8 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr,
kfree(x->ealg);
if (x->calg)
kfree(x->calg);
if (x->encap_alg)
kfree(x->encap_alg);
kfree(x);
return ERR_PTR(-ENOBUFS);
}
......@@ -2051,7 +2123,6 @@ static int pfkey_spdflush(struct sock *sk, struct sk_buff *skb, struct sadb_msg
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] = {
......@@ -2467,6 +2538,155 @@ static struct xfrm_policy *pfkey_compile_policy(u16 family, int opt,
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)
{
......@@ -2632,6 +2852,7 @@ static struct xfrm_mgr pfkeyv2_mgr =
.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)
......
......@@ -292,6 +292,7 @@ EXPORT_SYMBOL(dlci_ioctl_hook);
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);
......
......@@ -14,7 +14,7 @@ void __secpath_destroy(struct sec_path *sp)
{
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);
}
......
......@@ -9,6 +9,7 @@
* Kazunori MIYAZAWA @USAGI
* YOSHIFUJI Hideaki
* Split up af-specific portion
* Derek Atkins <derek@ihtfp.com> Add the post_input processor
*
*/
......@@ -889,7 +890,7 @@ xfrm_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int idx,
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;
......@@ -922,7 +923,15 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
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;
}
}
......
......@@ -8,6 +8,8 @@
* IPv6 support
* YOSHIFUJI Hideaki @USAGI
* Split up af-specific functions
* Derek Atkins <derek@ihtfp.com>
* Add UDP Encapsulation
*
*/
......@@ -155,6 +157,8 @@ void __xfrm_state_destroy(struct xfrm_state *x)
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);
......@@ -632,6 +636,22 @@ int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment