2005-04-17 02:20:36 +04:00
# ifndef _INET_ECN_H_
# define _INET_ECN_H_
# include <linux/ip.h>
2005-11-05 23:14:04 +03:00
# include <linux/skbuff.h>
2005-12-27 07:43:12 +03:00
# include <net/inet_sock.h>
2005-04-17 02:20:36 +04:00
# include <net/dsfield.h>
enum {
INET_ECN_NOT_ECT = 0 ,
INET_ECN_ECT_1 = 1 ,
INET_ECN_ECT_0 = 2 ,
INET_ECN_CE = 3 ,
INET_ECN_MASK = 3 ,
} ;
2012-09-25 15:02:48 +04:00
extern int sysctl_tunnel_ecn_log ;
2005-04-17 02:20:36 +04:00
static inline int INET_ECN_is_ce ( __u8 dsfield )
{
return ( dsfield & INET_ECN_MASK ) = = INET_ECN_CE ;
}
static inline int INET_ECN_is_not_ect ( __u8 dsfield )
{
return ( dsfield & INET_ECN_MASK ) = = INET_ECN_NOT_ECT ;
}
static inline int INET_ECN_is_capable ( __u8 dsfield )
{
2010-09-23 00:43:57 +04:00
return dsfield & INET_ECN_ECT_0 ;
2005-04-17 02:20:36 +04:00
}
2011-10-22 09:25:23 +04:00
/*
* RFC 3168 9.1 .1
* The full - functionality option for ECN encapsulation is to copy the
* ECN codepoint of the inside header to the outside header on
* encapsulation if the inside header is not - ECT or ECT , and to set the
* ECN codepoint of the outside header to ECT ( 0 ) if the ECN codepoint of
* the inside header is CE .
*/
2005-04-17 02:20:36 +04:00
static inline __u8 INET_ECN_encapsulate ( __u8 outer , __u8 inner )
{
outer & = ~ INET_ECN_MASK ;
outer | = ! INET_ECN_is_ce ( inner ) ? ( inner & INET_ECN_MASK ) :
INET_ECN_ECT_0 ;
return outer ;
}
2011-05-07 03:44:46 +04:00
static inline void INET_ECN_xmit ( struct sock * sk )
{
inet_sk ( sk ) - > tos | = INET_ECN_ECT_0 ;
if ( inet6_sk ( sk ) ! = NULL )
inet6_sk ( sk ) - > tclass | = INET_ECN_ECT_0 ;
}
static inline void INET_ECN_dontxmit ( struct sock * sk )
{
inet_sk ( sk ) - > tos & = ~ INET_ECN_MASK ;
if ( inet6_sk ( sk ) ! = NULL )
inet6_sk ( sk ) - > tclass & = ~ INET_ECN_MASK ;
}
2005-04-17 02:20:36 +04:00
# define IP6_ECN_flow_init(label) do { \
( label ) & = ~ htonl ( INET_ECN_MASK < < 20 ) ; \
} while ( 0 )
# define IP6_ECN_flow_xmit(sk, label) do { \
2008-04-14 10:40:51 +04:00
if ( INET_ECN_is_capable ( inet6_sk ( sk ) - > tclass ) ) \
2006-11-03 11:55:35 +03:00
( label ) | = htonl ( INET_ECN_ECT_0 < < 20 ) ; \
2005-04-17 02:20:36 +04:00
} while ( 0 )
2005-11-05 23:14:04 +03:00
static inline int IP_ECN_set_ce ( struct iphdr * iph )
2005-04-17 02:20:36 +04:00
{
2006-11-15 08:42:26 +03:00
u32 check = ( __force u32 ) iph - > check ;
2005-04-17 02:20:36 +04:00
u32 ecn = ( iph - > tos + 1 ) & INET_ECN_MASK ;
/*
* After the last operation we have ( in binary ) :
* INET_ECN_NOT_ECT = > 01
* INET_ECN_ECT_1 = > 10
* INET_ECN_ECT_0 = > 11
* INET_ECN_CE = > 00
*/
if ( ! ( ecn & 2 ) )
2005-11-05 23:14:04 +03:00
return ! ecn ;
2005-04-17 02:20:36 +04:00
/*
* The following gives us :
* INET_ECN_ECT_1 = > check + = htons ( 0xFFFD )
* INET_ECN_ECT_0 = > check + = htons ( 0xFFFE )
*/
2006-11-15 08:42:26 +03:00
check + = ( __force u16 ) htons ( 0xFFFB ) + ( __force u16 ) htons ( ecn ) ;
2005-04-17 02:20:36 +04:00
2006-11-15 08:42:26 +03:00
iph - > check = ( __force __sum16 ) ( check + ( check > = 0xFFFF ) ) ;
2005-04-17 02:20:36 +04:00
iph - > tos | = INET_ECN_CE ;
2005-11-05 23:14:04 +03:00
return 1 ;
2005-04-17 02:20:36 +04:00
}
static inline void IP_ECN_clear ( struct iphdr * iph )
{
iph - > tos & = ~ INET_ECN_MASK ;
}
2007-11-14 08:40:13 +03:00
static inline void ipv4_copy_dscp ( unsigned int dscp , struct iphdr * inner )
2005-04-17 02:20:36 +04:00
{
2007-11-14 08:40:13 +03:00
dscp & = ~ INET_ECN_MASK ;
2005-04-17 02:20:36 +04:00
ipv4_change_dsfield ( inner , INET_ECN_MASK , dscp ) ;
}
struct ipv6hdr ;
2005-11-05 23:14:04 +03:00
static inline int IP6_ECN_set_ce ( struct ipv6hdr * iph )
2005-04-17 02:20:36 +04:00
{
if ( INET_ECN_is_not_ect ( ipv6_get_dsfield ( iph ) ) )
2005-11-05 23:14:04 +03:00
return 0 ;
2006-11-08 11:24:47 +03:00
* ( __be32 * ) iph | = htonl ( INET_ECN_CE < < 20 ) ;
2005-11-05 23:14:04 +03:00
return 1 ;
2005-04-17 02:20:36 +04:00
}
static inline void IP6_ECN_clear ( struct ipv6hdr * iph )
{
2006-11-08 11:24:47 +03:00
* ( __be32 * ) iph & = ~ htonl ( INET_ECN_MASK < < 20 ) ;
2005-04-17 02:20:36 +04:00
}
2007-11-14 08:40:13 +03:00
static inline void ipv6_copy_dscp ( unsigned int dscp , struct ipv6hdr * inner )
2005-04-17 02:20:36 +04:00
{
2007-11-14 08:40:13 +03:00
dscp & = ~ INET_ECN_MASK ;
2005-04-17 02:20:36 +04:00
ipv6_change_dsfield ( inner , INET_ECN_MASK , dscp ) ;
}
2005-11-05 23:14:04 +03:00
static inline int INET_ECN_set_ce ( struct sk_buff * skb )
{
switch ( skb - > protocol ) {
2009-02-15 09:58:35 +03:00
case cpu_to_be16 ( ETH_P_IP ) :
2013-05-29 00:34:25 +04:00
if ( skb_network_header ( skb ) + sizeof ( struct iphdr ) < =
skb_tail_pointer ( skb ) )
2007-04-21 09:47:35 +04:00
return IP_ECN_set_ce ( ip_hdr ( skb ) ) ;
2005-11-05 23:14:04 +03:00
break ;
2009-02-15 09:58:35 +03:00
case cpu_to_be16 ( ETH_P_IPV6 ) :
2013-05-29 00:34:25 +04:00
if ( skb_network_header ( skb ) + sizeof ( struct ipv6hdr ) < =
skb_tail_pointer ( skb ) )
2007-04-26 04:54:47 +04:00
return IP6_ECN_set_ce ( ipv6_hdr ( skb ) ) ;
2005-11-05 23:14:04 +03:00
break ;
}
return 0 ;
}
2012-09-25 15:02:48 +04:00
/*
* RFC 6080 4.2
* To decapsulate the inner header at the tunnel egress , a compliant
* tunnel egress MUST set the outgoing ECN field to the codepoint at the
* intersection of the appropriate arriving inner header ( row ) and outer
* header ( column ) in Figure 4
*
* + - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
* | Arriving | Arriving Outer Header |
* | Inner + - - - - - - - - - + - - - - - - - - - - - - + - - - - - - - - - - - - + - - - - - - - - - - - - +
* | Header | Not - ECT | ECT ( 0 ) | ECT ( 1 ) | CE |
* + - - - - - - - - - + - - - - - - - - - + - - - - - - - - - - - - + - - - - - - - - - - - - + - - - - - - - - - - - - +
* | Not - ECT | Not - ECT | Not - ECT ( ! ! ! ) | Not - ECT ( ! ! ! ) | < drop > ( ! ! ! ) |
* | ECT ( 0 ) | ECT ( 0 ) | ECT ( 0 ) | ECT ( 1 ) | CE |
* | ECT ( 1 ) | ECT ( 1 ) | ECT ( 1 ) ( ! ) | ECT ( 1 ) | CE |
* | CE | CE | CE | CE ( ! ! ! ) | CE |
* + - - - - - - - - - + - - - - - - - - - + - - - - - - - - - - - - + - - - - - - - - - - - - + - - - - - - - - - - - - +
*
* Figure 4 : New IP in IP Decapsulation Behaviour
*
* returns 0 on success
* 1 if something is broken and should be logged ( ! ! ! above )
* 2 if packet should be dropped
*/
static inline int INET_ECN_decapsulate ( struct sk_buff * skb ,
__u8 outer , __u8 inner )
{
if ( INET_ECN_is_not_ect ( inner ) ) {
switch ( outer & INET_ECN_MASK ) {
case INET_ECN_NOT_ECT :
return 0 ;
case INET_ECN_ECT_0 :
case INET_ECN_ECT_1 :
return 1 ;
case INET_ECN_CE :
return 2 ;
}
}
if ( INET_ECN_is_ce ( outer ) )
INET_ECN_set_ce ( skb ) ;
return 0 ;
}
static inline int IP_ECN_decapsulate ( const struct iphdr * oiph ,
struct sk_buff * skb )
{
__u8 inner ;
if ( skb - > protocol = = htons ( ETH_P_IP ) )
inner = ip_hdr ( skb ) - > tos ;
else if ( skb - > protocol = = htons ( ETH_P_IPV6 ) )
inner = ipv6_get_dsfield ( ipv6_hdr ( skb ) ) ;
else
return 0 ;
return INET_ECN_decapsulate ( skb , oiph - > tos , inner ) ;
}
static inline int IP6_ECN_decapsulate ( const struct ipv6hdr * oipv6h ,
struct sk_buff * skb )
{
__u8 inner ;
if ( skb - > protocol = = htons ( ETH_P_IP ) )
inner = ip_hdr ( skb ) - > tos ;
else if ( skb - > protocol = = htons ( ETH_P_IPV6 ) )
inner = ipv6_get_dsfield ( ipv6_hdr ( skb ) ) ;
else
return 0 ;
return INET_ECN_decapsulate ( skb , ipv6_get_dsfield ( oipv6h ) , inner ) ;
}
2005-04-17 02:20:36 +04:00
# endif