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 { ...@@ -194,6 +194,26 @@ struct sadb_x_ipsecrequest {
} __attribute__((packed)); } __attribute__((packed));
/* sizeof(struct sadb_x_ipsecrequest) == 16 */ /* 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 */ /* Message types */
#define SADB_RESERVED 0 #define SADB_RESERVED 0
#define SADB_GETSPI 1 #define SADB_GETSPI 1
...@@ -218,7 +238,8 @@ struct sadb_x_ipsecrequest { ...@@ -218,7 +238,8 @@ struct sadb_x_ipsecrequest {
#define SADB_X_SPDSETIDX 20 #define SADB_X_SPDSETIDX 20
#define SADB_X_SPDEXPIRE 21 #define SADB_X_SPDEXPIRE 21
#define SADB_X_SPDDELETE2 22 #define SADB_X_SPDDELETE2 22
#define SADB_MAX 22 #define SADB_X_NAT_T_NEW_MAPPING 23
#define SADB_MAX 23
/* Security Association flags */ /* Security Association flags */
#define SADB_SAFLAGS_PFS 1 #define SADB_SAFLAGS_PFS 1
...@@ -291,7 +312,12 @@ struct sadb_x_ipsecrequest { ...@@ -291,7 +312,12 @@ struct sadb_x_ipsecrequest {
#define SADB_X_EXT_KMPRIVATE 17 #define SADB_X_EXT_KMPRIVATE 17
#define SADB_X_EXT_POLICY 18 #define SADB_X_EXT_POLICY 18
#define SADB_X_EXT_SA2 19 #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 */ /* Identity Extension values */
#define SADB_IDENTTYPE_RESERVED 0 #define SADB_IDENTTYPE_RESERVED 0
......
...@@ -30,10 +30,15 @@ struct udphdr { ...@@ -30,10 +30,15 @@ struct udphdr {
/* UDP socket options */ /* UDP socket options */
#define UDP_CORK 1 /* Never send partially complete segments */ #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 { struct udp_opt {
int pending; /* Any pending frames ? */ int pending; /* Any pending frames ? */
unsigned int corkflag; /* Cork is required */ unsigned int corkflag; /* Cork is required */
__u16 encap_type; /* Is this an Encapsulation socket? */
/* /*
* Following members retains the infomation to create a UDP header * Following members retains the infomation to create a UDP header
* when the socket is uncorked. * when the socket is uncorked.
......
...@@ -130,12 +130,19 @@ struct xfrm_user_tmpl { ...@@ -130,12 +130,19 @@ struct xfrm_user_tmpl {
__u32 calgos; __u32 calgos;
}; };
struct xfrm_encap_tmpl {
__u16 encap_type;
__u16 encap_sport;
__u16 encap_dport;
};
/* Netlink message attributes. */ /* Netlink message attributes. */
enum xfrm_attr_type_t { enum xfrm_attr_type_t {
XFRMA_UNSPEC, XFRMA_UNSPEC,
XFRMA_ALG_AUTH, /* struct xfrm_algo */ XFRMA_ALG_AUTH, /* struct xfrm_algo */
XFRMA_ALG_CRYPT, /* struct xfrm_algo */ XFRMA_ALG_CRYPT, /* struct xfrm_algo */
XFRMA_ALG_COMP, /* 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 */ XFRMA_TMPL, /* 1 or more struct xfrm_user_tmpl */
#define XFRMA_MAX XFRMA_TMPL #define XFRMA_MAX XFRMA_TMPL
......
...@@ -118,6 +118,7 @@ struct xfrm_state ...@@ -118,6 +118,7 @@ struct xfrm_state
struct xfrm_algo *aalg; struct xfrm_algo *aalg;
struct xfrm_algo *ealg; struct xfrm_algo *ealg;
struct xfrm_algo *calg; struct xfrm_algo *calg;
struct xfrm_algo *encap_alg;
/* State for replay detection */ /* State for replay detection */
struct xfrm_replay_state replay; struct xfrm_replay_state replay;
...@@ -192,6 +193,7 @@ extern int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo); ...@@ -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 struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family);
extern void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo); extern void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
struct xfrm_decap_state;
struct xfrm_type struct xfrm_type
{ {
char *description; char *description;
...@@ -200,7 +202,8 @@ struct xfrm_type ...@@ -200,7 +202,8 @@ struct xfrm_type
int (*init_state)(struct xfrm_state *x, void *args); int (*init_state)(struct xfrm_state *x, void *args);
void (*destructor)(struct xfrm_state *); 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); int (*output)(struct sk_buff *skb);
/* Estimate maximal size of result of transformation of a dgram */ /* Estimate maximal size of result of transformation of a dgram */
u32 (*get_max_size)(struct xfrm_state *, int size); u32 (*get_max_size)(struct xfrm_state *, int size);
...@@ -246,7 +249,7 @@ struct xfrm_tmpl ...@@ -246,7 +249,7 @@ struct xfrm_tmpl
__u32 calgos; __u32 calgos;
}; };
#define XFRM_MAX_DEPTH 3 #define XFRM_MAX_DEPTH 4
struct xfrm_policy struct xfrm_policy
{ {
...@@ -278,6 +281,7 @@ struct xfrm_mgr ...@@ -278,6 +281,7 @@ struct xfrm_mgr
int (*notify)(struct xfrm_state *x, int event); int (*notify)(struct xfrm_state *x, int event);
int (*acquire)(struct xfrm_state *x, struct xfrm_tmpl *, struct xfrm_policy *xp, int dir); 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); 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); extern int xfrm_register_km(struct xfrm_mgr *km);
...@@ -498,12 +502,26 @@ struct xfrm_dst ...@@ -498,12 +502,26 @@ struct xfrm_dst
} u; } 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 struct sec_path
{ {
kmem_cache_t *pool; kmem_cache_t *pool;
atomic_t refcnt; atomic_t refcnt;
int len; int len;
struct xfrm_state *xvec[XFRM_MAX_DEPTH]; struct sec_decap_state x[XFRM_MAX_DEPTH];
}; };
static inline struct sec_path * static inline struct sec_path *
...@@ -730,6 +748,7 @@ extern int xfrm_replay_check(struct xfrm_state *x, u32 seq); ...@@ -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 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 xfrm_check_selectors(struct xfrm_state **x, int n, struct flowi *fl);
extern int xfrm4_rcv(struct sk_buff *skb); 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_rcv(struct sk_buff **pskb);
extern int xfrm6_clear_mutable_options(struct sk_buff *skb, u16 *nh_offset, int dir); 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 int xfrm_user_policy(struct sock *sk, int optname, u8 *optval, int optlen);
...@@ -760,6 +779,7 @@ extern wait_queue_head_t km_waitq; ...@@ -760,6 +779,7 @@ extern wait_queue_head_t km_waitq;
extern void km_warn_expired(struct xfrm_state *x); extern void km_warn_expired(struct xfrm_state *x);
extern void km_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_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 xfrm4_input_init(void);
extern void xfrm6_input_init(void); extern void xfrm6_input_init(void);
......
...@@ -152,7 +152,7 @@ static int ah_output(struct sk_buff *skb) ...@@ -152,7 +152,7 @@ static int ah_output(struct sk_buff *skb)
return err; 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; int ah_hlen;
struct iphdr *iph; struct iphdr *iph;
......
...@@ -8,10 +8,17 @@ ...@@ -8,10 +8,17 @@
#include <linux/pfkeyv2.h> #include <linux/pfkeyv2.h>
#include <linux/random.h> #include <linux/random.h>
#include <net/icmp.h> #include <net/icmp.h>
#include <net/udp.h>
#define MAX_SG_ONSTACK 4 #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 esp_output(struct sk_buff *skb)
{ {
int err; int err;
...@@ -22,6 +29,8 @@ int esp_output(struct sk_buff *skb) ...@@ -22,6 +29,8 @@ int esp_output(struct sk_buff *skb)
struct crypto_tfm *tfm; struct crypto_tfm *tfm;
struct esp_data *esp; struct esp_data *esp;
struct sk_buff *trailer; struct sk_buff *trailer;
struct udphdr *uh = NULL;
struct xfrm_encap_tmpl *encap = NULL;
int blksize; int blksize;
int clen; int clen;
int alen; int alen;
...@@ -76,10 +85,29 @@ int esp_output(struct sk_buff *skb) ...@@ -76,10 +85,29 @@ int esp_output(struct sk_buff *skb)
*(u8*)(trailer->tail + clen-skb->len - 2) = (clen - skb->len)-2; *(u8*)(trailer->tail + clen-skb->len - 2) = (clen - skb->len)-2;
pskb_put(skb, trailer, clen - skb->len); 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; iph = skb->nh.iph;
if (x->props.mode) { if (x->props.mode) {
top_iph = (struct iphdr*)skb_push(skb, x->props.header_len); top_iph = (struct iphdr*)skb_push(skb, x->props.header_len);
esph = (struct ip_esp_hdr*)(top_iph+1); 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; *(u8*)(trailer->tail - 1) = IPPROTO_IPIP;
top_iph->ihl = 5; top_iph->ihl = 5;
top_iph->version = 4; top_iph->version = 4;
...@@ -89,7 +117,6 @@ int esp_output(struct sk_buff *skb) ...@@ -89,7 +117,6 @@ int esp_output(struct sk_buff *skb)
if (!(top_iph->frag_off)) if (!(top_iph->frag_off))
ip_select_ident(top_iph, dst, 0); ip_select_ident(top_iph, dst, 0);
top_iph->ttl = iph->ttl; /* TTL disclosed */ top_iph->ttl = iph->ttl; /* TTL disclosed */
top_iph->protocol = IPPROTO_ESP;
top_iph->check = 0; top_iph->check = 0;
top_iph->saddr = x->props.saddr.a4; top_iph->saddr = x->props.saddr.a4;
top_iph->daddr = x->id.daddr.a4; top_iph->daddr = x->id.daddr.a4;
...@@ -98,14 +125,37 @@ int esp_output(struct sk_buff *skb) ...@@ -98,14 +125,37 @@ int esp_output(struct sk_buff *skb)
esph = (struct ip_esp_hdr*)skb_push(skb, x->props.header_len); esph = (struct ip_esp_hdr*)skb_push(skb, x->props.header_len);
top_iph = (struct iphdr*)skb_push(skb, iph->ihl*4); top_iph = (struct iphdr*)skb_push(skb, iph->ihl*4);
memcpy(top_iph, &tmp_iph, 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; iph = &tmp_iph.iph;
top_iph->tot_len = htons(skb->len + alen); top_iph->tot_len = htons(skb->len + alen);
top_iph->protocol = IPPROTO_ESP;
top_iph->check = 0; top_iph->check = 0;
top_iph->frag_off = iph->frag_off; top_iph->frag_off = iph->frag_off;
*(u8*)(trailer->tail - 1) = iph->protocol; *(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->spi = x->id.spi;
esph->seq_no = htonl(++x->replay.oseq); esph->seq_no = htonl(++x->replay.oseq);
...@@ -163,7 +213,7 @@ int esp_output(struct sk_buff *skb) ...@@ -163,7 +213,7 @@ int esp_output(struct sk_buff *skb)
* expensive, so we only support truncated data, which is the recommended * expensive, so we only support truncated data, which is the recommended
* and common case. * 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 iphdr *iph;
struct ip_esp_hdr *esph; struct ip_esp_hdr *esph;
...@@ -173,6 +223,7 @@ int esp_input(struct xfrm_state *x, struct sk_buff *skb) ...@@ -173,6 +223,7 @@ int esp_input(struct xfrm_state *x, struct sk_buff *skb)
int alen = esp->auth.icv_trunc_len; int alen = esp->auth.icv_trunc_len;
int elen = skb->len - sizeof(struct ip_esp_hdr) - esp->conf.ivlen - alen; int elen = skb->len - sizeof(struct ip_esp_hdr) - esp->conf.ivlen - alen;
int nfrags; int nfrags;
int encap_len = 0;
if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr))) if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr)))
goto out; goto out;
...@@ -234,11 +285,45 @@ int esp_input(struct xfrm_state *x, struct sk_buff *skb) ...@@ -234,11 +285,45 @@ int esp_input(struct xfrm_state *x, struct sk_buff *skb)
/* ... check padding bits here. Silly. :-) */ /* ... 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]; iph->protocol = nexthdr[1];
pskb_trim(skb, skb->len - alen - padlen - 2); pskb_trim(skb, skb->len - alen - padlen - 2);
memcpy(workbuf, skb->nh.raw, iph->ihl*4); memcpy(workbuf, skb->nh.raw, iph->ihl*4);
skb->h.raw = skb_pull(skb, sizeof(struct ip_esp_hdr) + esp->conf.ivlen); 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); memcpy(skb->nh.raw, workbuf, iph->ihl*4);
skb->nh.iph->tot_len = htons(skb->len); skb->nh.iph->tot_len = htons(skb->len);
} }
...@@ -249,6 +334,70 @@ int esp_input(struct xfrm_state *x, struct sk_buff *skb) ...@@ -249,6 +334,70 @@ int esp_input(struct xfrm_state *x, struct sk_buff *skb)
return -EINVAL; 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) static u32 esp4_get_max_size(struct xfrm_state *x, int mtu)
{ {
struct esp_data *esp = x->data; struct esp_data *esp = x->data;
...@@ -368,6 +517,22 @@ int esp_init_state(struct xfrm_state *x, void *args) ...@@ -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; x->props.header_len = sizeof(struct ip_esp_hdr) + esp->conf.ivlen;
if (x->props.mode) if (x->props.mode)
x->props.header_len += sizeof(struct iphdr); 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->data = esp;
x->props.trailer_len = esp4_get_max_size(x, 0) - x->props.header_len; x->props.trailer_len = esp4_get_max_size(x, 0) - x->props.header_len;
return 0; return 0;
...@@ -393,6 +558,7 @@ static struct xfrm_type esp_type = ...@@ -393,6 +558,7 @@ static struct xfrm_type esp_type =
.destructor = esp_destroy, .destructor = esp_destroy,
.get_max_size = esp4_get_max_size, .get_max_size = esp4_get_max_size,
.input = esp_input, .input = esp_input,
.post_input = esp_post_input,
.output = esp_output .output = esp_output
}; };
......
...@@ -69,6 +69,7 @@ ...@@ -69,6 +69,7 @@
* YOSHIFUJI Hideaki @USAGI and: Support IPV6_V6ONLY socket option, which * YOSHIFUJI Hideaki @USAGI and: Support IPV6_V6ONLY socket option, which
* Alexey Kuznetsov: allow both IPv4 and IPv6 sockets to bind * Alexey Kuznetsov: allow both IPv4 and IPv6 sockets to bind
* a single port at the same time. * 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 * This program is free software; you can redistribute it and/or
...@@ -941,8 +942,90 @@ static void udp_close(struct sock *sk, long timeout) ...@@ -941,8 +942,90 @@ static void udp_close(struct sock *sk, long timeout)
inet_sock_release(sk); 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) 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. * 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) ...@@ -951,6 +1034,33 @@ static int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
return -1; 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 (sk->filter && skb->ip_summed != CHECKSUM_UNNECESSARY) {
if (__udp_checksum_complete(skb)) { if (__udp_checksum_complete(skb)) {
UDP_INC_STATS_BH(UdpInErrors); UDP_INC_STATS_BH(UdpInErrors);
...@@ -996,8 +1106,13 @@ static int udp_v4_mcast_deliver(struct sk_buff *skb, struct udphdr *uh, ...@@ -996,8 +1106,13 @@ static int udp_v4_mcast_deliver(struct sk_buff *skb, struct udphdr *uh,
if(sknext) if(sknext)
skb1 = skb_clone(skb, GFP_ATOMIC); skb1 = skb_clone(skb, GFP_ATOMIC);
if(skb1) if(skb1) {
udp_queue_rcv_skb(sk, 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; sk = sknext;
} while(sknext); } while(sknext);
} else } else
...@@ -1070,8 +1185,14 @@ int udp_rcv(struct sk_buff *skb) ...@@ -1070,8 +1185,14 @@ int udp_rcv(struct sk_buff *skb)
sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest, skb->dev->ifindex); sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest, skb->dev->ifindex);
if (sk != NULL) { if (sk != NULL) {
udp_queue_rcv_skb(sk, skb); int ret = udp_queue_rcv_skb(sk, skb);
sock_put(sk); 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; return 0;
} }
...@@ -1163,6 +1284,10 @@ static int udp_setsockopt(struct sock *sk, int level, int optname, ...@@ -1163,6 +1284,10 @@ static int udp_setsockopt(struct sock *sk, int level, int optname,
} }
break; break;
case UDP_ENCAP:
up->encap_type = val;
break;
default: default:
err = -ENOPROTOOPT; err = -ENOPROTOOPT;
break; break;
...@@ -1193,6 +1318,10 @@ static int udp_getsockopt(struct sock *sk, int level, int optname, ...@@ -1193,6 +1318,10 @@ static int udp_getsockopt(struct sock *sk, int level, int optname,
val = up->corkflag; val = up->corkflag;
break; break;
case UDP_ENCAP:
val = up->encap_type;
break;
default: default:
return -ENOPROTOOPT; return -ENOPROTOOPT;
}; };
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
* Changes: * Changes:
* YOSHIFUJI Hideaki @USAGI * YOSHIFUJI Hideaki @USAGI
* Split up af-specific portion * Split up af-specific portion
* Derek Atkins <derek@ihtfp.com>
* Add Encapsulation support
* *
*/ */
...@@ -13,10 +15,15 @@ ...@@ -13,10 +15,15 @@
static kmem_cache_t *secpath_cachep; static kmem_cache_t *secpath_cachep;
int xfrm4_rcv(struct sk_buff *skb) 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; int err;
u32 spi, seq; u32 spi, seq;
struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH]; struct sec_decap_state xfrm_vec[XFRM_MAX_DEPTH];
struct xfrm_state *x; struct xfrm_state *x;
int xfrm_nr = 0; int xfrm_nr = 0;
int decaps = 0; int decaps = 0;
...@@ -41,9 +48,13 @@ int xfrm4_rcv(struct sk_buff *skb) ...@@ -41,9 +48,13 @@ int xfrm4_rcv(struct sk_buff *skb)
if (x->props.replay_window && xfrm_replay_check(x, seq)) if (x->props.replay_window && xfrm_replay_check(x, seq))
goto drop_unlock; 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; goto drop_unlock;
/* only the first xfrm gets the encap type */
encap_type = 0;
if (x->props.replay_window) if (x->props.replay_window)
xfrm_replay_advance(x, seq); xfrm_replay_advance(x, seq);
...@@ -53,7 +64,7 @@ int xfrm4_rcv(struct sk_buff *skb) ...@@ -53,7 +64,7 @@ int xfrm4_rcv(struct sk_buff *skb)
spin_unlock(&x->lock); spin_unlock(&x->lock);
xfrm_vec[xfrm_nr++] = x; xfrm_vec[xfrm_nr++].xvec = x;
iph = skb->nh.iph; iph = skb->nh.iph;
...@@ -92,7 +103,7 @@ int xfrm4_rcv(struct sk_buff *skb) ...@@ -92,7 +103,7 @@ int xfrm4_rcv(struct sk_buff *skb)
if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH) if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH)
goto drop; 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; skb->sp->len += xfrm_nr;
if (decaps) { if (decaps) {
...@@ -111,7 +122,8 @@ int xfrm4_rcv(struct sk_buff *skb) ...@@ -111,7 +122,8 @@ int xfrm4_rcv(struct sk_buff *skb)
xfrm_state_put(x); xfrm_state_put(x);
drop: drop:
while (--xfrm_nr >= 0) while (--xfrm_nr >= 0)
xfrm_state_put(xfrm_vec[xfrm_nr]); xfrm_state_put(xfrm_vec[xfrm_nr].xvec);
kfree_skb(skb); kfree_skb(skb);
return 0; return 0;
} }
......
...@@ -146,7 +146,7 @@ int ah6_output(struct sk_buff *skb) ...@@ -146,7 +146,7 @@ int ah6_output(struct sk_buff *skb)
return err; 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; int ah_hlen;
struct ipv6hdr *iph; struct ipv6hdr *iph;
......
...@@ -254,7 +254,7 @@ int esp6_output(struct sk_buff *skb) ...@@ -254,7 +254,7 @@ int esp6_output(struct sk_buff *skb)
return err; 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 ipv6hdr *iph;
struct ipv6_esp_hdr *esph; struct ipv6_esp_hdr *esph;
......
...@@ -128,7 +128,7 @@ int xfrm6_rcv(struct sk_buff **pskb) ...@@ -128,7 +128,7 @@ int xfrm6_rcv(struct sk_buff **pskb)
struct sk_buff *skb = *pskb; struct sk_buff *skb = *pskb;
int err; int err;
u32 spi, seq; u32 spi, seq;
struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH]; struct sec_decap_state xfrm_vec[XFRM_MAX_DEPTH];
struct xfrm_state *x; struct xfrm_state *x;
int xfrm_nr = 0; int xfrm_nr = 0;
int decaps = 0; int decaps = 0;
...@@ -172,7 +172,7 @@ int xfrm6_rcv(struct sk_buff **pskb) ...@@ -172,7 +172,7 @@ int xfrm6_rcv(struct sk_buff **pskb)
if (x->props.replay_window && xfrm_replay_check(x, seq)) if (x->props.replay_window && xfrm_replay_check(x, seq))
goto drop_unlock; goto drop_unlock;
nexthdr = x->type->input(x, skb); nexthdr = x->type->input(x, &(xfrm_vec[xfrm_nr].decap), skb);
if (nexthdr <= 0) if (nexthdr <= 0)
goto drop_unlock; goto drop_unlock;
...@@ -185,7 +185,7 @@ int xfrm6_rcv(struct sk_buff **pskb) ...@@ -185,7 +185,7 @@ int xfrm6_rcv(struct sk_buff **pskb)
spin_unlock(&x->lock); spin_unlock(&x->lock);
xfrm_vec[xfrm_nr++] = x; xfrm_vec[xfrm_nr++].xvec = x;
iph = skb->nh.ipv6h; iph = skb->nh.ipv6h;
...@@ -229,7 +229,7 @@ int xfrm6_rcv(struct sk_buff **pskb) ...@@ -229,7 +229,7 @@ int xfrm6_rcv(struct sk_buff **pskb)
if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH) if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH)
goto drop; 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; skb->sp->len += xfrm_nr;
if (decaps) { if (decaps) {
...@@ -249,7 +249,7 @@ int xfrm6_rcv(struct sk_buff **pskb) ...@@ -249,7 +249,7 @@ int xfrm6_rcv(struct sk_buff **pskb)
drop: drop:
if (tmp_hdr) kfree(tmp_hdr); if (tmp_hdr) kfree(tmp_hdr);
while (--xfrm_nr >= 0) while (--xfrm_nr >= 0)
xfrm_state_put(xfrm_vec[xfrm_nr]); xfrm_state_put(xfrm_vec[xfrm_nr].xvec);
kfree_skb(skb); kfree_skb(skb);
return 0; return 0;
} }
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
* Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
* Kunihiro Ishiguro <kunihiro@ipinfusion.com> * Kunihiro Ishiguro <kunihiro@ipinfusion.com>
* Kazunori MIYAZAWA / USAGI Project <miyazawa@linux-ipv6.org> * Kazunori MIYAZAWA / USAGI Project <miyazawa@linux-ipv6.org>
* Derek Atkins <derek@ihtfp.com>
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -345,6 +346,10 @@ static u8 sadb_ext_min_len[] = { ...@@ -345,6 +346,10 @@ static u8 sadb_ext_min_len[] = {
[SADB_X_EXT_KMPRIVATE] = (u8) sizeof(struct sadb_x_kmprivate), [SADB_X_EXT_KMPRIVATE] = (u8) sizeof(struct sadb_x_kmprivate),
[SADB_X_EXT_POLICY] = (u8) sizeof(struct sadb_x_policy), [SADB_X_EXT_POLICY] = (u8) sizeof(struct sadb_x_policy),
[SADB_X_EXT_SA2] = (u8) sizeof(struct sadb_x_sa2), [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. */ /* 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 ...@@ -442,7 +447,8 @@ static int parse_exthdrs(struct sk_buff *skb, struct sadb_msg *hdr, void **ext_h
return -EINVAL; return -EINVAL;
if (ext_type == SADB_EXT_ADDRESS_SRC || if (ext_type == SADB_EXT_ADDRESS_SRC ||
ext_type == SADB_EXT_ADDRESS_DST || 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)) if (verify_address_len(p))
return -EINVAL; return -EINVAL;
} }
...@@ -601,6 +607,7 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys, ...@@ -601,6 +607,7 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys,
int auth_key_size = 0; int auth_key_size = 0;
int encrypt_key_size = 0; int encrypt_key_size = 0;
int sockaddr_size; int sockaddr_size;
struct xfrm_encap_tmpl *natt = NULL;
/* address family check */ /* address family check */
sockaddr_size = pfkey_sockaddr_size(x->props.family); 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, ...@@ -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; 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); skb = alloc_skb(size + 16, GFP_ATOMIC);
if (skb == NULL) if (skb == NULL)
return ERR_PTR(-ENOBUFS); return ERR_PTR(-ENOBUFS);
...@@ -858,6 +874,34 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys, ...@@ -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_sequence = 0;
sa2->sadb_x_sa2_reqid = x->props.reqid; 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; return skb;
} }
...@@ -1017,6 +1061,32 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr, ...@@ -1017,6 +1061,32 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr,
x->sel.prefixlen_s = addr->sadb_address_prefixlen; 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); x->type = xfrm_get_type(proto, x->props.family);
if (x->type == NULL) if (x->type == NULL)
goto out; goto out;
...@@ -1033,6 +1103,8 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr, ...@@ -1033,6 +1103,8 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr,
kfree(x->ealg); kfree(x->ealg);
if (x->calg) if (x->calg)
kfree(x->calg); kfree(x->calg);
if (x->encap_alg)
kfree(x->encap_alg);
kfree(x); kfree(x);
return ERR_PTR(-ENOBUFS); return ERR_PTR(-ENOBUFS);
} }
...@@ -2051,7 +2123,6 @@ static int pfkey_spdflush(struct sock *sk, struct sk_buff *skb, struct sadb_msg ...@@ -2051,7 +2123,6 @@ static int pfkey_spdflush(struct sock *sk, struct sk_buff *skb, struct sadb_msg
return 0; return 0;
} }
typedef int (*pfkey_handler)(struct sock *sk, struct sk_buff *skb, typedef int (*pfkey_handler)(struct sock *sk, struct sk_buff *skb,
struct sadb_msg *hdr, void **ext_hdrs); struct sadb_msg *hdr, void **ext_hdrs);
static pfkey_handler pfkey_funcs[SADB_MAX + 1] = { static pfkey_handler pfkey_funcs[SADB_MAX + 1] = {
...@@ -2467,6 +2538,155 @@ static struct xfrm_policy *pfkey_compile_policy(u16 family, int opt, ...@@ -2467,6 +2538,155 @@ static struct xfrm_policy *pfkey_compile_policy(u16 family, int opt,
return NULL; 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, static int pfkey_sendmsg(struct kiocb *kiocb,
struct socket *sock, struct msghdr *msg, int len) struct socket *sock, struct msghdr *msg, int len)
{ {
...@@ -2632,6 +2852,7 @@ static struct xfrm_mgr pfkeyv2_mgr = ...@@ -2632,6 +2852,7 @@ static struct xfrm_mgr pfkeyv2_mgr =
.notify = pfkey_send_notify, .notify = pfkey_send_notify,
.acquire = pfkey_send_acquire, .acquire = pfkey_send_acquire,
.compile_policy = pfkey_compile_policy, .compile_policy = pfkey_compile_policy,
.new_mapping = pfkey_send_new_mapping,
}; };
static void __exit ipsec_pfkey_exit(void) static void __exit ipsec_pfkey_exit(void)
......
...@@ -292,6 +292,7 @@ EXPORT_SYMBOL(dlci_ioctl_hook); ...@@ -292,6 +292,7 @@ EXPORT_SYMBOL(dlci_ioctl_hook);
EXPORT_SYMBOL(xfrm_user_policy); EXPORT_SYMBOL(xfrm_user_policy);
EXPORT_SYMBOL(km_waitq); EXPORT_SYMBOL(km_waitq);
EXPORT_SYMBOL(km_new_mapping);
EXPORT_SYMBOL(xfrm_cfg_sem); EXPORT_SYMBOL(xfrm_cfg_sem);
EXPORT_SYMBOL(xfrm_policy_alloc); EXPORT_SYMBOL(xfrm_policy_alloc);
EXPORT_SYMBOL(__xfrm_policy_destroy); EXPORT_SYMBOL(__xfrm_policy_destroy);
......
...@@ -14,7 +14,7 @@ void __secpath_destroy(struct sec_path *sp) ...@@ -14,7 +14,7 @@ void __secpath_destroy(struct sec_path *sp)
{ {
int i; int i;
for (i = 0; i < sp->len; 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); kmem_cache_free(sp->pool, sp);
} }
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
* Kazunori MIYAZAWA @USAGI * Kazunori MIYAZAWA @USAGI
* YOSHIFUJI Hideaki * YOSHIFUJI Hideaki
* Split up af-specific portion * 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, ...@@ -889,7 +890,7 @@ xfrm_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int idx,
unsigned short family) unsigned short family)
{ {
for (; idx < sp->len; idx++) { 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 ++idx;
} }
return -1; return -1;
...@@ -922,7 +923,15 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, ...@@ -922,7 +923,15 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
int i; int i;
for (i=skb->sp->len-1; i>=0; 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; return 0;
} }
} }
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
* IPv6 support * IPv6 support
* YOSHIFUJI Hideaki @USAGI * YOSHIFUJI Hideaki @USAGI
* Split up af-specific functions * 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) ...@@ -155,6 +157,8 @@ void __xfrm_state_destroy(struct xfrm_state *x)
kfree(x->ealg); kfree(x->ealg);
if (x->calg) if (x->calg)
kfree(x->calg); kfree(x->calg);
if (x->encap_alg)
kfree(x->encap_alg);
if (x->type) if (x->type)
xfrm_put_type(x->type); xfrm_put_type(x->type);
kfree(x); kfree(x);
...@@ -632,6 +636,22 @@ int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol) ...@@ -632,6 +636,22 @@ int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
return err; 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 xfrm_user_policy(struct sock *sk, int optname, u8 *optval, int optlen)
{ {
int err; 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