Commit fefd9246 authored by Alexey Kuznetsov's avatar Alexey Kuznetsov Committed by David S. Miller

[IPSEC]: Bug fixes and updates.

- Implement IP_IPSEC_POLICY setsockopt
- Rework input policy checks to use it
- dst->child destruction is repaired
- Fix tunnel mode IP header building.
parent b9346730
......@@ -69,6 +69,7 @@ struct in_addr {
#define IP_RECVTOS 13
#define IP_MTU 14
#define IP_FREEBIND 15
#define IP_IPSEC_POLICY 16
/* BSD compatibility */
#define IP_RECVRETOPTS IP_RETOPTS
......
......@@ -43,15 +43,4 @@ enum {
#define IPSEC_REPLAYWSIZE 32
#ifdef __KERNEL__
struct sock;
struct sk_buff;
static __inline__ int ipsec_sk_policy(struct sock *sk, struct sk_buff *skb)
{
return 1;
}
#endif
#endif /* _LINUX_IPSEC_H */
......@@ -44,6 +44,7 @@ struct dst_entry
#define DST_HOST 1
#define DST_NOXFRM 2
#define DST_NOPOLICY 4
#define DST_NOHASH 8
unsigned long lastuse;
unsigned long expires;
......@@ -138,8 +139,15 @@ struct dst_entry * dst_clone(struct dst_entry * dst)
static inline
void dst_release(struct dst_entry * dst)
{
if (dst)
if (dst) {
if (atomic_read(&dst->__refcnt) < 1) {
__label__ __lbl;
printk("BUG: dst underflow %d: %p\n",
atomic_read(&dst->__refcnt), &&__lbl);
__lbl:
}
atomic_dec(&dst->__refcnt);
}
}
/* Children define the path of the packet through the
......
......@@ -38,6 +38,7 @@ struct inet_protocol
{
int (*handler)(struct sk_buff *skb);
void (*err_handler)(struct sk_buff *skb, u32 info);
int no_policy;
};
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
......
......@@ -114,6 +114,7 @@ extern void ip_rt_advice(struct rtable **rp, int advice);
extern void rt_cache_flush(int how);
extern int __ip_route_output_key(struct rtable **, const struct flowi *flp);
extern int ip_route_output_key(struct rtable **, struct flowi *flp);
extern int ip_route_output_flow(struct rtable **rp, struct flowi *flp, struct sock *sk, int flags);
extern int ip_route_input(struct sk_buff*, u32 dst, u32 src, u8 tos, struct net_device *devin);
extern unsigned short ip_rt_frag_needed(struct iphdr *iph, unsigned short new_mtu);
extern void ip_rt_send_redirect(struct sk_buff *skb);
......
......@@ -110,6 +110,7 @@ struct sock {
wait_queue_head_t *sleep; /* Sock wait queue */
struct dst_entry *dst_cache; /* Destination cache */
rwlock_t dst_lock;
struct xfrm_policy *policy[2];
atomic_t rmem_alloc; /* Receive queue bytes committed */
struct sk_buff_head receive_queue; /* Incoming packets */
atomic_t wmem_alloc; /* Transmit queue bytes committed */
......
......@@ -312,6 +312,7 @@ struct xfrm_mgr
char *id;
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)(int opt, u8 *data, int len, int *dir);
};
extern int xfrm_register_km(struct xfrm_mgr *km);
......@@ -397,13 +398,16 @@ secpath_put(struct sec_path *sp)
__secpath_destroy(sp);
}
extern int __xfrm_policy_check(int dir, struct sk_buff *skb);
extern int __xfrm_policy_check(struct sock *, int dir, struct sk_buff *skb);
static inline int xfrm_policy_check(int dir, struct sk_buff *skb)
static inline int xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb)
{
if (sk && sk->policy[XFRM_POLICY_IN])
return __xfrm_policy_check(sk, dir, skb);
return !xfrm_policy_list[dir] ||
(skb->dst->flags & DST_NOPOLICY) ||
__xfrm_policy_check(dir, skb);
__xfrm_policy_check(sk, dir, skb);
}
extern int __xfrm_route_forward(struct sk_buff *skb);
......@@ -431,6 +435,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 xfrm_user_policy(struct sock *sk, int optname, u8 *optval, int optlen);
struct xfrm_policy *xfrm_policy_alloc(void);
extern int xfrm_policy_walk(int (*func)(struct xfrm_policy *, int, int, void*), void *);
......@@ -439,12 +444,12 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl);
struct xfrm_policy *xfrm_policy_delete(int dir, struct xfrm_selector *sel);
struct xfrm_policy *xfrm_policy_byid(int dir, u32 id, int delete);
void xfrm_policy_flush(void);
int xfrm_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm,
struct flowi *fl, struct dst_entry **dst_p);
void xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi);
struct xfrm_state * xfrm_find_acq(u8 mode, u16 reqid, u8 proto, u32 daddr, u32 saddr);
extern void xfrm_policy_flush(void);
extern void xfrm_policy_kill(struct xfrm_policy *);
extern int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol);
extern struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struct flowi *fl);
extern wait_queue_head_t *km_waitq;
extern void km_warn_expired(struct xfrm_state *x);
......
......@@ -36,6 +36,7 @@ static spinlock_t dst_lock = SPIN_LOCK_UNLOCKED;
static unsigned long dst_gc_timer_expires;
static unsigned long dst_gc_timer_inc = DST_GC_MAX;
static void dst_run_gc(unsigned long);
static void ___dst_free(struct dst_entry * dst);
static struct timer_list dst_gc_timer =
{ data: DST_GC_MIN, function: dst_run_gc };
......@@ -59,12 +60,26 @@ static void dst_run_gc(unsigned long dummy)
delayed++;
continue;
}
if (dst->child) {
dst->child->next = dst->next;
*dstp = dst->child;
} else
*dstp = dst->next;
dst_destroy(dst);
*dstp = dst->next;
dst = dst_destroy(dst);
if (dst) {
/* NOHASH and still referenced. Unless it is already
* on gc list, invalidate it and add to gc list.
*
* Note: this is temporary. Actually, NOHASH dst's
* must be obsoleted when parent is obsoleted.
* But we do not have state "obsoleted, but
* referenced by parent", so it is right.
*/
if (dst->obsolete > 1)
continue;
___dst_free(dst);
dst->next = *dstp;
*dstp = dst;
dstp = &dst->next;
}
}
if (!dst_garbage_list) {
dst_gc_timer_inc = DST_GC_MAX;
......@@ -120,10 +135,8 @@ void * dst_alloc(struct dst_ops * ops)
return dst;
}
void __dst_free(struct dst_entry * dst)
static void ___dst_free(struct dst_entry * dst)
{
spin_lock_bh(&dst_lock);
/* The first case (dev==NULL) is required, when
protocol module is unloaded.
*/
......@@ -132,6 +145,12 @@ void __dst_free(struct dst_entry * dst)
dst->output = dst_blackhole;
}
dst->obsolete = 2;
}
void __dst_free(struct dst_entry * dst)
{
spin_lock_bh(&dst_lock);
___dst_free(dst);
dst->next = dst_garbage_list;
dst_garbage_list = dst;
if (dst_gc_timer_inc > DST_GC_INC) {
......@@ -141,7 +160,6 @@ void __dst_free(struct dst_entry * dst)
dst_gc_timer.expires = jiffies + dst_gc_timer_expires;
add_timer(&dst_gc_timer);
}
spin_unlock_bh(&dst_lock);
}
......@@ -177,10 +195,19 @@ struct dst_entry *dst_destroy(struct dst_entry * dst)
kmem_cache_free(dst->ops->kmem_cachep, dst);
dst = child;
if (dst && !atomic_read(&dst->__refcnt))
goto again;
return dst;
if (dst) {
if (atomic_dec_and_test(&dst->__refcnt)) {
/* We were real parent of this dst, so kill child. */
if (dst->flags&DST_NOHASH)
goto again;
} else {
/* Child is still referenced, return it for freeing. */
if (dst->flags&DST_NOHASH)
return dst;
/* Child is still in his hash table */
}
}
return NULL;
}
static int dst_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
......
......@@ -349,14 +349,14 @@ config SYN_COOKIES
If unsure, say N.
config INET_AH
bool "IP: AH transformation"
tristate "IP: AH transformation"
---help---
Support for IPsec AH.
If unsure, say Y.
config INET_ESP
bool "IP: ESP transformation"
tristate "IP: ESP transformation"
---help---
Support for IPsec ESP.
......
......@@ -1038,11 +1038,13 @@ static struct inet_protocol igmp_protocol = {
static struct inet_protocol tcp_protocol = {
.handler = tcp_v4_rcv,
.err_handler = tcp_v4_err,
.no_policy = 1,
};
static struct inet_protocol udp_protocol = {
.handler = udp_rcv,
.err_handler = udp_err,
.no_policy = 1,
};
static struct inet_protocol icmp_protocol = {
......
......@@ -176,12 +176,13 @@ int ah_output(struct sk_buff *skb)
iph = skb->nh.iph;
if (x->props.mode) {
top_iph = (struct iphdr*)skb_push(skb, x->props.header_len);
top_iph->ihl = 4;
top_iph->version = 5;
top_iph->ihl = 5;
top_iph->version = 4;
top_iph->tos = 0;
top_iph->tot_len = htons(skb->len);
top_iph->id = inet_getid(((struct rtable*)dst)->peer, 0);
top_iph->frag_off = 0;
if (!(iph->frag_off&htons(IP_DF)))
__ip_select_ident(top_iph, dst, 0);
top_iph->ttl = 0;
top_iph->protocol = IPPROTO_AH;
top_iph->check = 0;
......@@ -379,6 +380,7 @@ static struct xfrm_type ah_type =
static struct inet_protocol ah4_protocol = {
.handler = xfrm4_rcv,
.err_handler = ah4_err,
.no_policy = 1,
};
int __init ah4_init(void)
......
......@@ -187,33 +187,13 @@ esp_hmac_digest(struct esp_data *esp, struct sk_buff *skb, int offset,
int len, u8 *auth_data)
{
struct crypto_tfm *tfm = esp->auth.tfm;
int i;
char tmp_digest[crypto_tfm_alg_digestsize(tfm)];
char pad[crypto_tfm_alg_blocksize(tfm)];
char digest[crypto_tfm_alg_digestsize(tfm)];
memset(auth_data, 0, esp->auth.authlen);
memset(pad, 0, sizeof(pad));
memcpy(pad, esp->auth.key, esp->auth.key_len);
for (i = 0; i < crypto_tfm_alg_blocksize(tfm); i++)
pad[i] ^= 0x36;
crypto_digest_init(tfm);
tfm->__crt_alg->cra_digest.dia_update(tfm->crt_ctx, pad, sizeof(pad));
crypto_hmac_init(tfm, esp->auth.key, &esp->auth.key_len);
skb_digest_walk(skb, tfm, offset, len);
tfm->__crt_alg->cra_digest.dia_update(tfm->crt_ctx, skb->data+offset, len);
crypto_digest_final(tfm, tmp_digest);
memset(pad, 0, sizeof(pad));
memcpy(pad, esp->auth.key, esp->auth.key_len);
for (i = 0; i < crypto_tfm_alg_blocksize(tfm); i++)
pad[i] ^= 0x5c;
crypto_digest_init(tfm);
tfm->__crt_alg->cra_digest.dia_update(tfm->crt_ctx, pad, sizeof(pad));
tfm->__crt_alg->cra_digest.dia_update(tfm->crt_ctx, tmp_digest, crypto_tfm_alg_digestsize(tfm));
crypto_digest_final(tfm, auth_data);
crypto_hmac_final(tfm, esp->auth.key, &esp->auth.key_len, digest);
memcpy(auth_data, digest, crypto_tfm_alg_digestsize(tfm));
}
/* Check that skb data bits are writable. If they are not, copy data
......@@ -390,12 +370,13 @@ int esp_output(struct sk_buff *skb)
top_iph = (struct iphdr*)skb_push(skb, x->props.header_len);
esph = (struct ip_esp_hdr*)(top_iph+1);
*(u8*)(trailer->tail - 1) = IPPROTO_IP;
top_iph->ihl = 4;
top_iph->version = 5;
top_iph->ihl = 5;
top_iph->version = 4;
top_iph->tos = iph->tos; /* DS disclosed */
top_iph->tot_len = htons(skb->len + esp->auth.authlen);
top_iph->id = inet_getid(((struct rtable*)dst)->peer, 0);
top_iph->frag_off = iph->frag_off&htons(IP_DF);
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;
......@@ -668,6 +649,7 @@ static struct xfrm_type esp_type =
static struct inet_protocol esp4_protocol = {
.handler = xfrm4_rcv,
.err_handler = esp4_err,
.no_policy = 1,
};
int __init esp4_init(void)
......
......@@ -60,7 +60,7 @@ int ip_forward(struct sk_buff *skb)
struct rtable *rt; /* Route we use */
struct ip_options * opt = &(IPCB(skb)->opt);
if (!xfrm_policy_check(XFRM_POLICY_FWD, skb))
if (!xfrm_policy_check(NULL, XFRM_POLICY_FWD, skb))
goto drop;
if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb))
......
......@@ -223,15 +223,6 @@ static inline int ip_local_deliver_finish(struct sk_buff *skb)
struct inet_protocol *ipprot;
resubmit:
/* Fuck... This IS ugly. */
if (protocol != IPPROTO_AH &&
protocol != IPPROTO_ESP &&
!xfrm_policy_check(XFRM_POLICY_IN, skb)) {
kfree_skb(skb);
return 0;
}
hash = protocol & (MAX_INET_PROTOS - 1);
raw_sk = raw_v4_htable[hash];
......@@ -242,7 +233,14 @@ static inline int ip_local_deliver_finish(struct sk_buff *skb)
raw_v4_input(skb, skb->nh.iph, hash);
if ((ipprot = inet_protos[hash]) != NULL) {
int ret = ipprot->handler(skb);
int ret;
if (!ipprot->no_policy &&
!xfrm_policy_check(NULL, XFRM_POLICY_IN, skb)) {
kfree_skb(skb);
return 0;
}
ret = ipprot->handler(skb);
if (ret < 0) {
protocol = -ret;
goto resubmit;
......@@ -250,9 +248,11 @@ static inline int ip_local_deliver_finish(struct sk_buff *skb)
IP_INC_STATS_BH(IpInDelivers);
} else {
if (!raw_sk) {
IP_INC_STATS_BH(IpInUnknownProtos);
icmp_send(skb, ICMP_DEST_UNREACH,
ICMP_PROT_UNREACH, 0);
if (xfrm_policy_check(NULL, XFRM_POLICY_IN, skb)) {
IP_INC_STATS_BH(IpInUnknownProtos);
icmp_send(skb, ICMP_DEST_UNREACH,
ICMP_PROT_UNREACH, 0);
}
} else
IP_INC_STATS_BH(IpInDelivers);
kfree_skb(skb);
......
......@@ -321,7 +321,7 @@ int ip_queue_xmit(struct sk_buff *skb)
* keep trying until route appears or the connection times
* itself out.
*/
if (ip_route_output_key(&rt, &fl))
if (ip_route_output_flow(&rt, &fl, sk, 0))
goto no_route;
}
__sk_dst_set(sk, &rt->u.dst);
......@@ -1220,6 +1220,10 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *ar
{ .daddr = daddr,
.saddr = rt->rt_spec_dst,
.tos = RT_TOS(skb->nh.iph->tos) } },
/* Not quite clean, but right. */
.uli_u = { .ports =
{ .sport = skb->h.th->dest,
.dport = skb->h.th->source } },
.proto = sk->protocol };
if (ip_route_output_key(&rt, &fl))
return;
......
......@@ -36,6 +36,7 @@
#include <linux/route.h>
#include <linux/mroute.h>
#include <net/route.h>
#include <net/xfrm.h>
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
#include <net/transp_v6.h>
#endif
......@@ -624,6 +625,10 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
inet->freebind = !!val;
break;
case IP_IPSEC_POLICY:
err = xfrm_user_policy(sk, optname, optval, optlen);
break;
default:
#ifdef CONFIG_NETFILTER
err = nf_setsockopt(sk, PF_INET, optname, optval,
......
......@@ -64,6 +64,7 @@
#include <net/raw.h>
#include <net/inet_common.h>
#include <net/checksum.h>
#include <net/xfrm.h>
#include <linux/netfilter_ipv4.h>
struct sock *raw_v4_htable[RAWV4_HTABLE_SIZE];
......@@ -237,6 +238,11 @@ static int raw_rcv_skb(struct sock * sk, struct sk_buff * skb)
int raw_rcv(struct sock *sk, struct sk_buff *skb)
{
if (xfrm_policy_check(sk, XFRM_POLICY_IN, skb)) {
kfree_skb(skb);
return NET_RX_DROP;
}
skb_push(skb, skb->data - skb->nh.raw);
raw_rcv_skb(sk, skb);
......@@ -425,7 +431,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
.saddr = saddr,
.tos = tos } },
.proto = IPPROTO_RAW };
err = ip_route_output_key(&rt, &fl);
err = ip_route_output_flow(&rt, &fl, sk, msg->msg_flags&MSG_DONTWAIT);
}
if (err)
goto done;
......
......@@ -2034,6 +2034,15 @@ int ip_route_output_key(struct rtable **rp, struct flowi *flp)
return flp->proto ? xfrm_lookup((struct dst_entry**)rp, flp, NULL, 0) : 0;
}
int ip_route_output_flow(struct rtable **rp, struct flowi *flp, struct sock *sk, int flags)
{
int err;
if ((err = __ip_route_output_key(rp, flp)) != 0)
return err;
return flp->proto ? xfrm_lookup((struct dst_entry**)rp, flp, sk, flags) : 0;
}
static int rt_fill_info(struct sk_buff *skb, u32 pid, u32 seq, int event,
int nowait)
{
......
......@@ -64,11 +64,11 @@
#include <net/tcp.h>
#include <net/ipv6.h>
#include <net/inet_common.h>
#include <net/xfrm.h>
#include <linux/inet.h>
#include <linux/ipv6.h>
#include <linux/stddef.h>
#include <linux/ipsec.h>
extern int sysctl_ip_dynaddr;
extern int sysctl_ip_default_ttl;
......@@ -1299,7 +1299,7 @@ static struct dst_entry* tcp_v4_route_req(struct sock *sk,
{ .sport = inet_sk(sk)->sport,
.dport = req->rmt_port } } };
if (ip_route_output_key(&rt, &fl)) {
if (ip_route_output_flow(&rt, &fl, sk, 0)) {
IP_INC_STATS_BH(IpOutNoRoutes);
return NULL;
}
......@@ -1796,12 +1796,12 @@ int tcp_v4_rcv(struct sk_buff *skb)
goto no_tcp_socket;
process:
if (!ipsec_sk_policy(sk, skb))
goto discard_and_relse;
if (sk->state == TCP_TIME_WAIT)
goto do_time_wait;
if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb))
goto discard_and_relse;
skb->dev = NULL;
bh_lock_sock(sk);
......@@ -1818,6 +1818,9 @@ int tcp_v4_rcv(struct sk_buff *skb)
return ret;
no_tcp_socket:
if (!xfrm_policy_check(NULL, XFRM_POLICY_IN, skb))
goto discard_it;
if (skb->len < (th->doff << 2) || tcp_checksum_complete(skb)) {
bad_packet:
TCP_INC_STATS_BH(TcpInErrs);
......@@ -1835,6 +1838,9 @@ int tcp_v4_rcv(struct sk_buff *skb)
goto discard_it;
do_time_wait:
if (!xfrm_policy_check(NULL, XFRM_POLICY_IN, skb))
goto discard_and_relse;
if (skb->len < (th->doff << 2) || tcp_checksum_complete(skb)) {
TCP_INC_STATS_BH(TcpInErrs);
goto discard_and_relse;
......@@ -1950,7 +1956,7 @@ int tcp_v4_rebuild_header(struct sock *sk)
{ .sport = inet->sport,
.dport = inet->dport } } };
err = ip_route_output_key(&rt, &fl);
err = ip_route_output_flow(&rt, &fl, sk, 0);
}
if (!err) {
__sk_dst_set(sk, &rt->u.dst);
......
......@@ -104,6 +104,7 @@
#include <net/route.h>
#include <net/inet_common.h>
#include <net/checksum.h>
#include <net/xfrm.h>
/*
* Snmp MIB for the UDP layer
......@@ -600,7 +601,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
.uli_u = { .ports =
{ .sport = inet->sport,
.dport = dport } } };
err = ip_route_output_key(&rt, &fl);
err = ip_route_output_flow(&rt, &fl, sk, msg->msg_flags&MSG_DONTWAIT);
if (err)
goto out;
......@@ -943,6 +944,10 @@ static int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
/*
* Charge it to the socket, dropping if the queue is full.
*/
if (!xfrm_policy_check(NULL, XFRM_POLICY_IN, skb)) {
kfree_skb(skb);
return -1;
}
#if defined(CONFIG_FILTER)
if (sk->filter && skb->ip_summed != CHECKSUM_UNNECESSARY) {
......@@ -1070,6 +1075,9 @@ int udp_rcv(struct sk_buff *skb)
return 0;
}
if (!xfrm_policy_check(NULL, XFRM_POLICY_IN, skb))
goto drop;
/* No socket. Drop packet silently, if checksum is wrong */
if (udp_checksum_complete(skb))
goto csum_error;
......@@ -1110,6 +1118,7 @@ int udp_rcv(struct sk_buff *skb)
NIPQUAD(daddr),
ntohs(uh->dest),
ulen));
drop:
UDP_INC_STATS_BH(UdpInErrors);
kfree_skb(skb);
return(0);
......
......@@ -123,8 +123,10 @@ int xfrm4_rcv(struct sk_buff *skb)
skb->sp->len += xfrm_nr;
if (decaps) {
dst_release(skb->dst);
skb->dst = NULL;
if (!(skb->dev->flags&IFF_LOOPBACK)) {
dst_release(skb->dst);
skb->dst = NULL;
}
netif_rx(skb);
return 0;
} else {
......
......@@ -397,7 +397,7 @@ int xfrm_policy_walk(int (*func)(struct xfrm_policy *, int, int, void*),
struct xfrm_policy *xfrm_policy_lookup(int dir, struct flowi *fl)
{
struct xfrm_policy *pol;
// Not now :-) u64 now = (unsigned long)xtime.tv_sec;
/* Not now :-) u64 now = (unsigned long)xtime.tv_sec; */
read_lock(&xfrm_policy_lock);
for (pol = xfrm_policy_list[dir]; pol; pol = pol->next) {
......@@ -412,6 +412,40 @@ struct xfrm_policy *xfrm_policy_lookup(int dir, struct flowi *fl)
return pol;
}
struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struct flowi *fl)
{
struct xfrm_policy *pol;
/* Not now :-) u64 now = (unsigned long)xtime.tv_sec; */
read_lock(&xfrm_policy_lock);
for (pol = sk->policy[dir]; pol; pol = pol->next) {
struct xfrm_selector *sel = &pol->selector;
if (xfrm4_selector_match(sel, fl) /* XXX && XXX now < pol->validtime */) {
atomic_inc(&pol->refcnt);
break;
}
}
read_unlock(&xfrm_policy_lock);
return pol;
}
int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol)
{
struct xfrm_policy *old_pol;
write_lock_bh(&xfrm_policy_lock);
old_pol = sk->policy[dir];
sk->policy[dir] = pol;
write_unlock_bh(&xfrm_policy_lock);
if (old_pol) {
xfrm_policy_kill(old_pol);
xfrm_pol_put(old_pol);
}
return 0;
}
/* Resolve list of templates for the flow, given policy. */
static int
......@@ -482,12 +516,13 @@ static int xfrm_bundle_ok(struct xfrm_dst *xdst, struct flowi *fl)
* all the metrics... Shortly, bundle a bundle.
*/
int
static int
xfrm_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm,
struct flowi *fl, struct dst_entry **dst_p)
{
struct dst_entry *dst, *dst_prev;
struct rtable *rt = (struct rtable*)(*dst_p);
struct rtable *rt0 = (struct rtable*)(*dst_p);
struct rtable *rt = rt0;
u32 remote = fl->fl4_dst;
u32 local = fl->fl4_src;
int i;
......@@ -507,8 +542,11 @@ xfrm_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm,
dst1->xfrm = xfrm[i];
if (!dst)
dst = dst1;
else
else {
dst_prev->child = dst1;
dst1->flags |= DST_NOHASH;
dst_clone(dst1);
}
dst_prev = dst1;
if (xfrm[i]->props.mode) {
remote = xfrm[i]->id.daddr.xfrm4_addr;
......@@ -523,11 +561,11 @@ xfrm_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm,
.saddr = local }
}
};
err = ip_route_output_key(&rt, &fl_tunnel);
err = __ip_route_output_key(&rt, &fl_tunnel);
if (err)
goto error;
dst_release(*dst_p);
*dst_p = &rt->u.dst;
} else {
dst_clone(&rt->u.dst);
}
dst_prev->child = &rt->u.dst;
for (dst_prev = dst; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) {
......@@ -537,7 +575,7 @@ xfrm_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm,
dst_prev->dev = rt->u.dst.dev;
if (rt->u.dst.dev)
dev_hold(rt->u.dst.dev);
dst_prev->flags = DST_HOST;
dst_prev->flags |= DST_HOST;
dst_prev->lastuse = jiffies;
dst_prev->header_len = header_len;
memcpy(&dst_prev->metrics, &rt->u.dst.metrics, sizeof(dst_prev->metrics));
......@@ -550,13 +588,14 @@ xfrm_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm,
if (rt->peer)
atomic_inc(&rt->peer->refcnt);
x->u.rt.peer = rt->peer;
/* Sheit... I remember I did this right. Apparently,
* it was magically lost, so this code needs audit */
x->u.rt.rt_flags = rt->rt_flags;
x->u.rt.rt_type = rt->rt_type;
x->u.rt.rt_src = rt->rt_src;
x->u.rt.rt_src = rt->rt_src;
x->u.rt.rt_dst = rt->rt_dst;
x->u.rt.rt_src = rt0->rt_src;
x->u.rt.rt_dst = rt0->rt_dst;
x->u.rt.rt_gateway = rt->rt_gateway;
x->u.rt.rt_spec_dst = rt->rt_spec_dst;
x->u.rt.rt_spec_dst = rt0->rt_spec_dst;
header_len -= x->u.dst.xfrm->props.header_len;
}
*dst_p = dst;
......@@ -583,18 +622,24 @@ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
int err;
u32 genid;
/* To accelerate a bit... */
if ((rt->u.dst.flags & DST_NOXFRM) || !xfrm_policy_list[XFRM_POLICY_OUT])
return 0;
fl->oif = rt->u.dst.dev->ifindex;
fl->fl4_src = rt->rt_src;
restart:
genid = xfrm_policy_genid;
policy = flow_lookup(XFRM_POLICY_OUT, fl);
if (!policy)
return 0;
policy = NULL;
if (sk && sk->policy[1])
policy = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl);
if (!policy) {
/* To accelerate a bit... */
if ((rt->u.dst.flags & DST_NOXFRM) || !xfrm_policy_list[XFRM_POLICY_OUT])
return 0;
policy = flow_lookup(XFRM_POLICY_OUT, fl);
if (!policy)
return 0;
}
switch (policy->action) {
case XFRM_POLICY_BLOCK:
......@@ -677,14 +722,13 @@ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
write_unlock_bh(&policy->lock);
xfrm_pol_put(policy);
if (dst) {
dst_release(dst);
if (dst)
dst_free(dst);
}
goto restart;
}
dst->next = policy->bundles;
policy->bundles = dst;
dst_clone(dst);
write_unlock_bh(&policy->lock);
}
*dst_p = dst;
......@@ -773,7 +817,7 @@ _decode_session(struct sk_buff *skb, struct flowi *fl)
fl->fl4_src = iph->saddr;
}
int __xfrm_policy_check(int dir, struct sk_buff *skb)
int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb)
{
struct xfrm_policy *pol;
struct flowi fl;
......@@ -789,7 +833,12 @@ int __xfrm_policy_check(int dir, struct sk_buff *skb)
}
}
pol = flow_lookup(dir, &fl);
pol = NULL;
if (sk && sk->policy[dir])
pol =xfrm_sk_policy_lookup(sk, dir, &fl);
if (!pol)
pol = flow_lookup(dir, &fl);
if (!pol)
return 1;
......@@ -925,10 +974,13 @@ static int xfrm4_get_mss(struct dst_entry *dst, u32 mtu)
do {
struct xfrm_state *x = d->xfrm;
if (x) {
if (x->type->get_max_size)
spin_lock_bh(&x->lock);
if (x->km.state == XFRM_STATE_VALID &&
x->type && x->type->get_max_size)
m = x->type->get_max_size(d->xfrm, m);
else
m += x->props.header_len;
spin_unlock_bh(&x->lock);
}
} while ((d = d->child) != NULL);
......
......@@ -508,6 +508,43 @@ int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
return err;
}
int xfrm_user_policy(struct sock *sk, int optname, u8 *optval, int optlen)
{
int err;
u8 *data;
struct xfrm_mgr *km;
struct xfrm_policy *pol = NULL;
if (optlen <= 0 || optlen > PAGE_SIZE)
return -EMSGSIZE;
data = kmalloc(optlen, GFP_KERNEL);
if (!data)
return -ENOMEM;
err = -EFAULT;
if (copy_from_user(data, optval, optlen))
goto out;
err = -EINVAL;
read_lock(&xfrm_km_lock);
list_for_each_entry(km, &xfrm_km_list, list) {
pol = km->compile_policy(optname, data, optlen, &err);
if (err >= 0)
break;
}
read_unlock(&xfrm_km_lock);
if (err >= 0) {
xfrm_sk_policy_insert(sk, err, pol);
err = 0;
}
out:
kfree(data);
return err;
}
int xfrm_register_km(struct xfrm_mgr *km)
{
write_lock_bh(&xfrm_km_lock);
......
......@@ -107,7 +107,8 @@ struct rt6_info ip6_null_entry = {
.error = -ENETUNREACH,
.input = ip6_pkt_discard,
.output = ip6_pkt_discard,
.ops = &ip6_dst_ops
.ops = &ip6_dst_ops,
.path = (struct dst_entry*)&ip6_null_entry,
}
},
.rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
......@@ -1252,6 +1253,9 @@ static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
*/
idev = __in6_dev_get(arg->dev);
if (idev == NULL)
return 0;
/* For administrative MTU increase, there is no way to discover
IPv6 PMTU increase, so PMTU increase should be updated here.
Since RFC 1981 doesn't include administrative MTU increase
......
......@@ -1638,8 +1638,6 @@ static int tcp_v6_rcv(struct sk_buff *skb)
goto no_tcp_socket;
process:
if(!ipsec_sk_policy(sk,skb))
goto discard_and_relse;
if(sk->state == TCP_TIME_WAIT)
goto do_time_wait;
......
......@@ -1080,38 +1080,6 @@ static int pfkey_acquire(struct sock *sk, struct sk_buff *skb, struct sadb_msg *
}
static int pfkey_update(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
{
struct sk_buff *resp_skb;
int err;
if (!ext_hdrs[SADB_EXT_SA-1] ||
!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
ext_hdrs[SADB_EXT_ADDRESS_DST-1]))
return -EINVAL;
if (hdr->sadb_msg_satype == SADB_SATYPE_ESP &&
!ext_hdrs[SADB_EXT_KEY_ENCRYPT-1])
return -EINVAL;
if (hdr->sadb_msg_satype == SADB_SATYPE_AH &&
!ext_hdrs[SADB_EXT_KEY_AUTH-1])
return -EINVAL;
if (!!ext_hdrs[SADB_EXT_LIFETIME_HARD-1] !=
!!ext_hdrs[SADB_EXT_LIFETIME_SOFT-1])
return -EINVAL;
/* XXX Implement XXX */
resp_skb = NULL;
err = -EOPNOTSUPP;
if (!err)
pfkey_broadcast(resp_skb, GFP_KERNEL, BROADCAST_ONE, sk);
return err;
}
static int pfkey_add(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
{
struct sk_buff *out_skb;
......@@ -1125,7 +1093,7 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr,
/* XXX there is race condition */
x1 = pfkey_xfrm_state_lookup(hdr, ext_hdrs);
if (x1 != NULL) {
if (x1 && hdr->sadb_msg_type == SADB_ADD) {
x->km.state = XFRM_STATE_DEAD;
xfrm_state_put(x);
xfrm_state_put(x1);
......@@ -1134,6 +1102,11 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr,
xfrm_state_insert(x);
if (x1 && hdr->sadb_msg_type != SADB_ADD) {
xfrm_state_delete(x1);
xfrm_state_put(x1);
}
out_skb = pfkey_xfrm_state2msg(x, 0, 3);
if (IS_ERR(out_skb))
return PTR_ERR(out_skb); /* XXX Should we return 0 here ? */
......@@ -1166,7 +1139,6 @@ static int pfkey_delete(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
return -ESRCH;
xfrm_state_delete(x);
xfrm_state_put(x);
pfkey_broadcast(skb_clone(skb, GFP_KERNEL), GFP_KERNEL,
......@@ -1382,7 +1354,7 @@ parse_ipsecrequest(struct xfrm_policy *xp, struct sadb_x_ipsecrequest *rq)
struct sockaddr_in *addr;
if (xp->xfrm_nr >= XFRM_MAX_DEPTH)
return -EINVAL;
return -ELOOP;
if (rq->sadb_x_ipsecrequest_mode == 0)
return -EINVAL;
......@@ -1408,7 +1380,7 @@ parse_ipsecrequests(struct xfrm_policy *xp, struct sadb_x_policy *pol)
int len = pol->sadb_x_policy_len*8 - sizeof(struct sadb_x_policy);
struct sadb_x_ipsecrequest *rq = (void*)(pol+1);
while (len > sizeof(struct sadb_x_ipsecrequest)) {
while (len >= sizeof(struct sadb_x_ipsecrequest)) {
if ((err = parse_ipsecrequest(xp, rq)) < 0)
return err;
len -= rq->sadb_x_ipsecrequest_len;
......@@ -1647,7 +1619,7 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ALL, sk);
return 0;
out:
out:
kfree(xp);
return err;
}
......@@ -1823,7 +1795,7 @@ typedef int (*pfkey_handler)(struct sock *sk, struct sk_buff *skb,
static pfkey_handler pfkey_funcs[SADB_MAX + 1] = {
[SADB_RESERVED] = pfkey_reserved,
[SADB_GETSPI] = pfkey_getspi,
[SADB_UPDATE] = pfkey_update,
[SADB_UPDATE] = pfkey_add,
[SADB_ADD] = pfkey_add,
[SADB_DELETE] = pfkey_delete,
[SADB_GET] = pfkey_get,
......@@ -2084,6 +2056,58 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct
return 0;
}
static struct xfrm_policy *pfkey_compile_policy(int opt, u8 *data, int len, int *dir)
{
struct xfrm_policy *xp;
struct sadb_x_policy *pol = (struct sadb_x_policy*)data;
if (opt != IP_IPSEC_POLICY) {
*dir = -EOPNOTSUPP;
return NULL;
}
*dir = -EINVAL;
if (len < sizeof(struct sadb_x_policy) ||
pol->sadb_x_policy_len*8 > len ||
pol->sadb_x_policy_type > IPSEC_POLICY_BYPASS ||
(!pol->sadb_x_policy_dir || pol->sadb_x_policy_dir > IPSEC_DIR_OUTBOUND))
return NULL;
xp = xfrm_policy_alloc();
if (xp == NULL) {
*dir = -ENOBUFS;
return NULL;
}
xp->index = pol->sadb_x_policy_id;
xp->action = (pol->sadb_x_policy_type == IPSEC_POLICY_DISCARD ?
XFRM_POLICY_BLOCK : XFRM_POLICY_ALLOW);
xp->curlft.add_time = (unsigned long)xtime.tv_sec;
xp->curlft.use_time = (unsigned long)xtime.tv_sec;
xp->lft.soft_byte_limit = -1;
xp->lft.hard_byte_limit = -1;
xp->lft.soft_packet_limit = -1;
xp->lft.hard_packet_limit = -1;
xp->lft.soft_add_expires_seconds = -1;
xp->lft.hard_add_expires_seconds = -1;
xp->lft.soft_use_expires_seconds = -1;
xp->lft.hard_use_expires_seconds = -1;
xp->xfrm_nr = 0;
if (pol->sadb_x_policy_type == IPSEC_POLICY_IPSEC &&
(*dir = parse_ipsecrequests(xp, pol)) < 0)
goto out;
*dir = pol->sadb_x_policy_dir-1;
return xp;
out:
kfree(xp);
return NULL;
}
static int pfkey_sendmsg(struct kiocb *kiocb,
struct socket *sock, struct msghdr *msg, int len,
struct scm_cookie *scm)
......@@ -2246,7 +2270,8 @@ static struct xfrm_mgr pfkeyv2_mgr =
{
.id = "pfkeyv2",
.notify = pfkey_send_notify,
.acquire = pfkey_send_acquire
.acquire = pfkey_send_acquire,
.compile_policy = pfkey_compile_policy,
};
static void __exit ipsec_pfkey_exit(void)
......
......@@ -286,7 +286,6 @@ EXPORT_SYMBOL(dlci_ioctl_hook);
EXPORT_SYMBOL(xfrm_policy_alloc);
EXPORT_SYMBOL(__xfrm_policy_destroy);
EXPORT_SYMBOL(xfrm_policy_lookup);
EXPORT_SYMBOL(xfrm_bundle_create);
EXPORT_SYMBOL(xfrm_lookup);
EXPORT_SYMBOL(__xfrm_policy_check);
EXPORT_SYMBOL(__xfrm_route_forward);
......
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