Commit 596aac2c authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller

[IPSEC]: Use per-SA flag to control ECN propagation.

parent 872d1c7a
...@@ -245,6 +245,7 @@ struct sadb_x_nat_t_port { ...@@ -245,6 +245,7 @@ struct sadb_x_nat_t_port {
/* Security Association flags */ /* Security Association flags */
#define SADB_SAFLAGS_PFS 1 #define SADB_SAFLAGS_PFS 1
#define SADB_SAFLAGS_NOECN 0x80000000
/* Security Association states */ /* Security Association states */
#define SADB_SASTATE_LARVAL 0 #define SADB_SASTATE_LARVAL 0
......
...@@ -166,6 +166,8 @@ struct xfrm_usersa_info { ...@@ -166,6 +166,8 @@ struct xfrm_usersa_info {
__u16 family; __u16 family;
__u8 mode; /* 0=transport,1=tunnel */ __u8 mode; /* 0=transport,1=tunnel */
__u8 replay_window; __u8 replay_window;
__u8 flags;
#define XFRM_STATE_NOECN 1
}; };
struct xfrm_usersa_id { struct xfrm_usersa_id {
......
#ifndef _INET_ECN_H_ #ifndef _INET_ECN_H_
#define _INET_ECN_H_ #define _INET_ECN_H_
#include <linux/ip.h>
static inline int INET_ECN_is_ce(__u8 dsfield) static inline int INET_ECN_is_ce(__u8 dsfield)
{ {
return (dsfield&3) == 3; return (dsfield&3) == 3;
...@@ -44,6 +46,11 @@ static inline void IP_ECN_set_ce(struct iphdr *iph) ...@@ -44,6 +46,11 @@ static inline void IP_ECN_set_ce(struct iphdr *iph)
iph->tos |= 1; iph->tos |= 1;
} }
static inline void IP_ECN_clear(struct iphdr *iph)
{
iph->tos &= ~3;
}
struct ipv6hdr; struct ipv6hdr;
static inline void IP6_ECN_set_ce(struct ipv6hdr *iph) static inline void IP6_ECN_set_ce(struct ipv6hdr *iph)
...@@ -51,6 +58,11 @@ static inline void IP6_ECN_set_ce(struct ipv6hdr *iph) ...@@ -51,6 +58,11 @@ static inline void IP6_ECN_set_ce(struct ipv6hdr *iph)
*(u32*)iph |= htonl(1<<20); *(u32*)iph |= htonl(1<<20);
} }
static inline void IP6_ECN_clear(struct ipv6hdr *iph)
{
*(u32*)iph &= ~htonl(3<<20);
}
#define ip6_get_dsfield(iph) ((ntohs(*(u16*)(iph)) >> 4) & 0xFF) #define ip6_get_dsfield(iph) ((ntohs(*(u16*)(iph)) >> 4) & 0xFF)
#endif #endif
...@@ -108,6 +108,7 @@ struct xfrm_state ...@@ -108,6 +108,7 @@ struct xfrm_state
u8 mode; u8 mode;
u8 replay_window; u8 replay_window;
u8 aalgo, ealgo, calgo; u8 aalgo, ealgo, calgo;
u8 flags;
u16 family; u16 family;
xfrm_address_t saddr; xfrm_address_t saddr;
int header_len; int header_len;
......
#include <linux/config.h> #include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
#include <net/inet_ecn.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/xfrm.h> #include <net/xfrm.h>
#include <net/ah.h> #include <net/ah.h>
...@@ -123,6 +124,8 @@ static int ah_output(struct sk_buff *skb) ...@@ -123,6 +124,8 @@ static int ah_output(struct sk_buff *skb)
top_iph->tos = iph->tos; top_iph->tos = iph->tos;
top_iph->ttl = iph->ttl; top_iph->ttl = iph->ttl;
if (x->props.mode) { if (x->props.mode) {
if (x->props.flags & XFRM_STATE_NOECN)
IP_ECN_clear(top_iph);
top_iph->frag_off = iph->frag_off&~htons(IP_MF|IP_OFFSET); top_iph->frag_off = iph->frag_off&~htons(IP_MF|IP_OFFSET);
memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
} else { } else {
......
#include <linux/config.h> #include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
#include <net/inet_ecn.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/xfrm.h> #include <net/xfrm.h>
#include <net/esp.h> #include <net/esp.h>
...@@ -109,6 +110,8 @@ int esp_output(struct sk_buff *skb) ...@@ -109,6 +110,8 @@ int esp_output(struct sk_buff *skb)
top_iph->ihl = 5; top_iph->ihl = 5;
top_iph->version = 4; top_iph->version = 4;
top_iph->tos = iph->tos; /* DS disclosed */ top_iph->tos = iph->tos; /* DS disclosed */
if (x->props.flags & XFRM_STATE_NOECN)
IP_ECN_clear(top_iph);
top_iph->tot_len = htons(skb->len + alen); top_iph->tot_len = htons(skb->len + alen);
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)) if (!(top_iph->frag_off))
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <asm/scatterlist.h> #include <asm/scatterlist.h>
#include <linux/crypto.h> #include <linux/crypto.h>
#include <linux/pfkeyv2.h> #include <linux/pfkeyv2.h>
#include <net/inet_ecn.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/xfrm.h> #include <net/xfrm.h>
#include <net/icmp.h> #include <net/icmp.h>
...@@ -210,6 +211,8 @@ static int ipcomp_output(struct sk_buff *skb) ...@@ -210,6 +211,8 @@ static int ipcomp_output(struct sk_buff *skb)
top_iph = (struct iphdr *)skb_push(skb, sizeof(struct ip_comp_hdr)); top_iph = (struct iphdr *)skb_push(skb, sizeof(struct ip_comp_hdr));
memcpy(top_iph, &tmp_iph, iph->ihl * 4); memcpy(top_iph, &tmp_iph, iph->ihl * 4);
iph = top_iph; iph = top_iph;
if (x->props.mode && (x->props.flags & XFRM_STATE_NOECN))
IP_ECN_clear(iph);
iph->tot_len = htons(skb->len); iph->tot_len = htons(skb->len);
iph->protocol = IPPROTO_COMP; iph->protocol = IPPROTO_COMP;
iph->check = 0; iph->check = 0;
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
*/ */
#include <linux/slab.h> #include <linux/slab.h>
#include <net/inet_ecn.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/xfrm.h> #include <net/xfrm.h>
...@@ -20,6 +21,15 @@ int xfrm4_rcv(struct sk_buff *skb) ...@@ -20,6 +21,15 @@ int xfrm4_rcv(struct sk_buff *skb)
return xfrm4_rcv_encap(skb, 0); return xfrm4_rcv_encap(skb, 0);
} }
static inline void ipip_ecn_decapsulate(struct iphdr *outer_iph, struct sk_buff *skb)
{
struct iphdr *inner_iph = skb->nh.iph;
if (INET_ECN_is_ce(outer_iph->tos) &&
INET_ECN_is_not_ce(inner_iph->tos))
IP_ECN_set_ce(inner_iph);
}
int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type) int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type)
{ {
int err; int err;
...@@ -75,6 +85,8 @@ int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type) ...@@ -75,6 +85,8 @@ int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type)
if (iph->protocol != IPPROTO_IPIP) if (iph->protocol != IPPROTO_IPIP)
goto drop; goto drop;
skb->nh.raw = skb->data; skb->nh.raw = skb->data;
if (!(x->props.flags & XFRM_STATE_NOECN))
ipip_ecn_decapsulate(iph, skb);
iph = skb->nh.iph; iph = skb->nh.iph;
memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
decaps = 1; decaps = 1;
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <linux/config.h> #include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
#include <net/inet_ecn.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/xfrm.h> #include <net/xfrm.h>
#include <net/ah.h> #include <net/ah.h>
...@@ -220,6 +221,8 @@ int ah6_output(struct sk_buff *skb) ...@@ -220,6 +221,8 @@ int ah6_output(struct sk_buff *skb)
skb->nh.ipv6h->flow_lbl[0] = iph->flow_lbl[0]; skb->nh.ipv6h->flow_lbl[0] = iph->flow_lbl[0];
skb->nh.ipv6h->flow_lbl[1] = iph->flow_lbl[1]; skb->nh.ipv6h->flow_lbl[1] = iph->flow_lbl[1];
skb->nh.ipv6h->flow_lbl[2] = iph->flow_lbl[2]; skb->nh.ipv6h->flow_lbl[2] = iph->flow_lbl[2];
if (x->props.flags & XFRM_STATE_NOECN)
IP6_ECN_clear(skb->nh.ipv6h);
} else { } else {
memcpy(skb->nh.ipv6h, iph, hdr_len); memcpy(skb->nh.ipv6h, iph, hdr_len);
skb->nh.raw[nh_offset] = IPPROTO_AH; skb->nh.raw[nh_offset] = IPPROTO_AH;
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <linux/config.h> #include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
#include <net/inet_ecn.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/xfrm.h> #include <net/xfrm.h>
#include <net/esp.h> #include <net/esp.h>
...@@ -121,6 +122,8 @@ int esp6_output(struct sk_buff *skb) ...@@ -121,6 +122,8 @@ int esp6_output(struct sk_buff *skb)
top_iph->flow_lbl[0] = iph->flow_lbl[0]; top_iph->flow_lbl[0] = iph->flow_lbl[0];
top_iph->flow_lbl[1] = iph->flow_lbl[1]; top_iph->flow_lbl[1] = iph->flow_lbl[1];
top_iph->flow_lbl[2] = iph->flow_lbl[2]; top_iph->flow_lbl[2] = iph->flow_lbl[2];
if (x->props.flags & XFRM_STATE_NOECN)
IP6_ECN_clear(top_iph);
top_iph->nexthdr = IPPROTO_ESP; top_iph->nexthdr = IPPROTO_ESP;
top_iph->payload_len = htons(skb->len + alen - sizeof(struct ipv6hdr)); top_iph->payload_len = htons(skb->len + alen - sizeof(struct ipv6hdr));
top_iph->hop_limit = iph->hop_limit; top_iph->hop_limit = iph->hop_limit;
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
*/ */
#include <linux/config.h> #include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
#include <net/inet_ecn.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/xfrm.h> #include <net/xfrm.h>
#include <net/ipcomp.h> #include <net/ipcomp.h>
...@@ -201,6 +202,8 @@ static int ipcomp6_output(struct sk_buff *skb) ...@@ -201,6 +202,8 @@ static int ipcomp6_output(struct sk_buff *skb)
memcpy(top_iph, tmp_iph, hdr_len); memcpy(top_iph, tmp_iph, hdr_len);
kfree(tmp_iph); kfree(tmp_iph);
if (x->props.mode && (x->props.flags & XFRM_STATE_NOECN))
IP6_ECN_clear(top_iph);
top_iph->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); top_iph->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
skb->nh.raw = skb->data; /* top_iph */ skb->nh.raw = skb->data; /* top_iph */
ip6_find_1stfragopt(skb, &prevhdr); ip6_find_1stfragopt(skb, &prevhdr);
......
...@@ -9,12 +9,21 @@ ...@@ -9,12 +9,21 @@
* IPv6 support * IPv6 support
*/ */
#include <net/inet_ecn.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/ipv6.h> #include <net/ipv6.h>
#include <net/xfrm.h> #include <net/xfrm.h>
static kmem_cache_t *secpath_cachep; static kmem_cache_t *secpath_cachep;
static inline void ipip6_ecn_decapsulate(struct ipv6hdr *iph,
struct sk_buff *skb)
{
if (INET_ECN_is_ce(ip6_get_dsfield(iph)) &&
INET_ECN_is_not_ce(ip6_get_dsfield(skb->nh.ipv6h)))
IP6_ECN_set_ce(skb->nh.ipv6h);
}
int xfrm6_rcv(struct sk_buff **pskb, unsigned int *nhoffp) int xfrm6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
{ {
struct sk_buff *skb = *pskb; struct sk_buff *skb = *pskb;
...@@ -71,6 +80,8 @@ int xfrm6_rcv(struct sk_buff **pskb, unsigned int *nhoffp) ...@@ -71,6 +80,8 @@ int xfrm6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
if (nexthdr != IPPROTO_IPV6) if (nexthdr != IPPROTO_IPV6)
goto drop; goto drop;
skb->nh.raw = skb->data; skb->nh.raw = skb->data;
if (!(x->props.flags & XFRM_STATE_NOECN))
ipip6_ecn_decapsulate(iph, skb);
iph = skb->nh.ipv6h; iph = skb->nh.ipv6h;
decaps = 1; decaps = 1;
break; break;
......
...@@ -681,6 +681,8 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys, ...@@ -681,6 +681,8 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys,
} }
sa->sadb_sa_flags = 0; sa->sadb_sa_flags = 0;
if (x->props.flags & XFRM_STATE_NOECN)
sa->sadb_sa_flags |= SADB_SAFLAGS_NOECN;
/* hard time */ /* hard time */
if (hsc & 2) { if (hsc & 2) {
...@@ -957,6 +959,8 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr, ...@@ -957,6 +959,8 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr,
x->id.proto = proto; x->id.proto = proto;
x->id.spi = sa->sadb_sa_spi; x->id.spi = sa->sadb_sa_spi;
x->props.replay_window = sa->sadb_sa_replay; x->props.replay_window = sa->sadb_sa_replay;
if (sa->sadb_sa_flags & SADB_SAFLAGS_NOECN)
x->props.flags |= XFRM_STATE_NOECN;
lifetime = (struct sadb_lifetime*) ext_hdrs[SADB_EXT_LIFETIME_HARD-1]; lifetime = (struct sadb_lifetime*) ext_hdrs[SADB_EXT_LIFETIME_HARD-1];
if (lifetime != NULL) { if (lifetime != NULL) {
......
...@@ -201,6 +201,7 @@ static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info * ...@@ -201,6 +201,7 @@ static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *
x->props.reqid = p->reqid; x->props.reqid = p->reqid;
x->props.family = p->family; x->props.family = p->family;
x->props.saddr = p->saddr; x->props.saddr = p->saddr;
x->props.flags = p->flags;
} }
static struct xfrm_state *xfrm_state_construct(struct xfrm_usersa_info *p, static struct xfrm_state *xfrm_state_construct(struct xfrm_usersa_info *p,
...@@ -305,6 +306,7 @@ static void copy_to_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p) ...@@ -305,6 +306,7 @@ static void copy_to_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p)
p->replay_window = x->props.replay_window; p->replay_window = x->props.replay_window;
p->reqid = x->props.reqid; p->reqid = x->props.reqid;
p->family = x->props.family; p->family = x->props.family;
p->flags = x->props.flags;
p->seq = x->km.seq; p->seq = x->km.seq;
} }
......
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