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