diff options
| -rw-r--r-- | include/net/inet_ecn.h | 76 | ||||
| -rw-r--r-- | net/ipv4/ip_gre.c | 38 | ||||
| -rw-r--r-- | net/ipv4/ipip.c | 42 | ||||
| -rw-r--r-- | net/ipv6/ip6_gre.c | 54 |
4 files changed, 147 insertions, 63 deletions
diff --git a/include/net/inet_ecn.h b/include/net/inet_ecn.h index 2fa14691869c..aab73757bc4d 100644 --- a/include/net/inet_ecn.h +++ b/include/net/inet_ecn.h | |||
| @@ -15,6 +15,8 @@ enum { | |||
| 15 | INET_ECN_MASK = 3, | 15 | INET_ECN_MASK = 3, |
| 16 | }; | 16 | }; |
| 17 | 17 | ||
| 18 | extern int sysctl_tunnel_ecn_log; | ||
| 19 | |||
| 18 | static inline int INET_ECN_is_ce(__u8 dsfield) | 20 | static inline int INET_ECN_is_ce(__u8 dsfield) |
| 19 | { | 21 | { |
| 20 | return (dsfield & INET_ECN_MASK) == INET_ECN_CE; | 22 | return (dsfield & INET_ECN_MASK) == INET_ECN_CE; |
| @@ -145,4 +147,78 @@ static inline int INET_ECN_set_ce(struct sk_buff *skb) | |||
| 145 | return 0; | 147 | return 0; |
| 146 | } | 148 | } |
| 147 | 149 | ||
| 150 | /* | ||
| 151 | * RFC 6080 4.2 | ||
| 152 | * To decapsulate the inner header at the tunnel egress, a compliant | ||
| 153 | * tunnel egress MUST set the outgoing ECN field to the codepoint at the | ||
| 154 | * intersection of the appropriate arriving inner header (row) and outer | ||
| 155 | * header (column) in Figure 4 | ||
| 156 | * | ||
| 157 | * +---------+------------------------------------------------+ | ||
| 158 | * |Arriving | Arriving Outer Header | | ||
| 159 | * | Inner +---------+------------+------------+------------+ | ||
| 160 | * | Header | Not-ECT | ECT(0) | ECT(1) | CE | | ||
| 161 | * +---------+---------+------------+------------+------------+ | ||
| 162 | * | Not-ECT | Not-ECT |Not-ECT(!!!)|Not-ECT(!!!)| <drop>(!!!)| | ||
| 163 | * | ECT(0) | ECT(0) | ECT(0) | ECT(1) | CE | | ||
| 164 | * | ECT(1) | ECT(1) | ECT(1) (!) | ECT(1) | CE | | ||
| 165 | * | CE | CE | CE | CE(!!!)| CE | | ||
| 166 | * +---------+---------+------------+------------+------------+ | ||
| 167 | * | ||
| 168 | * Figure 4: New IP in IP Decapsulation Behaviour | ||
| 169 | * | ||
| 170 | * returns 0 on success | ||
| 171 | * 1 if something is broken and should be logged (!!! above) | ||
| 172 | * 2 if packet should be dropped | ||
| 173 | */ | ||
| 174 | static inline int INET_ECN_decapsulate(struct sk_buff *skb, | ||
| 175 | __u8 outer, __u8 inner) | ||
| 176 | { | ||
| 177 | if (INET_ECN_is_not_ect(inner)) { | ||
| 178 | switch (outer & INET_ECN_MASK) { | ||
| 179 | case INET_ECN_NOT_ECT: | ||
| 180 | return 0; | ||
| 181 | case INET_ECN_ECT_0: | ||
| 182 | case INET_ECN_ECT_1: | ||
| 183 | return 1; | ||
| 184 | case INET_ECN_CE: | ||
| 185 | return 2; | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | if (INET_ECN_is_ce(outer)) | ||
| 190 | INET_ECN_set_ce(skb); | ||
| 191 | |||
| 192 | return 0; | ||
| 193 | } | ||
| 194 | |||
| 195 | static inline int IP_ECN_decapsulate(const struct iphdr *oiph, | ||
| 196 | struct sk_buff *skb) | ||
| 197 | { | ||
| 198 | __u8 inner; | ||
| 199 | |||
| 200 | if (skb->protocol == htons(ETH_P_IP)) | ||
| 201 | inner = ip_hdr(skb)->tos; | ||
| 202 | else if (skb->protocol == htons(ETH_P_IPV6)) | ||
| 203 | inner = ipv6_get_dsfield(ipv6_hdr(skb)); | ||
| 204 | else | ||
| 205 | return 0; | ||
| 206 | |||
| 207 | return INET_ECN_decapsulate(skb, oiph->tos, inner); | ||
| 208 | } | ||
| 209 | |||
| 210 | static inline int IP6_ECN_decapsulate(const struct ipv6hdr *oipv6h, | ||
| 211 | struct sk_buff *skb) | ||
| 212 | { | ||
| 213 | __u8 inner; | ||
| 214 | |||
| 215 | if (skb->protocol == htons(ETH_P_IP)) | ||
| 216 | inner = ip_hdr(skb)->tos; | ||
| 217 | else if (skb->protocol == htons(ETH_P_IPV6)) | ||
| 218 | inner = ipv6_get_dsfield(ipv6_hdr(skb)); | ||
| 219 | else | ||
| 220 | return 0; | ||
| 221 | |||
| 222 | return INET_ECN_decapsulate(skb, ipv6_get_dsfield(oipv6h), inner); | ||
| 223 | } | ||
| 148 | #endif | 224 | #endif |
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 1c012cb2cb94..ef0b861ce044 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c | |||
| @@ -120,6 +120,10 @@ | |||
| 120 | Alexey Kuznetsov. | 120 | Alexey Kuznetsov. |
| 121 | */ | 121 | */ |
| 122 | 122 | ||
| 123 | static bool log_ecn_error = true; | ||
| 124 | module_param(log_ecn_error, bool, 0644); | ||
| 125 | MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); | ||
| 126 | |||
| 123 | static struct rtnl_link_ops ipgre_link_ops __read_mostly; | 127 | static struct rtnl_link_ops ipgre_link_ops __read_mostly; |
| 124 | static int ipgre_tunnel_init(struct net_device *dev); | 128 | static int ipgre_tunnel_init(struct net_device *dev); |
| 125 | static void ipgre_tunnel_setup(struct net_device *dev); | 129 | static void ipgre_tunnel_setup(struct net_device *dev); |
| @@ -204,7 +208,9 @@ static struct rtnl_link_stats64 *ipgre_get_stats64(struct net_device *dev, | |||
| 204 | tot->rx_crc_errors = dev->stats.rx_crc_errors; | 208 | tot->rx_crc_errors = dev->stats.rx_crc_errors; |
| 205 | tot->rx_fifo_errors = dev->stats.rx_fifo_errors; | 209 | tot->rx_fifo_errors = dev->stats.rx_fifo_errors; |
| 206 | tot->rx_length_errors = dev->stats.rx_length_errors; | 210 | tot->rx_length_errors = dev->stats.rx_length_errors; |
| 211 | tot->rx_frame_errors = dev->stats.rx_frame_errors; | ||
| 207 | tot->rx_errors = dev->stats.rx_errors; | 212 | tot->rx_errors = dev->stats.rx_errors; |
| 213 | |||
| 208 | tot->tx_fifo_errors = dev->stats.tx_fifo_errors; | 214 | tot->tx_fifo_errors = dev->stats.tx_fifo_errors; |
| 209 | tot->tx_carrier_errors = dev->stats.tx_carrier_errors; | 215 | tot->tx_carrier_errors = dev->stats.tx_carrier_errors; |
| 210 | tot->tx_dropped = dev->stats.tx_dropped; | 216 | tot->tx_dropped = dev->stats.tx_dropped; |
| @@ -587,17 +593,6 @@ static void ipgre_err(struct sk_buff *skb, u32 info) | |||
| 587 | t->err_time = jiffies; | 593 | t->err_time = jiffies; |
| 588 | } | 594 | } |
| 589 | 595 | ||
| 590 | static inline void ipgre_ecn_decapsulate(const struct iphdr *iph, struct sk_buff *skb) | ||
| 591 | { | ||
| 592 | if (INET_ECN_is_ce(iph->tos)) { | ||
| 593 | if (skb->protocol == htons(ETH_P_IP)) { | ||
| 594 | IP_ECN_set_ce(ip_hdr(skb)); | ||
| 595 | } else if (skb->protocol == htons(ETH_P_IPV6)) { | ||
| 596 | IP6_ECN_set_ce(ipv6_hdr(skb)); | ||
| 597 | } | ||
| 598 | } | ||
| 599 | } | ||
| 600 | |||
| 601 | static inline u8 | 596 | static inline u8 |
| 602 | ipgre_ecn_encapsulate(u8 tos, const struct iphdr *old_iph, struct sk_buff *skb) | 597 | ipgre_ecn_encapsulate(u8 tos, const struct iphdr *old_iph, struct sk_buff *skb) |
| 603 | { | 598 | { |
| @@ -620,6 +615,7 @@ static int ipgre_rcv(struct sk_buff *skb) | |||
| 620 | struct ip_tunnel *tunnel; | 615 | struct ip_tunnel *tunnel; |
| 621 | int offset = 4; | 616 | int offset = 4; |
| 622 | __be16 gre_proto; | 617 | __be16 gre_proto; |
| 618 | int err; | ||
| 623 | 619 | ||
| 624 | if (!pskb_may_pull(skb, 16)) | 620 | if (!pskb_may_pull(skb, 16)) |
| 625 | goto drop; | 621 | goto drop; |
| @@ -723,17 +719,27 @@ static int ipgre_rcv(struct sk_buff *skb) | |||
| 723 | skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN); | 719 | skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN); |
| 724 | } | 720 | } |
| 725 | 721 | ||
| 722 | __skb_tunnel_rx(skb, tunnel->dev); | ||
| 723 | |||
| 724 | skb_reset_network_header(skb); | ||
| 725 | err = IP_ECN_decapsulate(iph, skb); | ||
| 726 | if (unlikely(err)) { | ||
| 727 | if (log_ecn_error) | ||
| 728 | net_info_ratelimited("non-ECT from %pI4 with TOS=%#x\n", | ||
| 729 | &iph->saddr, iph->tos); | ||
| 730 | if (err > 1) { | ||
| 731 | ++tunnel->dev->stats.rx_frame_errors; | ||
| 732 | ++tunnel->dev->stats.rx_errors; | ||
| 733 | goto drop; | ||
| 734 | } | ||
| 735 | } | ||
| 736 | |||
| 726 | tstats = this_cpu_ptr(tunnel->dev->tstats); | 737 | tstats = this_cpu_ptr(tunnel->dev->tstats); |
| 727 | u64_stats_update_begin(&tstats->syncp); | 738 | u64_stats_update_begin(&tstats->syncp); |
| 728 | tstats->rx_packets++; | 739 | tstats->rx_packets++; |
| 729 | tstats->rx_bytes += skb->len; | 740 | tstats->rx_bytes += skb->len; |
| 730 | u64_stats_update_end(&tstats->syncp); | 741 | u64_stats_update_end(&tstats->syncp); |
| 731 | 742 | ||
| 732 | __skb_tunnel_rx(skb, tunnel->dev); | ||
| 733 | |||
| 734 | skb_reset_network_header(skb); | ||
| 735 | ipgre_ecn_decapsulate(iph, skb); | ||
| 736 | |||
| 737 | netif_rx(skb); | 743 | netif_rx(skb); |
| 738 | 744 | ||
| 739 | return 0; | 745 | return 0; |
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 618bde867ac1..e15b45297c09 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c | |||
| @@ -120,6 +120,10 @@ | |||
| 120 | #define HASH_SIZE 16 | 120 | #define HASH_SIZE 16 |
| 121 | #define HASH(addr) (((__force u32)addr^((__force u32)addr>>4))&0xF) | 121 | #define HASH(addr) (((__force u32)addr^((__force u32)addr>>4))&0xF) |
| 122 | 122 | ||
| 123 | static bool log_ecn_error = true; | ||
| 124 | module_param(log_ecn_error, bool, 0644); | ||
| 125 | MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); | ||
