aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorFlorent Daigniere <nextgens@freenetproject.org>2019-02-24 19:27:21 +0100
committerFlorent Daigniere <nextgens@freenetproject.org>2019-02-26 17:48:30 +0100
commit6bf5a3c48be01eb43bfbb1ab154b8ff8cfd6016e (patch)
treecbc61566bad676fa456f868890635d23a6371127
parentMakefile: make the depmod path configurable (diff)
downloadwireguard-monolithic-historical-fd/ECN-rfc6040.tar.xz
wireguard-monolithic-historical-fd/ECN-rfc6040.zip
net: implement ECN handling, rfc6040 stylefd/ECN-rfc6040
To decide whether we should use the compatibility mode or the normal mode with a peer, we use the handshake messages as a signaling channel. If we receive the expected ECN bits, it most likely means they're running a compatible version. Signed-off-by: Florent Daigniere <nextgens@freenetproject.org>
-rw-r--r--src/messages.h2
-rw-r--r--src/peer.h1
-rw-r--r--src/receive.c75
-rw-r--r--src/send.c8
4 files changed, 77 insertions, 9 deletions
diff --git a/src/messages.h b/src/messages.h
index 3cfd1c5..93dea49 100644
--- a/src/messages.h
+++ b/src/messages.h
@@ -123,6 +123,6 @@ enum message_alignments {
#define DATA_PACKET_HEAD_ROOM \
ALIGN(sizeof(struct message_data) + SKB_HEADER_LEN, 4)
-enum { HANDSHAKE_DSCP = 0x88 /* AF41, plus 00 ECN */ };
+enum { HANDSHAKE_DSCP = 0x8A /* AF41, plus 10 ECN */ };
#endif /* _WG_MESSAGES_H */
diff --git a/src/peer.h b/src/peer.h
index 2e04262..06b5b1e 100644
--- a/src/peer.h
+++ b/src/peer.h
@@ -63,6 +63,7 @@ struct wg_peer {
u64 internal_id;
struct napi_struct napi;
bool is_dead;
+ bool is_ecn_aware;
};
struct wg_peer *wg_peer_create(struct wg_device *wg,
diff --git a/src/receive.c b/src/receive.c
index 51d06d3..8a8a7d1 100644
--- a/src/receive.c
+++ b/src/receive.c
@@ -153,6 +153,7 @@ static void wg_receive_handshake_packet(struct wg_device *wg,
return;
}
wg_socket_set_peer_endpoint_from_skb(peer, skb);
+ peer->is_ecn_aware = INET_ECN_is_capable(PACKET_CB(skb)->ds);
net_dbg_ratelimited("%s: Receiving handshake initiation from peer %llu (%pISpfsc)\n",
wg->dev->name, peer->internal_id,
&peer->endpoint.addr);
@@ -175,6 +176,7 @@ static void wg_receive_handshake_packet(struct wg_device *wg,
return;
}
wg_socket_set_peer_endpoint_from_skb(peer, skb);
+ peer->is_ecn_aware = INET_ECN_is_capable(PACKET_CB(skb)->ds);
net_dbg_ratelimited("%s: Receiving handshake response from peer %llu (%pISpfsc)\n",
wg->dev->name, peer->internal_id,
&peer->endpoint.addr);
@@ -297,6 +299,59 @@ static bool decrypt_packet(struct sk_buff *skb, struct noise_symmetric_key *key,
return true;
}
+/*
+ * RFC 6040 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 the packet should be dropped
+ */
+static inline int ECN_RFC6040_decapsulate(struct sk_buff *skb)
+{
+ __u8 outer = PACKET_CB(skb)->ds;
+ __u8 inner = ip_tunnel_get_dsfield(ip_hdr(skb), skb);
+
+ switch (outer & INET_ECN_MASK) {
+ case INET_ECN_CE:
+ PACKET_CB(skb)->ds =
+ (inner & ~INET_ECN_MASK) |
+ INET_ECN_CE;
+ switch (inner & INET_ECN_MASK) {
+ case INET_ECN_NOT_ECT:
+ return 1;
+ }
+ return 0;
+ case INET_ECN_ECT_1:
+ switch (inner & INET_ECN_MASK) {
+ case INET_ECN_ECT_0:
+ PACKET_CB(skb)->ds =
+ (inner & ~INET_ECN_MASK) |
+ INET_ECN_ECT_1;
+ return 0;
+ }
+ }
+
+ PACKET_CB(skb)->ds = inner;
+
+ return 0;
+}
+
/* This is RFC6479, a replay detection bitmap algorithm that avoids bitshifts */
static bool counter_validate(union noise_counter *counter, u64 their_counter)
{
@@ -394,13 +449,24 @@ static void wg_packet_consume_data_done(struct wg_peer *peer,
len = ntohs(ip_hdr(skb)->tot_len);
if (unlikely(len < sizeof(struct iphdr)))
goto dishonest_packet_size;
- if (INET_ECN_is_ce(PACKET_CB(skb)->ds))
- IP_ECN_set_ce(ip_hdr(skb));
+
+ if (ECN_RFC6040_decapsulate(skb)) {
+ net_dbg_ratelimited("%s: Dropping packet from peer %llu (%pISpfsc) - ECN\n",
+ dev->name, peer->internal_id,
+ &peer->endpoint.addr);
+ ++dev->stats.rx_dropped;
+ goto packet_processed;
+ }
} else if (skb->protocol == htons(ETH_P_IPV6)) {
len = ntohs(ipv6_hdr(skb)->payload_len) +
sizeof(struct ipv6hdr);
- if (INET_ECN_is_ce(PACKET_CB(skb)->ds))
- IP6_ECN_set_ce(skb, ipv6_hdr(skb));
+ if (ECN_RFC6040_decapsulate(skb)) {
+ net_dbg_ratelimited("%s: Dropping packet from peer %llu (%pISpfsc) - ECN\n",
+ dev->name, peer->internal_id,
+ &peer->endpoint.addr);
+ ++dev->stats.rx_dropped;
+ goto packet_processed;
+ }
} else {
goto dishonest_packet_type;
}
@@ -582,6 +648,7 @@ void wg_packet_receive(struct wg_device *wg, struct sk_buff *skb)
goto err;
}
skb_queue_tail(&wg->incoming_handshakes, skb);
+ PACKET_CB(skb)->ds = ip_tunnel_get_dsfield(ip_hdr(skb), skb);
/* Queues up a call to packet_process_queued_handshake_
* packets(skb):
*/
diff --git a/src/send.c b/src/send.c
index b0df5c7..11c4052 100644
--- a/src/send.c
+++ b/src/send.c
@@ -389,10 +389,10 @@ void wg_packet_send_staged_packets(struct wg_peer *peer)
* handshake.
*/
skb_queue_walk(&packets, skb) {
- /* 0 for no outer TOS: no leak. TODO: at some later point, we
- * might consider using flowi->tos as outer instead.
- */
- PACKET_CB(skb)->ds = ip_tunnel_ecn_encap(0, ip_hdr(skb), skb);
+ PACKET_CB(skb)->ds = ip_tunnel_get_dsfield(ip_hdr(skb), skb);
+ if(!peer->is_ecn_aware) {
+ PACKET_CB(skb)->ds &= ~INET_ECN_MASK;
+ }
PACKET_CB(skb)->nonce =
atomic64_inc_return(&key->counter.counter) - 1;
if (unlikely(PACKET_CB(skb)->nonce >= REJECT_AFTER_MESSAGES))