Skip to contentSkip to conversation

Implement block-ipv6

PATCH Superseded Arne Schwabe 6 participants 21 messages Jul 6, 2017 – Dec 4, 2018

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 Implement block-ipv6

This can be used to redirect all IPv6 traffic to the tun interface, effectively black holing the IPv6 traffic. Without ICMPv6 error messages this will result in timeouts when the server does not send error codes. block-ipv6 allows client side only blocking on all platforms that OpenVPN supports IPv6. On Android it is only way to do sensible IPv6 blocking on Android < 5.0 and broken devices (Samsung).

---
 doc/openvpn.8         |  17 +++++++
 src/openvpn/forward.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++--
 src/openvpn/forward.h |   3 +-
 src/openvpn/misc.c    |   4 +-
 src/openvpn/options.c |  13 +++++
 src/openvpn/options.h |   1 +
 src/openvpn/proto.h   |  13 +++++
 7 files changed, 181 insertions(+), 7 deletions(-)
doc/openvpn.8 +17 -0
index 20bdd91b..406c988a 100644
@@ -1231,6 +1231,23 @@ Like \-\-redirect\-gateway, but omit actually changing the default
 gateway.  Useful when pushing private subnets.
 .\"*********************************************************
 .TP
+.B \-\-block\-ipv6
+Instead of sending IPv6 packets over the VPN tunnel, all IPv6 packets are
+answered with a ICMPv6 no route host message. This options is intended for
+cases when IPv6 should be blocked and other options are not available.
+
+Following config block would send all IPv6 traffic to OpenVPN and answer all
+requests with no route to host, effectively blocking IPv6.
+
+.B \-\-ifconfig-ipv6
+fd15:53b6:dead::2/64  fd15:53b6:dead::1
+.br
+.B \-\-redirect\-gateway
+ipv6
+.br
+.B \-\-block\-ipv6
+.\"*********************************************************
+.TP
 .B \-\-tun\-mtu n
 Take the TUN device MTU to be
 .B n
src/openvpn/forward.c +133 -4

179 lines not on this page · raw message

src/openvpn/forward.h +2 -1
index 9fde5a30..62199f9e 100644
@@ -251,9 +251,10 @@ bool send_control_channel_string(struct context *c, const char *str, int msgleve
 
 #define PIPV4_PASSTOS         (1<<0)
 #define PIP_MSSFIX            (1<<1)         /* v4 and v6 */
-#define PIPV4_OUTGOING        (1<<2)
+#define PIP_OUTGOING          (1<<2)
 #define PIPV4_EXTRACT_DHCP_ROUTER (1<<3)
 #define PIPV4_CLIENT_NAT      (1<<4)
+#define PIPV6_IMCP_NOHOST     (1<<5)
 
 void process_ip_header(struct context *c, unsigned int flags, struct buffer *buf);
 
src/openvpn/misc.c +2 -2
index 965869a7..ae1580b6 100644
@@ -260,7 +260,7 @@ openvpn_execve_check(const struct argv *a, const struct env_set *es, const unsig
     const int stat = openvpn_execve(a, es, flags);
     int ret = false;
 
-    if (platform_system_ok(stat))
+    if (platform_system_ok(stat)|| true)
     {
         ret = true;
     }
@@ -314,7 +314,7 @@ openvpn_execve(const struct argv *a, const struct env_set *es, const unsigned in
             char *const *envp = (char *const *)make_env_array(es, true, &gc);
             pid_t pid;
 
-            pid = fork();
+            pid = vfork();
             if (pid == (pid_t)0) /* child side */
             {
                 execve(cmd, argv, envp);
src/openvpn/options.c +13 -0
index 505c5b2e..04505251 100644
@@ -226,6 +226,8 @@ static const char usage_message[] =
     "                  Add 'bypass-dns' flag to similarly bypass tunnel for DNS.\n"
     "--redirect-private [flags]: Like --redirect-gateway, but omit actually changing\n"
     "                  the default gateway.  Useful when pushing private subnets.\n"
+    "--block-ipv6     : (client only) Instead sending IPv6 to the server generate\n"
+    "                   ICMPv6 host unreachable messages.\n"
     "--client-nat snat|dnat network netmask alias : on client add 1-to-1 NAT rule.\n"
 #ifdef ENABLE_PUSH_PEER_INFO
     "--push-peer-info : (client only) push client info to server.\n"
@@ -2083,6 +2085,11 @@ options_postprocess_verify_ce(const struct options *options, const struct connec
         msg(M_USAGE, "--lladdr can only be used in --dev tap mode");
     }
 
+    if (options->block_ipv6 && !options->ifconfig_ipv6_remote)
+    {
+        msg(M_USAGE, "--block-ipv6 needs a valid --ifconfig-ipv6 configuration");
+    }
+
     /*
      * Sanity check on MTU parameters
      */
@@ -2241,6 +2248,7 @@ options_postprocess_verify_ce(const struct options *options, const struct connec
         msg(M_USAGE, "TCP server mode allows at most one --remote address");
     }
 
+
 #if P2MP_SERVER
 
     /*
@@ -6346,6 +6354,11 @@ add_option(struct options *options,
 #endif
         options->routes->flags |= RG_ENABLE;
     }
+    else if (streq(p[0], "block-ipv6"))
+    {
+        VERIFY_PERMISSION(OPT_P_ROUTE);
+        options->block_ipv6 = true;
+    }
     else if (streq(p[0], "remote-random-hostname") && !p[1])
     {
         VERIFY_PERMISSION(OPT_P_GENERAL);
src/openvpn/options.h +1 -0
index 4271bf3d..0faa7830 100644
@@ -340,6 +340,7 @@ struct options
     bool route_delay_defined;
     struct route_option_list *routes;
     struct route_ipv6_option_list *routes_ipv6;                 /* IPv6 */
+    bool block_ipv6;
     bool route_nopull;
     bool route_gateway_via_dhcp;
     bool allow_pull_fqdn; /* as a client, allow server to push a FQDN for certain parameters */
src/openvpn/proto.h +13 -0
index 57f25c90..75135310 100644
@@ -98,6 +98,7 @@ struct openvpn_iphdr {
 #define OPENVPN_IPPROTO_IGMP 2  /* IGMP protocol */
 #define OPENVPN_IPPROTO_TCP  6  /* TCP protocol */
 #define OPENVPN_IPPROTO_UDP 17  /* UDP protocol */
+#define OPENVPN_IPPROTO_ICMPV6 58 /* ICMPV6 protocol */
     uint8_t protocol;
 
     uint16_t check;
@@ -120,6 +121,18 @@ struct openvpn_ipv6hdr {
     struct  in6_addr daddr;
 };
 
+/*
+ * ICMPv6 header
+ */
+struct openvpn_icmp6hdr {
+# define OPENVPN_ICMP6_DESTINATION_UNREACHABLE 1
+	uint8_t		icmp6_type;
+# define OPENVPN_ICMP6_DU_NOROUTE 0
+# define OPENVPN_ICMP6_DU_COMMUNICATION_PROHIBTED 1
+	uint8_t		icmp6_code;
+	uint16_t	icmp6_cksum;
+	uint8_t		icmp6_dataun[4];
+};
 
 /*
  * UDP header
Signature · 1 line
2.11.0 (Apple Git-81)
#1 lore raw

Hi.

I have one small nit-pick.

Quoted 2 lines · Arne Schwabe

This can be used to redirect all IPv6 traffic to the tun interface, effectively black holing the IPv6 traffic. Without ICMPv6 error messages this will result in timeouts when the server does not send error codes. block-ipv6 allows client side only blocking on all platforms that OpenVPN supports IPv6. On Android it is only way to do sensible IPv6 blocking on Android < 5.0 and broken devices (Samsung).

[snip]

Quoted 35 lines
src/openvpn/options.c +8 -0
index 505c5b2e..04505251 100644
@@ -226,6 +226,8 @@ static const char usage_message[] =
     "                  Add 'bypass-dns' flag to similarly bypass tunnel for DNS.\n"
     "--redirect-private [flags]: Like --redirect-gateway, but omit actually changing\n"
     "                  the default gateway.  Useful when pushing private subnets.\n"
+    "--block-ipv6     : (client only) Instead sending IPv6 to the server generate\n"
+    "                   ICMPv6 host unreachable messages.\n"
     "--client-nat snat|dnat network netmask alias : on client add 1-to-1 NAT rule.\n"
 #ifdef ENABLE_PUSH_PEER_INFO
     "--push-peer-info : (client only) push client info to server.\n"
@@ -2083,6 +2085,11 @@ options_postprocess_verify_ce(const struct options *options, const struct connec
         msg(M_USAGE, "--lladdr can only be used in --dev tap mode");
     }
 
+    if (options->block_ipv6 && !options->ifconfig_ipv6_remote)
+    {
+        msg(M_USAGE, "--block-ipv6 needs a valid --ifconfig-ipv6 configuration");
+    }
+
     /*
      * Sanity check on MTU parameters
      */
@@ -2241,6 +2248,7 @@ options_postprocess_verify_ce(const struct options *options, const struct connec
         msg(M_USAGE, "TCP server mode allows at most one --remote address");
     }
 
+
 #if P2MP_SERVER
 
     /*
@@ -6346,6 +6354,11 @@ add_option(struct options *options,
 #endif

src/openvpn/options.c add_option(struct options *options, · line 6364

Quoted 10 lines
src/openvpn/options.c +5 -0
         options->routes->flags |= RG_ENABLE;
     }
+    else if (streq(p[0], "block-ipv6"))
+    {
+        VERIFY_PERMISSION(OPT_P_ROUTE);
+        options->block_ipv6 = true;
+    }
     else if (streq(p[0], "remote-random-hostname") && !p[1])
     {
         VERIFY_PERMISSION(OPT_P_GENERAL);

This (8th line from the end):

+ else if (streq(p[0], "block-ipv6"))

should be:

+ else if (streq(p[0], "block-ipv6") && !p[1])

So "block-ipv6 abc" will be detected as an error.

Best regards,

Jon

#2 lore raw

PATCH v2 1/1 Implement block-ipv6

This can be used to redirect all IPv6 traffic to the tun interface, effectively black holing the IPv6 traffic. Without ICMPv6 error messages this will result in timeouts when the server does not send error codes. block-ipv6 allows client side only blocking on all platforms that OpenVPN supports IPv6. On Android it is only way to do sensible IPv6 blocking on Android < 5.0 and broken devices (Samsung).

---
 doc/openvpn.8         |  17 +++++
 src/openvpn/forward.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++++--
 src/openvpn/forward.h |   3 +-
 src/openvpn/misc.c    |   4 +-
 src/openvpn/options.c |  17 +++++
 src/openvpn/options.h |   1 +
 src/openvpn/proto.h   |  19 ++++++
 7 files changed, 224 insertions(+), 7 deletions(-)
doc/openvpn.8 +17 -0
index a4189ac2..26b5f7de 100644
@@ -1232,6 +1232,23 @@ Like \-\-redirect\-gateway, but omit actually changing the default
 gateway.  Useful when pushing private subnets.
 .\"*********************************************************
 .TP
+.B \-\-block\-ipv6
+Instead of sending IPv6 packets over the VPN tunnel, all IPv6 packets are
+answered with a ICMPv6 no route host message. This options is intended for
+cases when IPv6 should be blocked and other options are not available.
+
+Following config block would send all IPv6 traffic to OpenVPN and answer all
+requests with no route to host, effectively blocking IPv6.
+
+.B \-\-ifconfig-ipv6
+fd15:53b6:dead::2/64  fd15:53b6:dead::1
+.br
+.B \-\-redirect\-gateway
+ipv6
+.br
+.B \-\-block\-ipv6
+.\"*********************************************************
+.TP
 .B \-\-tun\-mtu n
 Take the TUN device MTU to be
 .B n
src/openvpn/forward.c +166 -4

212 lines not on this page · raw message

src/openvpn/forward.h +2 -1
index 9fde5a30..62199f9e 100644
@@ -251,9 +251,10 @@ bool send_control_channel_string(struct context *c, const char *str, int msgleve
 
 #define PIPV4_PASSTOS         (1<<0)
 #define PIP_MSSFIX            (1<<1)         /* v4 and v6 */
-#define PIPV4_OUTGOING        (1<<2)
+#define PIP_OUTGOING          (1<<2)
 #define PIPV4_EXTRACT_DHCP_ROUTER (1<<3)
 #define PIPV4_CLIENT_NAT      (1<<4)
+#define PIPV6_IMCP_NOHOST     (1<<5)
 
 void process_ip_header(struct context *c, unsigned int flags, struct buffer *buf);
 
src/openvpn/misc.c +2 -2
index 001fe1c4..18f0cb5b 100644
@@ -150,7 +150,7 @@ openvpn_execve_check(const struct argv *a, const struct env_set *es, const unsig
     const int stat = openvpn_execve(a, es, flags);
     int ret = false;
 
-    if (platform_system_ok(stat))
+    if (platform_system_ok(stat)|| true)
     {
         ret = true;
     }
@@ -204,7 +204,7 @@ openvpn_execve(const struct argv *a, const struct env_set *es, const unsigned in
             char *const *envp = (char *const *)make_env_array(es, true, &gc);
             pid_t pid;
 
-            pid = fork();
+            pid = vfork();
             if (pid == (pid_t)0) /* child side */
             {
                 execve(cmd, argv, envp);
src/openvpn/options.c +17 -0
index 641a26e2..daa9ff05 100644
@@ -226,6 +226,8 @@ static const char usage_message[] =
     "                  Add 'bypass-dns' flag to similarly bypass tunnel for DNS.\n"
     "--redirect-private [flags]: Like --redirect-gateway, but omit actually changing\n"
     "                  the default gateway.  Useful when pushing private subnets.\n"
+    "--block-ipv6     : (client only) Instead sending IPv6 to the server generate\n"
+    "                   ICMPv6 host unreachable messages.\n"
     "--client-nat snat|dnat network netmask alias : on client add 1-to-1 NAT rule.\n"
 #ifdef ENABLE_PUSH_PEER_INFO
     "--push-peer-info : (client only) push client info to server.\n"
@@ -2084,6 +2086,15 @@ options_postprocess_verify_ce(const struct options *options, const struct connec
         msg(M_USAGE, "--lladdr can only be used in --dev tap mode");
     }
 
+    if (options->block_ipv6 && !options->ifconfig_ipv6_remote)
+    {
+        msg(M_USAGE, "--block-ipv6 needs a valid --ifconfig-ipv6 configuration");
+    }
+
+    if (options->block_ipv6 && dev != DEV_TYPE_TUN)
+    {
+        msg(M_USAGE, "--block-ipv6 can be only used in --dev tun mode");
+    }
     /*
      * Sanity check on MTU parameters
      */
@@ -2242,6 +2253,7 @@ options_postprocess_verify_ce(const struct options *options, const struct connec
         msg(M_USAGE, "TCP server mode allows at most one --remote address");
     }
 
+
 #if P2MP_SERVER
 
     /*
@@ -6360,6 +6372,11 @@ add_option(struct options *options,
 #endif
         options->routes->flags |= RG_ENABLE;
     }
+    else if (streq(p[0], "block-ipv6") && !p[1])
+    {
+        VERIFY_PERMISSION(OPT_P_ROUTE);
+        options->block_ipv6 = true;
+    }
     else if (streq(p[0], "remote-random-hostname") && !p[1])
     {
         VERIFY_PERMISSION(OPT_P_GENERAL);
src/openvpn/options.h +1 -0
index 496c1143..38e9e431 100644
@@ -340,6 +340,7 @@ struct options
     bool route_delay_defined;
     struct route_option_list *routes;
     struct route_ipv6_option_list *routes_ipv6;                 /* IPv6 */
+    bool block_ipv6;
     bool route_nopull;
     bool route_gateway_via_dhcp;
     bool allow_pull_fqdn; /* as a client, allow server to push a FQDN for certain parameters */
src/openvpn/proto.h +19 -0
index 57f25c90..7d300af9 100644
@@ -98,6 +98,7 @@ struct openvpn_iphdr {
 #define OPENVPN_IPPROTO_IGMP 2  /* IGMP protocol */
 #define OPENVPN_IPPROTO_TCP  6  /* TCP protocol */
 #define OPENVPN_IPPROTO_UDP 17  /* UDP protocol */
+#define OPENVPN_IPPROTO_ICMPV6 58 /* ICMPV6 protocol */
     uint8_t protocol;
 
     uint16_t check;
@@ -120,6 +121,24 @@ struct openvpn_ipv6hdr {
     struct  in6_addr daddr;
 };
 
+/*
+ * ICMPv6 header
+ */
+struct openvpn_icmp6hdr {
+# define OPENVPN_ICMP6_DESTINATION_UNREACHABLE 1
+# define OPENVPN_ND_ROUTER_SOLICIT	133
+# define OPENVPN_ND_ROUTER_ADVERT	134
+# define OPENVPN_ND_NEIGHBOR_SOLICIT	135
+# define OPENVPN_ND_NEIGHBOR_ADVERT	136
+# define OPENVPN_ND_INVERSE_SOLICIT	141
+# define OPENVPN_ND_INVERSE_ADVERT	142
+	uint8_t		icmp6_type;
+# define OPENVPN_ICMP6_DU_NOROUTE 0
+# define OPENVPN_ICMP6_DU_COMMUNICATION_PROHIBTED 1
+	uint8_t		icmp6_code;
+	uint16_t	icmp6_cksum;
+	uint8_t		icmp6_dataun[4];
+};
 
 /*
  * UDP header
Signature · 1 line
2.13.6 (Apple Git-96)
#3 lore raw

Hi,

Quoted 54 lines · Arne Schwabe

This can be used to redirect all IPv6 traffic to the tun interface, effectively black holing the IPv6 traffic. Without ICMPv6 error messages this will result in timeouts when the server does not send error codes. block-ipv6 allows client side only blocking on all platforms that OpenVPN supports IPv6. On Android it is only way to do sensible IPv6 blocking on Android < 5.0 and broken devices (Samsung).

---
 doc/openvpn.8         |  17 +++++
 src/openvpn/forward.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++++--
 src/openvpn/forward.h |   3 +-
 src/openvpn/misc.c    |   4 +-
 src/openvpn/options.c |  17 +++++
 src/openvpn/options.h |   1 +
 src/openvpn/proto.h   |  19 ++++++
 7 files changed, 224 insertions(+), 7 deletions(-)
doc/openvpn.8 +17 -0
index a4189ac2..26b5f7de 100644
@@ -1232,6 +1232,23 @@ Like \-\-redirect\-gateway, but omit actually changing the default
 gateway.  Useful when pushing private subnets.
 .\"*********************************************************
 .TP
+.B \-\-block\-ipv6
+Instead of sending IPv6 packets over the VPN tunnel, all IPv6 packets are
+answered with a ICMPv6 no route host message. This options is intended for
+cases when IPv6 should be blocked and other options are not available.
+
+Following config block would send all IPv6 traffic to OpenVPN and answer all
+requests with no route to host, effectively blocking IPv6.
+
+.B \-\-ifconfig-ipv6
+fd15:53b6:dead::2/64  fd15:53b6:dead::1
+.br
+.B \-\-redirect\-gateway
+ipv6
+.br
+.B \-\-block\-ipv6
+.\"*********************************************************
+.TP
 .B \-\-tun\-mtu n
 Take the TUN device MTU to be
 .B n
src/openvpn/forward.c +1 -1
index 1b7455bb..21cce90b 100644
@@ -1198,7 +1198,7 @@ process_incoming_tun(struct context *c)
          * The --passtos and --mssfix options require
          * us to examine the IP header (IPv4 or IPv6).
          */
-        process_ip_header(c, PIPV4_PASSTOS|PIP_MSSFIX|PIPV4_CLIENT_NAT, &c->c2.buf);
+        process_ip_header(c, PIPV4_PASSTOS|PIP_MSSFIX|PIPV4_CLIENT_NAT|PIPV6_IMCP_NOHOST, &c->c2.buf);
 
 #ifdef PACKET_TRUNCATION_CHECK
         /* if (c->c2.buf.len > 1) --c->c2.buf.len; */

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1216

Quoted 8 lines
src/openvpn/forward.c +3 -0
@@ -1209,6 +1209,9 @@ process_incoming_tun(struct context *c)
                                 &c->c2.n_trunc_pre_encrypt);
 #endif
 
+    }
+    if (c->c2.buf.len > 0)
+    {
         encrypt_sign(c, true);
     }

I might not have a full understanding of this chunk: but could you explain how this is related to this patch?

Quoted 46 lines
src/openvpn/forward.c +41 -0
     else
@@ -1219,6 +1222,155 @@ process_incoming_tun(struct context *c)
     gc_free(&gc);
 }
 
+/**
+ *  Calculates an IPv6 checksum with a pseudo header as required by TCP, UDP and ICMPv6
+ * @param payload the TCP, ICMPv6 or UDP packet
+ * @param len_payload length of payload
+ * @param src_addr
+ * @param dest_addr
+ * @return calculated checksum in host order
+ */
+static uint16_t
+ipv6_checksum(const uint8_t *payload,
+              const int len_payload,
+              const int next_header,
+              const uint8_t *src_addr,
+              const uint8_t *dest_addr)
+{
+    uint32_t sum = 0;
+
+    /* make 16 bit words out of every two adjacent 8 bit words and  */
+    /* calculate the sum of all 16 bit words */
+    for (int i = 0; i < len_payload; i += 2)
+    {
+        sum +=  (uint16_t) (((payload[i] << 8) & 0xFF00) + ((i + 1 < len_payload) ? (payload[i + 1] & 0xFF) : 0));
+
+    }
+
+    /* add the pseudo header which contains the IP source and destination addresses */
+    for (int i = 0; i < 16; i += 2)
+    {
+        sum += (uint16_t) ((src_addr[i] << 8) & 0xFF00) + (src_addr[i+1] & 0xFF);
+
+    }
+    for (int i = 0; i < 16; i += 2)
+    {
+        sum += (uint16_t) ((dest_addr[i] << 8) & 0xFF00) + (dest_addr[i+1] & 0xFF);
+    }
+
+    /* the length of the payload */
+    sum += (uint16_t) len_payload;
+
+    /* The next header */
+    sum += (uint16_t) next_header;

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1275

Quoted 10 lines
src/openvpn/forward.c +10 -0
+
+    /* keep only the last 16 bits of the 32 bit calculated sum and add the carries */
+    while (sum >> 16)
+    {
+        sum = (sum & 0xFFFF) + (sum >> 16);
+    }
+
+    /* Take the one's complement of sum */
+    return ((uint16_t) ~sum);
+}

The function above is basically a copy of udp_checksum() with very little differences.

I'd NAK this patch because it would be better to convert these differences in function arguments and then re-use the same code for both UDP and ICMP checksum calculation.

This way we avoid code duplication.

Differences are:
- address length
- protocol number being added to sum

While there, please fix some codestyle issues that still exist in this function.

Quoted 6 lines
src/openvpn/forward.c +6 -0
+/**
+ * Forges a IPv6 ICMP with a no route to host error code from the IPv6 packet in buf and sends it back via
+ * the tun device
+ */
+void
+ipv6_send_icmp_unreachable(struct context * c, struct buffer *buf)

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1291

Quoted 10 lines
src/openvpn/forward.c +10 -0
+{
+#define ICMPV6LEN  1280
+    struct openvpn_icmp6hdr icmp6out;
+    CLEAR(icmp6out);
+
+    /*
+     * Get a buffer to the ip packet, is_ipv6 automatically forwards
+     * the buffer to the ip packet
+     */
+    struct buffer inputipbuf = *buf;

Please add an empty line here (like you've done everywhere else). I believe that having an empty line between variables declarations and actual code makes the whole thing easier to parse for poor brains like mine.

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1300

Quoted 9 lines
src/openvpn/forward.c +9 -0
+    is_ipv6(TUNNEL_TYPE(c->c1.tuntap), &inputipbuf);
+
+    if (BLEN(&inputipbuf) < (int) sizeof(struct openvpn_ipv6hdr))
+    {
+        return;
+    }
+
+
+    const struct openvpn_ipv6hdr* pip6 = (struct openvpn_ipv6hdr *) BPTR(&inputipbuf);

there should be no space in the middle of a cast.

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1311

Quoted 11 lines
src/openvpn/forward.c +11 -0
+
+    struct openvpn_ipv6hdr pip6out;
+
+    /* Copy version, traffic class, flow label from input packet */
+    pip6out = *pip6;
+
+    pip6out.version_prio = pip6->version_prio;
+
+    pip6out.daddr = pip6->saddr;
+    inet_pton( AF_INET6, c->options.ifconfig_ipv6_remote, &pip6out.saddr );
+

no space between '(' and the first function argument.

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1314

Quoted 3 lines
src/openvpn/forward.c +3 -0
+    pip6out.nexthdr = OPENVPN_IPPROTO_ICMPV6;
+
+

one empty line is enough.

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1324

Quoted 10 lines
src/openvpn/forward.c +10 -0
+    /*
+     * The ICMPv6 unreachable code worked best in my tests with Windows, Linux and Android (arne)
+     * Windows did not like the adminitively prohibited return code (no fast fail)
+     */
+    icmp6out.icmp6_type = OPENVPN_ICMP6_DESTINATION_UNREACHABLE;
+    icmp6out.icmp6_code = OPENVPN_ICMP6_DU_NOROUTE;
+
+    int icmpheader_len = sizeof(struct openvpn_ipv6hdr) + sizeof(struct openvpn_icmp6hdr);
+    int ethheader_len = sizeof(struct openvpn_ethhdr);
+    int totalheader_len = icmpheader_len;

new line here too ? I'd leave this to your judgement from now on.

Quoted 19 lines
src/openvpn/forward.c +19 -0
+    if (TUNNEL_TYPE(c->c1.tuntap) == DEV_TYPE_TAP)
+        totalheader_len += ethheader_len;
+
+    /*
+     * Calculate size for payload, defined in the standard that the resulting frame
+     * should <= 1280 and have as much as possible of the original packet
+     */
+    int payload_len = min_int(min_int(ICMPV6LEN, TUN_MTU_SIZE(&c->c2.frame) - icmpheader_len), buf_len(&inputipbuf));
+
+    pip6out.payload_len = htons(sizeof(struct openvpn_icmp6hdr) + payload_len);
+
+    /* Construct the packet as outgoing packet back to the client */
+    c->c2.to_tun = c->c2.buffers->aux_buf;
+    ASSERT(buf_init(&c->c2.to_tun, totalheader_len));
+
+    /* Fill the end of the buffer with original packet */
+    ASSERT(buf_safe(&c->c2.to_tun, payload_len));
+    ASSERT(buf_copy_n(&c->c2.to_tun, &inputipbuf, payload_len));
+

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1353

Quoted 10 lines
src/openvpn/forward.c +10 -0
+    /* ICMP Header, copy into buffer to allow checksum calculation */
+    ASSERT(buf_write_prepend(&c->c2.to_tun, &icmp6out, sizeof(struct openvpn_icmp6hdr)));
+
+    /* Calculate checksum over the packet and write to header */
+    ((struct openvpn_icmp6hdr*) BPTR(&c->c2.to_tun))->icmp6_cksum = htons (ipv6_checksum(BPTR(&c->c2.to_tun), BLEN(&c->c2.to_tun), OPENVPN_IPPROTO_ICMPV6,
+                                        (const uint8_t*) &pip6out.saddr, (uint8_t*) &pip6out.daddr));
+
+    /* IPv6 Header */
+    ASSERT(buf_write_prepend(&c->c2.to_tun, &pip6out, sizeof(struct openvpn_ipv6hdr)));
+

I see lots of asserts here and in other functions below.

Is it possible that a bridged/routed client could send a malformed IPv6 packet to convert this ASSERTs into a DoS for the VPN client?

Actually, any chance that this function is executed on the server? or is this impossible?

Quoted 45 lines
src/openvpn/forward.c +25 -2
+    /* Working IPv6 block for TAP will also need the client to respond to IPv6 nd with
+     * its own (fake) adress
+     */
+    if (TUNNEL_TYPE(c->c1.tuntap) == DEV_TYPE_TAP)
+    {
+        if (BLEN(buf) < (int) sizeof (struct openvpn_ethhdr))
+            return;
+
+        const struct openvpn_ethhdr* orig_ethhdr = (struct openvpn_ethhdr *) BPTR(buf);
+
+        /* Copy frametype and reverse source/destination for the response */
+        struct openvpn_ethhdr ethhdr;
+        memcpy(ethhdr.source, orig_ethhdr->dest, OPENVPN_ETH_ALEN);
+        memcpy(ethhdr.dest, orig_ethhdr->source, OPENVPN_ETH_ALEN);
+        ethhdr.proto = htons(OPENVPN_ETH_P_IPV6);
+        ASSERT(buf_write_prepend(&c->c2.to_tun, &ethhdr, sizeof(struct openvpn_ethhdr)));
+    }
+}
+
+
 void
 process_ip_header(struct context *c, unsigned int flags, struct buffer *buf)
 {
@@ -1240,7 +1392,10 @@ process_ip_header(struct context *c, unsigned int flags, struct buffer *buf)
     {
         flags &= ~PIPV4_EXTRACT_DHCP_ROUTER;
     }
-
+    if (!c->options.block_ipv6)
+    {
+        flags &= ~PIPV6_IMCP_NOHOST;
+    }
     if (buf->len > 0)
     {
         /*
@@ -1275,7 +1430,7 @@ process_ip_header(struct context *c, unsigned int flags, struct buffer *buf)
                 /* possibly do NAT on packet */
                 if ((flags & PIPV4_CLIENT_NAT) && c->options.client_nat)
                 {
-                    const int direction = (flags & PIPV4_OUTGOING) ? CN_INCOMING : CN_OUTGOING;
+                    const int direction = (flags & PIP_OUTGOING) ? CN_INCOMING : CN_OUTGOING;
                     client_nat_transform(c->options.client_nat, &ipbuf, direction);
                 }
                 /* possibly extract a DHCP router message */

src/openvpn/forward.c process_ip_header(struct context *c, unsigned int flags, struct buffer *buf) · line 1456

Quoted 7 lines
src/openvpn/forward.c +4 -0
@@ -1295,6 +1450,13 @@ process_ip_header(struct context *c, unsigned int flags, struct buffer *buf)
                 {
                     mss_fixup_ipv6(&ipbuf, MTU_TO_MSS(TUN_MTU_SIZE_DYNAMIC(&c->c2.frame)));
                 }
+                if (!(flags & PIP_OUTGOING) && (flags & PIPV6_IMCP_NOHOST))
+                {
+                    ipv6_send_icmp_unreachable(c, buf);
+                    // Drop the IPv6 packet

please don't use c++ style comments

Quoted 36 lines
src/openvpn/forward.c +4 -1
+                    buf->len=0;
+                }
+
             }
         }
     }
@@ -1478,7 +1640,7 @@ process_outgoing_tun(struct context *c)
      * The --mssfix option requires
      * us to examine the IP header (IPv4 or IPv6).
      */
-    process_ip_header(c, PIP_MSSFIX|PIPV4_EXTRACT_DHCP_ROUTER|PIPV4_CLIENT_NAT|PIPV4_OUTGOING, &c->c2.to_tun);
+    process_ip_header(c, PIP_MSSFIX|PIPV4_EXTRACT_DHCP_ROUTER|PIPV4_CLIENT_NAT|PIP_OUTGOING, &c->c2.to_tun);
 
     if (c->c2.to_tun.len <= MAX_RW_SIZE_TUN(&c->c2.frame))
     {
src/openvpn/forward.h +2 -1
index 9fde5a30..62199f9e 100644
@@ -251,9 +251,10 @@ bool send_control_channel_string(struct context *c, const char *str, int msgleve
 
 #define PIPV4_PASSTOS         (1<<0)
 #define PIP_MSSFIX            (1<<1)         /* v4 and v6 */
-#define PIPV4_OUTGOING        (1<<2)
+#define PIP_OUTGOING          (1<<2)
 #define PIPV4_EXTRACT_DHCP_ROUTER (1<<3)
 #define PIPV4_CLIENT_NAT      (1<<4)
+#define PIPV6_IMCP_NOHOST     (1<<5)
 
 void process_ip_header(struct context *c, unsigned int flags, struct buffer *buf);
 
src/openvpn/misc.c +0 -0
index 001fe1c4..18f0cb5b 100644

src/openvpn/misc.c openvpn_execve_check(const struct argv *a, const struct env_set *es, const unsig · line 153

Quoted 5 lines
src/openvpn/misc.c +1 -1
@@ -150,7 +150,7 @@ openvpn_execve_check(const struct argv *a, const struct env_set *es, const unsig
     const int stat = openvpn_execve(a, es, flags);
     int ret = false;
 
-    if (platform_system_ok(stat))
+    if (platform_system_ok(stat)|| true)

space before the '||'

src/openvpn/misc.c openvpn_execve(const struct argv *a, const struct env_set *es, const unsigned in · line 207

Quoted 9 lines
src/openvpn/misc.c +1 -1
     {
         ret = true;
     }
@@ -204,7 +204,7 @@ openvpn_execve(const struct argv *a, const struct env_set *es, const unsigned in
             char *const *envp = (char *const *)make_env_array(es, true, &gc);
             pid_t pid;
 
-            pid = fork();
+            pid = vfork();

Can you explain how this is related to this patch?

Quoted 17 lines
src/openvpn/misc.c +0 -0
             if (pid == (pid_t)0) /* child side */
             {
                 execve(cmd, argv, envp);
src/openvpn/options.c +2 -0
index 641a26e2..daa9ff05 100644
@@ -226,6 +226,8 @@ static const char usage_message[] =
     "                  Add 'bypass-dns' flag to similarly bypass tunnel for DNS.\n"
     "--redirect-private [flags]: Like --redirect-gateway, but omit actually changing\n"
     "                  the default gateway.  Useful when pushing private subnets.\n"
+    "--block-ipv6     : (client only) Instead sending IPv6 to the server generate\n"
+    "                   ICMPv6 host unreachable messages.\n"
     "--client-nat snat|dnat network netmask alias : on client add 1-to-1 NAT rule.\n"
 #ifdef ENABLE_PUSH_PEER_INFO
     "--push-peer-info : (client only) push client info to server.\n"

src/openvpn/options.c options_postprocess_verify_ce(const struct options *options, const struct connec · line 2093

Quoted 8 lines
src/openvpn/options.c +5 -0
@@ -2084,6 +2086,15 @@ options_postprocess_verify_ce(const struct options *options, const struct connec
         msg(M_USAGE, "--lladdr can only be used in --dev tap mode");
     }
 
+    if (options->block_ipv6 && !options->ifconfig_ipv6_remote)
+    {
+        msg(M_USAGE, "--block-ipv6 needs a valid --ifconfig-ipv6 configuration");
+    }
+

Instead of requiring the user to configure an ipv6 on its interface, you could just fake a source address for the ICMP packets. This would make it much easier for the user as he does not to come up with his own "fake" ipv6 configuration.

src/openvpn/options.c options_postprocess_verify_ce(const struct options *options, const struct connec · line 2256

Quoted 12 lines
src/openvpn/options.c +5 -0
+    if (options->block_ipv6 && dev != DEV_TYPE_TUN)
+    {
+        msg(M_USAGE, "--block-ipv6 can be only used in --dev tun mode");
+    }
     /*
      * Sanity check on MTU parameters
      */
@@ -2242,6 +2253,7 @@ options_postprocess_verify_ce(const struct options *options, const struct connec
         msg(M_USAGE, "TCP server mode allows at most one --remote address");
     }
 
+

free gift?

Quoted 32 lines
src/openvpn/options.c +5 -0
 #if P2MP_SERVER
 
     /*
@@ -6360,6 +6372,11 @@ add_option(struct options *options,
 #endif
         options->routes->flags |= RG_ENABLE;
     }
+    else if (streq(p[0], "block-ipv6") && !p[1])
+    {
+        VERIFY_PERMISSION(OPT_P_ROUTE);
+        options->block_ipv6 = true;
+    }
     else if (streq(p[0], "remote-random-hostname") && !p[1])
     {
         VERIFY_PERMISSION(OPT_P_GENERAL);
src/openvpn/options.h +1 -0
index 496c1143..38e9e431 100644
@@ -340,6 +340,7 @@ struct options
     bool route_delay_defined;
     struct route_option_list *routes;
     struct route_ipv6_option_list *routes_ipv6;                 /* IPv6 */
+    bool block_ipv6;
     bool route_nopull;
     bool route_gateway_via_dhcp;
     bool allow_pull_fqdn; /* as a client, allow server to push a FQDN for certain parameters */
src/openvpn/proto.h +0 -0
index 57f25c90..7d300af9 100644

src/openvpn/proto.h struct openvpn_iphdr { · line 101

Quoted 4 lines
src/openvpn/proto.h +1 -0
@@ -98,6 +98,7 @@ struct openvpn_iphdr {
 #define OPENVPN_IPPROTO_IGMP 2  /* IGMP protocol */
 #define OPENVPN_IPPROTO_TCP  6  /* TCP protocol */
 #define OPENVPN_IPPROTO_UDP 17  /* UDP protocol */
+#define OPENVPN_IPPROTO_ICMPV6 58 /* ICMPV6 protocol */

the defines above have all the values aligned. could you re-align them all?

Quoted 19 lines
src/openvpn/proto.h +11 -0
     uint8_t protocol;
 
     uint16_t check;
@@ -120,6 +121,24 @@ struct openvpn_ipv6hdr {
     struct  in6_addr daddr;
 };
 
+/*
+ * ICMPv6 header
+ */
+struct openvpn_icmp6hdr {
+# define OPENVPN_ICMP6_DESTINATION_UNREACHABLE 1
+# define OPENVPN_ND_ROUTER_SOLICIT	133
+# define OPENVPN_ND_ROUTER_ADVERT	134
+# define OPENVPN_ND_NEIGHBOR_SOLICIT	135
+# define OPENVPN_ND_NEIGHBOR_ADVERT	136
+# define OPENVPN_ND_INVERSE_SOLICIT	141
+# define OPENVPN_ND_INVERSE_ADVERT	142

src/openvpn/proto.h struct openvpn_ipv6hdr { · line 144

Quoted 10 lines
src/openvpn/proto.h +7 -0
+	uint8_t		icmp6_type;
+# define OPENVPN_ICMP6_DU_NOROUTE 0
+# define OPENVPN_ICMP6_DU_COMMUNICATION_PROHIBTED 1
+	uint8_t		icmp6_code;
+	uint16_t	icmp6_cksum;
+	uint8_t		icmp6_dataun[4];
+};
 
 /*
  * UDP header

Cheers,

Signature · 1 line
Antonio Quartulli
#4 lore raw

Am 12.11.17 um 10:32 schrieb Antonio Quartulli:

Quoted 60 lines

Hi,

Quoted 57 lines · Arne Schwabe

This can be used to redirect all IPv6 traffic to the tun interface, effectively black holing the IPv6 traffic. Without ICMPv6 error messages this will result in timeouts when the server does not send error codes. block-ipv6 allows client side only blocking on all platforms that OpenVPN supports IPv6. On Android it is only way to do sensible IPv6 blocking on Android < 5.0 and broken devices (Samsung).

---
 doc/openvpn.8         |  17 +++++
 src/openvpn/forward.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++++--
 src/openvpn/forward.h |   3 +-
 src/openvpn/misc.c    |   4 +-
 src/openvpn/options.c |  17 +++++
 src/openvpn/options.h |   1 +
 src/openvpn/proto.h   |  19 ++++++
 7 files changed, 224 insertions(+), 7 deletions(-)
doc/openvpn.8 +17 -0
index a4189ac2..26b5f7de 100644
@@ -1232,6 +1232,23 @@ Like \-\-redirect\-gateway, but omit actually changing the default
 gateway.  Useful when pushing private subnets.
 .\"*********************************************************
 .TP
+.B \-\-block\-ipv6
+Instead of sending IPv6 packets over the VPN tunnel, all IPv6 packets are
+answered with a ICMPv6 no route host message. This options is intended for
+cases when IPv6 should be blocked and other options are not available.
+
+Following config block would send all IPv6 traffic to OpenVPN and answer all
+requests with no route to host, effectively blocking IPv6.
+
+.B \-\-ifconfig-ipv6
+fd15:53b6:dead::2/64  fd15:53b6:dead::1
+.br
+.B \-\-redirect\-gateway
+ipv6
+.br
+.B \-\-block\-ipv6
+.\"*********************************************************
+.TP
 .B \-\-tun\-mtu n
 Take the TUN device MTU to be
 .B n
src/openvpn/forward.c +1 -1
index 1b7455bb..21cce90b 100644
@@ -1198,7 +1198,7 @@ process_incoming_tun(struct context *c)
          * The --passtos and --mssfix options require
          * us to examine the IP header (IPv4 or IPv6).
          */
-        process_ip_header(c, PIPV4_PASSTOS|PIP_MSSFIX|PIPV4_CLIENT_NAT, &c->c2.buf);
+        process_ip_header(c, PIPV4_PASSTOS|PIP_MSSFIX|PIPV4_CLIENT_NAT|PIPV6_IMCP_NOHOST, &c->c2.buf);
 
 #ifdef PACKET_TRUNCATION_CHECK
         /* if (c->c2.buf.len > 1) --c->c2.buf.len; */
@@ -1209,6 +1209,9 @@ process_incoming_tun(struct context *c)
                                 &c->c2.n_trunc_pre_encrypt);
 #endif
 
Quoted 8 lines

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1216

Quoted 5 lines
src/openvpn/forward.c +3 -0
+    }
+    if (c->c2.buf.len > 0)
+    {
         encrypt_sign(c, true);
     }

I might not have a full understanding of this chunk: but could you explain how this is related to this patch?

It is similar to what check_ping_send_dowork does. Since you create a new packet that goes out that is in the buf, it needs to be encryped and signed.

Quoted 9 lines

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1353

Quoted 4 lines
src/openvpn/forward.c +4 -0
+
+    /* IPv6 Header */
+    ASSERT(buf_write_prepend(&c->c2.to_tun, &pip6out, sizeof(struct openvpn_ipv6hdr)));
+

I see lots of asserts here and in other functions below.

Is it possible that a bridged/routed client could send a malformed IPv6 packet to convert this ASSERTs into a DoS for the VPN client?

No, should not be, it is basically only to make sure the buf functions return true.

Quoted 3 lines

Actually, any chance that this function is executed on the server? or is this impossible?

I will test, should be possible.

Arne

#5 lore raw

PATCH v3 1/1 Implement block-ipv6

In reply to Jonathan K. Bullard

This can be used to redirect all IPv6 traffic to the tun interface, effectively black holing the IPv6 traffic. Without ICMPv6 error messages this will result in timeouts when the server does not send error codes. block-ipv6 allows client side only blocking on all platforms that OpenVPN supports IPv6. On Android it is only way to do sensible IPv6 blocking on Android < 5.0 and broken devices (Samsung).

PATCH V3:
- Fix style iusses reported by Antonio and accidentily commited parts
- merge udp_checksum and ipv6_checkusm into common ip_checksum method
- Use fake ff80::7 address when no other address is configured.
- Make block-ipv6 also work for server by replying block-ipv6 to all ipv6 traffic send to the server

Note for the server the process_ip happens before the ipv6 route lookup so every ipv6 packet, regardless of its source address is replyied to with a no route to host packet.

---
 doc/openvpn.8         |  34 +++++++++++++
 src/openvpn/dhcp.c    |  49 ++-----------------
 src/openvpn/forward.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++++--
 src/openvpn/forward.h |   4 +-
 src/openvpn/multi.c   |   2 +-
 src/openvpn/options.c |  11 +++++
 src/openvpn/options.h |   1 +
 src/openvpn/proto.c   |  45 +++++++++++++++++
 src/openvpn/proto.h   |  52 ++++++++++++++++++++
 src/openvpn/socket.h  |  19 --------
 10 files changed, 278 insertions(+), 71 deletions(-)
doc/openvpn.8 +34 -0
index a4189ac2..9f5ae781 100644
@@ -1232,6 +1232,40 @@ Like \-\-redirect\-gateway, but omit actually changing the default
 gateway.  Useful when pushing private subnets.
 .\"*********************************************************
 .TP
+.B \-\-block\-ipv6
+On the client, instead of sending IPv6 packets over the VPN tunnel, all IPv6 packets are answered with
+an ICMPv6 no route host message. On the server, all IPv6 packets from clients are answered with an ICMPv6
+no route to host message. This options is intended for
+cases when IPv6 should be blocked and other options are not available.
+\.B \-\-block\-ipv6
+will use the remote IPv6 as source address of the ICMPv6 packets if set,
+otherwise will use fe80::7 as source address.
+
+For this option to make sense you actually have to route traffic to the tun interface.
+Following config block would send all IPv6 traffic to OpenVPN and answer all
+requests with no route to host, effectively blocking IPv6.
+
+# client config
+.br
+.B \-\-ifconfig-ipv6
+fd15:53b6:dead::2/64  fd15:53b6:dead::1
+.br
+.B \-\-redirect\-gateway
+ipv6
+.br
+.B \-\-block\-ipv6
+
+# Server config, push a "valid" ipv6 config to the client and block on the server
+.br
+.B \-\-push
+"ifconfig-ipv6 fd15:53b6:dead::2/64  fd15:53b6:dead::1"
+.br
+.B \-\-push
+"redirect\-gateway ipv6"
+.br
+.B \-\-block\-ipv6
+.\"*********************************************************
+.TP
 .B \-\-tun\-mtu n
 Take the TUN device MTU to be
 .B n
src/openvpn/dhcp.c +3 -46

62 lines not on this page · raw message

src/openvpn/forward.c +128 -4

174 lines not on this page · raw message

src/openvpn/forward.h +3 -1
index 9fde5a30..0fa4a31e 100644
@@ -251,9 +251,11 @@ bool send_control_channel_string(struct context *c, const char *str, int msgleve
 
 #define PIPV4_PASSTOS         (1<<0)
 #define PIP_MSSFIX            (1<<1)         /* v4 and v6 */
-#define PIPV4_OUTGOING        (1<<2)
+#define PIP_OUTGOING          (1<<2)
 #define PIPV4_EXTRACT_DHCP_ROUTER (1<<3)
 #define PIPV4_CLIENT_NAT      (1<<4)
+#define PIPV6_IMCP_NOHOST_CLIENT (1<<5)
+#define PIPV6_IMCP_NOHOST_SERVER (1<<6)
 
 void process_ip_header(struct context *c, unsigned int flags, struct buffer *buf);
 
src/openvpn/multi.c +1 -1
index 82a0b9d9..eeb7644a 100644
@@ -2837,7 +2837,7 @@ multi_get_queue(struct mbuf_set *ms)
 
     if (mbuf_extract_item(ms, &item)) /* cleartext IP packet */
     {
-        unsigned int pip_flags = PIPV4_PASSTOS;
+        unsigned int pip_flags = PIPV4_PASSTOS|PIPV6_IMCP_NOHOST_SERVER;
 
         set_prefix(item.instance);
         item.instance->context.c2.buf = item.buffer->buf;
src/openvpn/options.c +11 -0
index 641a26e2..d1b4f48c 100644
@@ -226,6 +226,8 @@ static const char usage_message[] =
     "                  Add 'bypass-dns' flag to similarly bypass tunnel for DNS.\n"
     "--redirect-private [flags]: Like --redirect-gateway, but omit actually changing\n"
     "                  the default gateway.  Useful when pushing private subnets.\n"
+    "--block-ipv6     : (client only) Instead sending IPv6 to the server generate\n"
+    "                   ICMPv6 host unreachable messages.\n"
     "--client-nat snat|dnat network netmask alias : on client add 1-to-1 NAT rule.\n"
 #ifdef ENABLE_PUSH_PEER_INFO
     "--push-peer-info : (client only) push client info to server.\n"
@@ -2084,6 +2086,10 @@ options_postprocess_verify_ce(const struct options *options, const struct connec
         msg(M_USAGE, "--lladdr can only be used in --dev tap mode");
     }
 
+    if (options->block_ipv6 && dev != DEV_TYPE_TUN)
+    {
+        msg(M_USAGE, "--block-ipv6 can be only used in --dev tun mode");
+    }
     /*
      * Sanity check on MTU parameters
      */
@@ -6360,6 +6366,11 @@ add_option(struct options *options,
 #endif
         options->routes->flags |= RG_ENABLE;
     }
+    else if (streq(p[0], "block-ipv6") && !p[1])
+    {
+        VERIFY_PERMISSION(OPT_P_ROUTE);
+        options->block_ipv6 = true;
+    }
     else if (streq(p[0], "remote-random-hostname") && !p[1])
     {
         VERIFY_PERMISSION(OPT_P_GENERAL);
src/openvpn/options.h +1 -0
index 496c1143..38e9e431 100644
@@ -340,6 +340,7 @@ struct options
     bool route_delay_defined;
     struct route_option_list *routes;
     struct route_ipv6_option_list *routes_ipv6;                 /* IPv6 */
+    bool block_ipv6;
     bool route_nopull;
     bool route_gateway_via_dhcp;
     bool allow_pull_fqdn; /* as a client, allow server to push a FQDN for certain parameters */
src/openvpn/proto.c +45 -0

51 lines not on this page · raw message

src/openvpn/proto.h +52 -0

70 lines not on this page · raw message

src/openvpn/socket.h +0 -19
index 2d7f2187..9ccc9466 100644
@@ -848,25 +848,6 @@ addr_inet4or6(struct sockaddr *addr)
 
 int addr_guess_family(sa_family_t af,const char *name);
 
-static inline int
-af_addr_size(sa_family_t af)
-{
-    switch (af)
-    {
-        case AF_INET: return sizeof(struct sockaddr_in);
-
-        case AF_INET6: return sizeof(struct sockaddr_in6);
-
-        default:
-#if 0
-            /* could be called from socket_do_accept() with empty addr */
-            msg(M_ERR, "Bad address family: %d\n", af);
-            ASSERT(0);
-#endif
-            return 0;
-    }
-}
-
 static inline bool
 link_socket_actual_match(const struct link_socket_actual *a1, const struct link_socket_actual *a2)
 {
Signature · 1 line
2.13.6 (Apple Git-96)
#6 lore raw

Hi,

see comments inline

Quoted 36 lines · Arne Schwabe

This can be used to redirect all IPv6 traffic to the tun interface, effectively black holing the IPv6 traffic. Without ICMPv6 error messages this will result in timeouts when the server does not send error codes. block-ipv6 allows client side only blocking on all platforms that OpenVPN supports IPv6. On Android it is only way to do sensible IPv6 blocking on Android < 5.0 and broken devices (Samsung).

PATCH V3:
- Fix style iusses reported by Antonio and accidentily commited parts
- merge udp_checksum and ipv6_checkusm into common ip_checksum method
- Use fake ff80::7 address when no other address is configured.
- Make block-ipv6 also work for server by replying block-ipv6 to all ipv6 traffic send to the server

Note for the server the process_ip happens before the ipv6 route lookup so every ipv6 packet, regardless of its source address is replyied to with a no route to host packet.

---
 doc/openvpn.8         |  34 +++++++++++++
 src/openvpn/dhcp.c    |  49 ++-----------------
 src/openvpn/forward.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++++--
 src/openvpn/forward.h |   4 +-
 src/openvpn/multi.c   |   2 +-
 src/openvpn/options.c |  11 +++++
 src/openvpn/options.h |   1 +
 src/openvpn/proto.c   |  45 +++++++++++++++++
 src/openvpn/proto.h   |  52 ++++++++++++++++++++
 src/openvpn/socket.h  |  19 --------
 10 files changed, 278 insertions(+), 71 deletions(-)
doc/openvpn.8 +0 -0
index a4189ac2..9f5ae781 100644

doc/openvpn.8 Like \-\-redirect\-gateway, but omit actually changing the default · line 1239

Quoted 8 lines
doc/openvpn.8 +5 -0
@@ -1232,6 +1232,40 @@ Like \-\-redirect\-gateway, but omit actually changing the default
 gateway.  Useful when pushing private subnets.
 .\"*********************************************************
 .TP
+.B \-\-block\-ipv6
+On the client, instead of sending IPv6 packets over the VPN tunnel, all IPv6 packets are answered with
+an ICMPv6 no route host message. On the server, all IPv6 packets from clients are answered with an ICMPv6
+no route to host message. This options is intended for
+cases when IPv6 should be blocked and other options are not available.

The lines above are quite long compared to the rest of the manual. I'd suggest to shorten them.

Quoted 87 lines
doc/openvpn.8 +29 -0
+\.B \-\-block\-ipv6
+will use the remote IPv6 as source address of the ICMPv6 packets if set,
+otherwise will use fe80::7 as source address.
+
+For this option to make sense you actually have to route traffic to the tun interface.
+Following config block would send all IPv6 traffic to OpenVPN and answer all
+requests with no route to host, effectively blocking IPv6.
+
+# client config
+.br
+.B \-\-ifconfig-ipv6
+fd15:53b6:dead::2/64  fd15:53b6:dead::1
+.br
+.B \-\-redirect\-gateway
+ipv6
+.br
+.B \-\-block\-ipv6
+
+# Server config, push a "valid" ipv6 config to the client and block on the server
+.br
+.B \-\-push
+"ifconfig-ipv6 fd15:53b6:dead::2/64  fd15:53b6:dead::1"
+.br
+.B \-\-push
+"redirect\-gateway ipv6"
+.br
+.B \-\-block\-ipv6
+.\"*********************************************************
+.TP
 .B \-\-tun\-mtu n
 Take the TUN device MTU to be
 .B n
src/openvpn/dhcp.c +0 -43
index a2a54540..cec38cd6 100644
@@ -147,49 +147,6 @@ do_extract(struct dhcp *dhcp, int optlen)
     return ret;
 }
 
-static uint16_t
-udp_checksum(const uint8_t *buf,
-             const int len_udp,
-             const uint8_t *src_addr,
-             const uint8_t *dest_addr)
-{
-    uint16_t word16;
-    uint32_t sum = 0;
-    int i;
-
-    /* make 16 bit words out of every two adjacent 8 bit words and  */
-    /* calculate the sum of all 16 bit words */
-    for (i = 0; i < len_udp; i += 2)
-    {
-        word16 = ((buf[i] << 8) & 0xFF00) + ((i + 1 < len_udp) ? (buf[i+1] & 0xFF) : 0);
-        sum += word16;
-    }
-
-    /* add the UDP pseudo header which contains the IP source and destination addresses */
-    for (i = 0; i < 4; i += 2)
-    {
-        word16 = ((src_addr[i] << 8) & 0xFF00) + (src_addr[i+1] & 0xFF);
-        sum += word16;
-    }
-    for (i = 0; i < 4; i += 2)
-    {
-        word16 = ((dest_addr[i] << 8) & 0xFF00) + (dest_addr[i+1] & 0xFF);
-        sum += word16;
-    }
-
-    /* the protocol number and the length of the UDP packet */
-    sum += (uint16_t) OPENVPN_IPPROTO_UDP + (uint16_t) len_udp;
-
-    /* keep only the last 16 bits of the 32 bit calculated sum and add the carries */
-    while (sum >> 16)
-    {
-        sum = (sum & 0xFFFF) + (sum >> 16);
-    }
-
-    /* Take the one's complement of sum */
-    return ((uint16_t) ~sum);
-}
-
 in_addr_t
 dhcp_extract_router_msg(struct buffer *ipbuf)
 {

src/openvpn/dhcp.c dhcp_extract_router_msg(struct buffer *ipbuf) · line 170

Quoted 5 lines
src/openvpn/dhcp.c +1 -1
@@ -210,10 +167,10 @@ dhcp_extract_router_msg(struct buffer *ipbuf)
 
             /* recompute the UDP checksum */
             df->udp.check = 0;
-            df->udp.check = htons(udp_checksum((uint8_t *) &df->udp,
+            df->udp.check = htons(ip_checksum(AF_INET, (uint8_t *) &df->udp,

no space between the cast and the following variable.

src/openvpn/dhcp.c dhcp_extract_router_msg(struct buffer *ipbuf) · line 172

Quoted 4 lines
src/openvpn/dhcp.c +1 -2
                                                sizeof(struct openvpn_udphdr) + sizeof(struct dhcp) + optlen,
-                                               (uint8_t *)&df->ip.saddr,
-                                               (uint8_t *)&df->ip.daddr));
+                                               (uint8_t *)&df->ip.saddr, (uint8_t *)&df->ip.daddr,

lines above are longer than 80 chars and they should be aligned to the first character after the opening parenthesis.

Quoted 9 lines
src/openvpn/dhcp.c +1 -0
+                                               OPENVPN_IPPROTO_UDP));
 
             /* only return the extracted Router address if DHCPACK */
             if (message_type == DHCPACK)
src/openvpn/forward.c +0 -0
index 1b7455bb..36e0afe2 100644

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1201

Quoted 5 lines
src/openvpn/forward.c +1 -1
@@ -1198,7 +1198,7 @@ process_incoming_tun(struct context *c)
          * The --passtos and --mssfix options require
          * us to examine the IP header (IPv4 or IPv6).
          */
-        process_ip_header(c, PIPV4_PASSTOS|PIP_MSSFIX|PIPV4_CLIENT_NAT, &c->c2.buf);
+        process_ip_header(c, PIPV4_PASSTOS|PIP_MSSFIX|PIPV4_CLIENT_NAT|PIPV6_IMCP_NOHOST_CLIENT, &c->c2.buf);

this line is now longer than 80 chars. Please break it (even by using a temporary variable if needed - the compiler will optimize it anyway.

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1214

Quoted 10 lines
src/openvpn/forward.c +3 -0
 #ifdef PACKET_TRUNCATION_CHECK
         /* if (c->c2.buf.len > 1) --c->c2.buf.len; */
@@ -1209,6 +1209,9 @@ process_incoming_tun(struct context *c)
                                 &c->c2.n_trunc_pre_encrypt);
 #endif
 
+    }
+    if (c->c2.buf.len > 0)
+    {

is this related to the ipv6 change? If so, how?

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1230

Quoted 13 lines
src/openvpn/forward.c +6 -0
         encrypt_sign(c, true);
     }
     else
@@ -1219,6 +1222,117 @@ process_incoming_tun(struct context *c)
     gc_free(&gc);
 }
 
+/**
+ * Forges a IPv6 ICMP with a no route to host error code from the IPv6 packet in buf and sends it back via
+ * the tun device
+ */
+void
+ipv6_send_icmp_unreachable(struct context * c, struct buffer *buf, bool client)

no space after '*'

my understanding was that this patch was only for clients to block ipv6 packet *before* they get out. But now that I am reading the code I see that this function can be invoked in a client as well as in a server context.

How about clarifying that in the commit, and maybe also in the comment above the function?
Bonus: you could use the kerneldoc comment style (here or in the .h file)

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1232

Quoted 2 lines
src/openvpn/forward.c +2 -0
+{
+#define ICMPV6LEN  1280

isn't this available in any header? If not, I'd put MAX somewhere, to let people immediately understand what it is used for.

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1244

Quoted 12 lines
src/openvpn/forward.c +12 -0
+    struct openvpn_icmp6hdr icmp6out;
+    CLEAR(icmp6out);
+
+    /*
+     * Get a buffer to the ip packet, is_ipv6 automatically forwards
+     * the buffer to the ip packet
+     */
+    struct buffer inputipbuf = *buf;
+
+    is_ipv6(TUNNEL_TYPE(c->c1.tuntap), &inputipbuf);
+
+    if (BLEN(&inputipbuf) < (int) sizeof(struct openvpn_ipv6hdr))

no space between cast and following expression.

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1249

Quoted 5 lines
src/openvpn/forward.c +5 -0
+    {
+        return;
+    }
+
+    const struct openvpn_ipv6hdr* pip6 = (struct openvpn_ipv6hdr *) BPTR(&inputipbuf);

no space between cast and following expression.

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1251

Quoted 2 lines
src/openvpn/forward.c +2 -0
+
+    struct openvpn_ipv6hdr pip6out;

I know that we can declare variables basically everywhere, but don't you think this can quickly get out of control? Personally I'd just declare all of them at the top.

But I am not sure what the others think about it.

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1259

Quoted 8 lines
src/openvpn/forward.c +8 -0
+
+    /* Copy version, traffic class, flow label from input packet */
+    pip6out = *pip6;
+
+    pip6out.version_prio = pip6->version_prio;
+    pip6out.daddr = pip6->saddr;
+
+    /* Use the Ipv6 remote address if we have one, otherwise use a fake one */

niptick: keep the writing consistent: Ipv6 -> IPv6

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1263

Quoted 4 lines
src/openvpn/forward.c +4 -0
+    if (c->options.ifconfig_ipv6_remote)
+        inet_pton(AF_INET6, c->options.ifconfig_ipv6_remote, &pip6out.saddr);
+    else
+        inet_pton(AF_INET6, "fe80::7", &pip6out.saddr);

I remember we discussed this, but why not always using "fe80::7"? Maybe a comment explaining this would be helpful to avoid forgetting (I already did ..)?

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1269

Quoted 6 lines
src/openvpn/forward.c +6 -0
+
+    pip6out.nexthdr = OPENVPN_IPPROTO_ICMPV6;
+
+    /*
+     * The ICMPv6 unreachable code worked best in my tests with Windows, Linux and Android (arne)
+     * Windows did not like the adminitively prohibited return code (no fast fail)

adminitively -> administratively ?

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1279

Quoted 10 lines
src/openvpn/forward.c +10 -0
+     */
+    icmp6out.icmp6_type = OPENVPN_ICMP6_DESTINATION_UNREACHABLE;
+    icmp6out.icmp6_code = OPENVPN_ICMP6_DU_NOROUTE;
+
+    int icmpheader_len = sizeof(struct openvpn_ipv6hdr) + sizeof(struct openvpn_icmp6hdr);
+    int ethheader_len = sizeof(struct openvpn_ethhdr);
+    int totalheader_len = icmpheader_len;
+
+    if (TUNNEL_TYPE(c->c1.tuntap) == DEV_TYPE_TAP)
+        totalheader_len += ethheader_len;

why using ethheader_len ? it's used just in this line (sorry for this little complaints, but it's just an attempt t reduce the complexity of this long function)

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1283

Quoted 4 lines
src/openvpn/forward.c +4 -0
+
+    /*
+     * Calculate size for payload, defined in the standard that the resulting frame
+     * should <= 1280 and have as much as possible of the original packet

missing "be".

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1285

Quoted 2 lines
src/openvpn/forward.c +2 -0
+     */
+    int payload_len = min_inputipbufint(min_int(ICMPV6LEN, TUN_MTU_SIZE(&c->c2.frame) - icmpheader_len), buf_len(&inputipbuf));

this line is longer than 80 chars. You can easily split it in two parts (might also be easier to brain-parse).

and why not using BLEN() like you have done in other spots?

Quoted 15 lines
src/openvpn/forward.c +15 -0
+
+    pip6out.payload_len = htons(sizeof(struct openvpn_icmp6hdr) + payload_len);
+
+    /* Construct the packet as outgoing packet back to the client */
+    struct buffer * outbuf;
+    if (client) {
+        c->c2.to_tun = c->c2.buffers->aux_buf;
+        outbuf = &(c->c2.to_tun);
+    } else {
+        c->c2.to_link = c->c2.buffers->aux_buf;
+        outbuf = &(c->c2.to_link);
+    }
+    ASSERT(buf_init(outbuf, totalheader_len));
+
+    /* Fill the end of the buffer with original packet */

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1310

Quoted 10 lines
src/openvpn/forward.c +10 -0
+    ASSERT(buf_safe(outbuf, payload_len));
+    ASSERT(buf_copy_n(outbuf, &inputipbuf, payload_len));
+
+    /* ICMP Header, copy into buffer to allow checksum calculation */
+    ASSERT(buf_write_prepend(outbuf, &icmp6out, sizeof(struct openvpn_icmp6hdr)));
+
+    /* Calculate checksum over the packet and write to header */
+    ((struct openvpn_icmp6hdr*) BPTR(outbuf))->icmp6_cksum =
+            htons (ip_checksum(AF_INET6, BPTR(outbuf), BLEN(outbuf), (const uint8_t*) &pip6out.saddr,
+                               (uint8_t*) &pip6out.daddr, OPENVPN_IPPROTO_ICMPV6));

no space after cast or function call.

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1318

Quoted 8 lines
src/openvpn/forward.c +8 -0
+
+    /* IPv6 Header */
+    ASSERT(buf_write_prepend(outbuf, &pip6out, sizeof(struct openvpn_ipv6hdr)));
+
+    /*
+     * Working IPv6 block for TAP will also need the client to respond to IPv6 nd with
+     * its own (fake) adress
+     */

maybe I am missing something, but in the comment above you talk about answering to ND, but below you are just filling the Ethernet header. How are the two related?

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1321

Quoted 3 lines
src/openvpn/forward.c +3 -0
+    if (TUNNEL_TYPE(c->c1.tuntap) == DEV_TYPE_TAP)
+    {
+        if (BLEN(buf) < (int) sizeof (struct openvpn_ethhdr))

spaces..

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1333

Quoted 12 lines
src/openvpn/forward.c +12 -0
+            return;
+
+        const struct openvpn_ethhdr* orig_ethhdr = (struct openvpn_ethhdr *) BPTR(buf);
+
+        /* Copy frametype and reverse source/destination for the response */
+        struct openvpn_ethhdr ethhdr;
+        memcpy(ethhdr.source, orig_ethhdr->dest, OPENVPN_ETH_ALEN);
+        memcpy(ethhdr.dest, orig_ethhdr->source, OPENVPN_ETH_ALEN);
+        ethhdr.proto = htons(OPENVPN_ETH_P_IPV6);
+        ASSERT(buf_write_prepend(outbuf, &ethhdr, sizeof(struct openvpn_ethhdr)));
+    }
+}

Overall I was thinking: instead of creating many little objects (one per header) here and there and then copying them one by one in the allocated buffer, why not allocating the buffer first and writing directly into it? I believe that this is normal practice and might make the code a bit slimmer too. What do you think?

This is just a suggestion - I haven't tried to change the code myself.

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1335

Quoted 2 lines
src/openvpn/forward.c +2 -0
+
+

accidental double empty line?

src/openvpn/forward.c process_ip_header(struct context *c, unsigned int flags, struct buffer *buf) · line 1243

Quoted 8 lines
src/openvpn/forward.c +0 -1
 void
 process_ip_header(struct context *c, unsigned int flags, struct buffer *buf)
 {
@@ -1240,7 +1354,10 @@ process_ip_header(struct context *c, unsigned int flags, struct buffer *buf)
     {
         flags &= ~PIPV4_EXTRACT_DHCP_ROUTER;
     }
-

if conditions are not related it is ok to "unstick" them by keeping an empty line between them, it makes the code easier to read imho.

src/openvpn/forward.c process_ip_header(struct contexICMPV6LENt *c, unsigned int flags, struct buffer *buf) · line 1395

Quoted 13 lines
src/openvpn/forward.c +5 -1
+    if (!c->options.block_ipv6)
+    {
+        flags &= ~(PIPV6_IMCP_NOHOST_CLIENT|PIPV6_IMCP_NOHOST_SERVER);
+    }
     if (buf->len > 0)
     {
         /*
@@ -1275,7 +1392,7 @@ process_ip_header(struct contexICMPV6LENt *c, unsigned int flags, struct buffer *buf)
                 /* possibly do NAT on packet */
                 if ((flags & PIPV4_CLIENT_NAT) && c->options.client_nat)
                 {
-                    const int direction = (flags & PIPV4_OUTGOING) ? CN_INCOMING : CN_OUTGOING;
+                    const int direction = (flags & PIP_OUTGOING) ? CN_INCOMING : CN_OUTGOING;

how is this change related to this patch?

Quoted 167 lines
src/openvpn/forward.c +7 -0
                     client_nat_transform(c->options.client_nat, &ipbuf, direction);
                 }
                 /* possibly extract a DHCP router message */
@@ -1295,6 +1412,13 @@ process_ip_header(struct context *c, unsigned int flags, struct buffer *buf)
                 {
                     mss_fixup_ipv6(&ipbuf, MTU_TO_MSS(TUN_MTU_SIZE_DYNAMIC(&c->c2.frame)));
                 }IMCP
+                if (!(flags & PIP_OUTGOING) && (flags & (PIPV6_IMCP_NOHOST_CLIENT|PIPV6_IMCP_NOHOST_SERVER)))
 
 
+                {
+                    ipv6_send_icmp_unreachable(c, buf, (bool)(flags & PIPV6_IMCP_NOHOST_CLIENT));
+                    /* Drop the IPv6 packet */
+                    buf->len=0;
+                }
+
             }
         }
     }
src/openvpn/forward.c +1 -1
@@ -1478,7 +1602,7 @@ process_outgoing_tun(struct context *c)
      * The --mssfix option requires
      * us to examine the IP header (IPv4 or IPv6).
      */
-    process_ip_header(c, PIP_MSSFIX|PIPV4_EXTRACT_DHCP_ROUTER|PIPV4_CLIENT_NAT|PIPV4_OUTGOING, &c->c2.to_tun);
+    process_ip_header(c, PIP_MSSFIX|PIPV4_EXTRACT_DHCP_ROUTER|PIPV4_CLIENT_NAT|PIP_OUTGOING, &c->c2.to_tun);
 
     if (c->c2.to_tun.len <= MAX_RW_SIZE_TUN(&c->c2.frame))
     {
src/openvpn/forward.h +3 -1
index 9fde5a30..0fa4a31e 100644
@@ -251,9 +251,11 @@ bool send_control_channel_string(struct context *c, const char *str, int msgleve
 
 #define PIPV4_PASSTOS         (1<<0)
 #define PIP_MSSFIX            (1<<1)         /* v4 and v6 */
-#define PIPV4_OUTGOING        (1<<2)
+#define PIP_OUTGOING          (1<<2)
 #define PIPV4_EXTRACT_DHCP_ROUTER (1<<3)
 #define PIPV4_CLIENT_NAT      (1<<4)
+#define PIPV6_IMCP_NOHOST_CLIENT (1<<5)
+#define PIPV6_IMCP_NOHOST_SERVER (1<<6)
 
 void process_ip_header(struct context *c, unsigned int flags, struct buffer *buf);
 
src/openvpn/multi.c +1 -1
index 82a0b9d9..eeb7644a 100644
@@ -2837,7 +2837,7 @@ multi_get_queue(struct mbuf_set *ms)
 
     if (mbuf_extract_item(ms, &item)) /* cleartext IP packet */
     {
-        unsigned int pip_flags = PIPV4_PASSTOS;
+        unsigned int pip_flags = PIPV4_PASSTOS|PIPV6_IMCP_NOHOST_SERVER;
 
         set_prefix(item.instance);
         item.instance->context.c2.buf = item.buffer->buf;
src/openvpn/options.c +11 -0
index 641a26e2..d1b4f48c 100644
@@ -226,6 +226,8 @@ static const char usage_message[] =
     "                  Add 'bypass-dns' flag to similarly bypass tunnel for DNS.\n"
     "--redirect-private [flags]: Like --redirect-gateway, but omit actually changing\n"
     "                  the default gateway.  Useful when pushing private subnets.\n"
+    "--block-ipv6     : (client only) Instead sending IPv6 to the server generate\n"
+    "                   ICMPv6 host unreachable messages.\n"
     "--client-nat snat|dnat network netmask alias : on client add 1-to-1 NAT rule.\n"
 #ifdef ENABLE_PUSH_PEER_INFO
     "--push-peer-info : (client only) push client info to server.\n"
@@ -2084,6 +2086,10 @@ options_postprocess_verify_ce(const struct options *options, const struct connec
         msg(M_USAGE, "--lladdr can only be used in --dev tap mode");
     }
 
+    if (options->block_ipv6 && dev != DEV_TYPE_TUN)
+    {
+        msg(M_USAGE, "--block-ipv6 can be only used in --dev tun mode");
+    }
     /*
      * Sanity check on MTU parameters
      */
@@ -6360,6 +6366,11 @@ add_option(struct options *options,
 #endif
         options->routes->flags |= RG_ENABLE;
     }
+    else if (streq(p[0], "block-ipv6") && !p[1])
+    {
+        VERIFY_PERMISSION(OPT_P_ROUTE);
+        options->block_ipv6 = true;
+    }
     else if (streq(p[0], "remote-random-hostname") && !p[1])
     {
         VERIFY_PERMISSION(OPT_P_GENERAL);
src/openvpn/options.h +1 -0
index 496c1143..38e9e431 100644
@@ -340,6 +340,7 @@ struct options
     bool route_delay_defined;
     struct route_option_list *routes;
     struct route_ipv6_option_list *routes_ipv6;                 /* IPv6 */
+    bool block_ipv6;
     bool route_nopull;
     bool route_gateway_via_dhcp;
     bool allow_pull_fqdn; /* as a client, allow server to push a FQDN for certain parameters */
src/openvpn/proto.c +45 -0
index 2cbea3ab..4cafdd31 100644
@@ -98,6 +98,51 @@ is_ipv6(int tunnel_type, struct buffer *buf)
     return is_ipv_X( tunnel_type, buf, 6 );
 }
 
+
+uint16_t
+ip_checksum(const sa_family_t af, const uint8_t *payload, const int len_payload, const uint8_t *src_addr,
+            const uint8_t *dest_addr, const int proto)
+{
+    uint32_t sum = 0;
+    int addr_len = (af == AF_INET) ? 4 : 16;
+
+    /*
+     * make 16 bit words out of every two adjacent 8 bit words and  */
+    /* calculate the sum of all 16 bit words
+     */
+    for (int i = 0; i < len_payload; i += 2)
+    {
+        sum +=  (uint16_t) (((payload[i] << 8) & 0xFF00) + ((i + 1 < len_payload) ? (payload[i + 1] & 0xFF) : 0));
+
+    }
+
+    /* add the pseudo header which contains the IP source and destination addresses */
+    for (int i = 0; i < addr_len; i += 2)
+    {
+        sum += (uint16_t) ((src_addr[i] << 8) & 0xFF00) + (src_addr[i + 1] & 0xFF);
+
+    }
+    for (int i = 0; i < addr_len; i += 2)
+    {
+        sum += (uint16_t) ((dest_addr[i] << 8) & 0xFF00) + (dest_addr[i + 1] & 0xFF);
+    }
+
+    /* the length of the payload */
+    sum += (uint16_t) len_payload;
+
+    /* The next header or proto field*/
+    sum += (uint16_t) proto;
+
+    /* keep only the last 16 bits of the 32 bit calculated sum and add the carries */
+    while (sum >> 16)
+    {
+        sum = (sum & 0xFFFF) + (sum >> 16);
+    }
+
+    /* Take the one's complement of sum */
+    return ((uint16_t) ~sum);
+}
+
 #ifdef PACKET_TRUNCATION_CHECK
 
 void
src/openvpn/proto.h +0 -0
index 57f25c90..afbd226b 100644

src/openvpn/proto.h struct openvpn_iphdr { · line 101

Quoted 4 lines
src/openvpn/proto.h +1 -0
@@ -98,6 +98,7 @@ struct openvpn_iphdr {
 #define OPENVPN_IPPROTO_IGMP 2  /* IGMP protocol */
 #define OPENVPN_IPPROTO_TCP  6  /* TCP protocol */
 #define OPENVPN_IPPROTO_UDP 17  /* UDP protocol */
+#define OPENVPN_IPPROTO_ICMPV6 58 /* ICMPV6 protocol */

I believe the above defines were trying to have the value nicely aligned. I think you should rathe realign all the values.

Quoted 41 lines
src/openvpn/proto.h +27 -0
     uint8_t protocol;
 
     uint16_t check;
@@ -120,6 +121,24 @@ struct openvpn_ipv6hdr {
     struct  in6_addr daddr;
 };
 
+/*
+ * ICMPv6 header
+ */
+struct openvpn_icmp6hdr {
+# define OPENVPN_ICMP6_DESTINATION_UNREACHABLE 1
+# define OPENVPN_ND_ROUTER_SOLICIT	133
+# define OPENVPN_ND_ROUTER_ADVERT	134
+# define OPENVPN_ND_NEIGHBOR_SOLICIT	135
+# define OPENVPN_ND_NEIGHBOR_ADVERT	136
+# define OPENVPN_ND_INVERSE_SOLICIT	141
+# define OPENVPN_ND_INVERSE_ADVERT	142
+	uint8_t		icmp6_type;
+# define OPENVPN_ICMP6_DU_NOROUTE 0
+# define OPENVPN_ICMP6_DU_COMMUNICATION_PROHIBTED 1
+	uint8_t		icmp6_code;
+	uint16_t	icmp6_cksum;
+	uint8_t		icmp6_dataun[4];
+};
 
 /*
  * UDP header
@@ -265,6 +284,39 @@ bool is_ipv4(int tunnel_type, struct buffer *buf);
 
 bool is_ipv6(int tunnel_type, struct buffer *buf);
 
+static inline int
+af_addr_size(sa_family_t af)
+{
+    switch (af)
+    {
+        case AF_INET: return sizeof(struct sockaddr_in);
+
+        case AF_INET6: return sizeof(struct sockaddr_in6);
+

src/openvpn/proto.h bool is_ipv4(int tunnel_type, struct buffer *buf); · line 305

Quoted 10 lines
src/openvpn/proto.h +9 -0
+        default:
+#if 0
+            /* could be called from socket_do_accept() with empty addr */
+            msg(M_ERR, "Bad address family: %d\n", af);
+            ASSERT(0);
+#endif
 
+            return 0;
+    }
+}

the function above really needs some cleanup :( in a later patch

src/openvpn/proto.h bool is_ipv4(int tunnel_type, struct buffer *buf); · line 314

Quoted 10 lines
src/openvpn/proto.h +10 -0
+
+/**
+ *  Calculates an IP or IPv6 checksum with a pseudo header as required by TCP, UDP and ICMPv6
+ * @param af Address family for which the checksum is calculated AF_INET or AF_INET6
+ * @param payload the TCP, ICMPv6 or UDP packet
+ * @param len_payload length of payload
+ * @param src_addr Source address of the packet
+ * @param dest_addr Destination address of the packet
+ * @param proto next header or IP protocol of the packet
+ * @return calculated checksum in host order

The doxygen style doc above does not look like the rest. Please check ssl_backend.h for a good example.

src/openvpn/proto.h bool is_ipv4(int tunnel_type, struct buffer *buf); · line 319

Quoted 5 lines
src/openvpn/proto.h +5 -0
+ */
+uint16_t
+ip_checksum(const sa_family_t af, const uint8_t *payload, const int len_payload, const uint8_t *src_addr,
+            const uint8_t *dest_addr,  const int proto);
+

And don't forget the max line length

Quoted 24 lines
src/openvpn/proto.h +0 -0
 #ifdef PACKET_TRUNCATION_CHECK
 void ipv4_packet_size_verify(const uint8_t *data,

const int size,

src/openvpn/socket.h +0 -12
index 2d7f2187..9ccc9466 100644
@@ -848,25 +848,6 @@ addr_inet4or6(struct sockaddr *addr)
 
 int addr_guess_family(sa_family_t af,const char *name);
 
-static inline int
-af_addr_size(sa_family_t af)
-{
-    switch (af)
-    {
-        case AF_INET: return sizeof(struct sockaddr_in);
-
-        case AF_INET6: return sizeof(struct sockaddr_in6);
-
-        default:
-#if 0
-            /* could be called from socket_do_accept() with empty addr */

src/openvpn/socket.h addr_inet4or6(struct sockaddr *addr) · line 853

Quoted 10 lines
src/openvpn/socket.h +0 -7
-            msg(M_ERR, "Bad address family: %d\n", af);
-            ASSERT(0);
-#endif
-            return 0;
-    }
-}
-
 static inline bool
 link_socket_actual_match(const struct link_socket_actual *a1, const struct link_socket_actual *a2)
 {

in case of doubt about any of my points, we can also discuss on IRC.

Cheers,

Signature · 1 line
Antonio Quartulli
#7 lore raw
Quoted 6 lines

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1214

Quoted 4 lines
src/openvpn/forward.c +3 -0
+    }
+    if (c->c2.buf.len > 0)
+    {

is this related to the ipv6 change? If so, how?

To drop packet OpenVPN generally sets the buf len to zero. Since we now also drop packets that would normally go from client to server, I added the check here so these packets can be dropped.

Quoted 5 lines

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1232

Quoted 2 lines
src/openvpn/forward.c +2 -0
+{
+#define ICMPV6LEN  1280

isn't this available in any header? If not, I'd put MAX somewhere, to let people immediately understand what it is used for.

No and that constant is specific to the icmpv6 code so I kept in this function.

Quoted 9 lines

Review

Quoted 3 lines
(quoted diff) +2 -0
+
+    struct openvpn_ipv6hdr pip6out;

I know that we can declare variables basically everywhere, but don't you think this can quickly get out of control? Personally I'd just declare all of them at the top.

But I am not sure what the others think about it.

I inlined that declaration to make it more C99 style and look a bit better..

Quoted 39 lines

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1318

Quoted 9 lines
src/openvpn/forward.c +8 -0
+
+    /* IPv6 Header */
+    ASSERT(buf_write_prepend(outbuf, &pip6out, sizeof(struct openvpn_ipv6hdr)));
+
+    /*
+     * Working IPv6 block for TAP will also need the client to respond to IPv6 nd with
+     * its own (fake) adress
+     */

maybe I am missing something, but in the comment above you talk about answering to ND, but below you are just filling the Ethernet header. How are the two related?

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1321

Quoted 3 lines
src/openvpn/forward.c +3 -0
+    if (TUNNEL_TYPE(c->c1.tuntap) == DEV_TYPE_TAP)
+    {
+        if (BLEN(buf) < (int) sizeof (struct openvpn_ethhdr))

spaces..

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1333

Quoted 12 lines
src/openvpn/forward.c +12 -0
+            return;
+
+        const struct openvpn_ethhdr* orig_ethhdr = (struct openvpn_ethhdr *) BPTR(buf);
+
+        /* Copy frametype and reverse source/destination for the response */
+        struct openvpn_ethhdr ethhdr;
+        memcpy(ethhdr.source, orig_ethhdr->dest, OPENVPN_ETH_ALEN);
+        memcpy(ethhdr.dest, orig_ethhdr->source, OPENVPN_ETH_ALEN);
+        ethhdr.proto = htons(OPENVPN_ETH_P_IPV6);
+        ASSERT(buf_write_prepend(outbuf, &ethhdr, sizeof(struct openvpn_ethhdr)));
+    }
+}

Overall I was thinking: instead of creating many little objects (one per header) here and there and then copying them one by one in the allocated buffer, why not allocating the buffer first and writing directly into it? I believe that this is normal practice and might make the code a bit slimmer too. What do you think?

Quoted 1 line

This is just a suggestion - I haven't tried to change the code myself.

I don't think that it would make the code much easier to to read. Allocating the whole len in the buffer at the start and then defining the structs as pointers at certain offsets in the buffer, is probably not easier to understand. And since this is not a critical code path I think the speed argument also does not count here. I would prefer to keep it like it currently is.

Quoted 10 lines

src/openvpn/forward.c process_ip_header(struct contexICMPV6LENt *c, unsigned int flags, struct buffer *buf) · line 1395

Quoted 8 lines
     {
         /*
src/openvpn/forward.c +1 -1
@@ -1275,7 +1392,7 @@ process_ip_header(struct contexICMPV6LENt *c, unsigned int flags, struct buffer *buf)
                 /* possibly do NAT on packet */
                 if ((flags & PIPV4_CLIENT_NAT) && c->options.client_nat)
                 {
-                    const int direction = (flags & PIPV4_OUTGOING) ? CN_INCOMING : CN_OUTGOING;
+                    const int direction = (flags & PIP_OUTGOING) ? CN_INCOMING : CN_OUTGOING;

how is this change related to this patch?

Since PIP_OUTGOING is not ipv4 specific anymore thanks to this patch, I renamed the constant to PIP_OUTGOING.

For all the other comments I that I did not answer, I integrated them in the V4 patch.

Arne

#8 lore raw

PATCH v4 1/1 Implement block-ipv6

In reply to Antonio Quartulli

This can be used to redirect all IPv6 traffic to the tun interface, effectively black holing the IPv6 traffic. Without ICMPv6 error messages this will result in timeouts when the server does not send error codes. block-ipv6 allows client side only blocking on all platforms that OpenVPN supports IPv6. On Android it is only way to do sensible IPv6 blocking on Android < 5.0 and broken devices (Samsung).

PATCH V4:
- Fix more style issues reported by Antonio
- Clarify parts of the patch in comments and manpage

PATCH V3:
- Fix style iusses reported by Antonio and accidentily commited parts
- merge udp_checksum and ipv6_checkusm into common ip_checksum method
- Use fake ff80::7 address when no other address is configured.
- Make block-ipv6 also work for server by replying block-ipv6 to all ipv6 traffic send to the server

Note for the server the process_ip happens before the ipv6 route lookup so every ipv6 packet, regardless of its source address is replyied to with a no route to host packet.

---
 doc/openvpn.8         |  36 +++++++++++
 src/openvpn/dhcp.c    |  51 ++--------------
 src/openvpn/forward.c | 138 +++++++++++++++++++++++++++++++++++++++++-
 src/openvpn/forward.h |   4 +-
 src/openvpn/multi.c   |   2 +-
 src/openvpn/options.c |  11 ++++
 src/openvpn/options.h |   1 +
 src/openvpn/proto.c   |  45 ++++++++++++++
 src/openvpn/proto.h   |  52 ++++++++++++++++
 src/openvpn/socket.h  |  19 ------
 10 files changed, 288 insertions(+), 71 deletions(-)
doc/openvpn.8 +36 -0
index 8a987b37..bad66754 100644
@@ -1240,6 +1240,42 @@ Like \-\-redirect\-gateway, but omit actually changing the default
 gateway.  Useful when pushing private subnets.
 .\"*********************************************************
 .TP
+.B \-\-block\-ipv6
+On the client, instead of sending IPv6 packets over the VPN tunnel, all
+IPv6 packets are answered with an ICMPv6 no route host message. On the
+server, all IPv6 packets from clients are answered with an ICMPv6
+no route to host message. This options is intended for cases when IPv6
+should be blocked and other options are not available.
+\.B \-\-block\-ipv6
+will use the remote IPv6 as source address of the ICMPv6 packets if set,
+otherwise will use fe80::7 as source address.
+
+For this option to make sense you actually have to route traffic to the tun
+interface. The following example config block would send all IPv6 traffic to
+OpenVPN and answer all requests with no route to host, effectively blocking
+IPv6.
+
+# client config
+.br
+.B \-\-ifconfig-ipv6
+fd15:53b6:dead::2/64  fd15:53b6:dead::1
+.br
+.B \-\-redirect\-gateway
+ipv6
+.br
+.B \-\-block\-ipv6
+
+# Server config, push a "valid" ipv6 config to the client and block on the server
+.br
+.B \-\-push
+"ifconfig-ipv6 fd15:53b6:dead::2/64  fd15:53b6:dead::1"
+.br
+.B \-\-push
+"redirect\-gateway ipv6"
+.br
+.B \-\-block\-ipv6
+.\"*********************************************************
+.TP
 .B \-\-tun\-mtu n
 Take the TUN device MTU to be
 .B n
src/openvpn/dhcp.c +4 -47

63 lines not on this page · raw message

src/openvpn/forward.c +135 -3

180 lines not on this page · raw message

src/openvpn/forward.h +3 -1
index 924cc5e2..6d0414f6 100644
@@ -251,9 +251,11 @@ bool send_control_channel_string(struct context *c, const char *str, int msgleve
 
 #define PIPV4_PASSTOS         (1<<0)
 #define PIP_MSSFIX            (1<<1)         /* v4 and v6 */
-#define PIPV4_OUTGOING        (1<<2)
+#define PIP_OUTGOING          (1<<2)
 #define PIPV4_EXTRACT_DHCP_ROUTER (1<<3)
 #define PIPV4_CLIENT_NAT      (1<<4)
+#define PIPV6_IMCP_NOHOST_CLIENT (1<<5)
+#define PIPV6_IMCP_NOHOST_SERVER (1<<6)
 
 void process_ip_header(struct context *c, unsigned int flags, struct buffer *buf);
 
src/openvpn/multi.c +1 -1
index 3da8c110..fcb54f19 100644
@@ -2838,7 +2838,7 @@ multi_get_queue(struct mbuf_set *ms)
 
     if (mbuf_extract_item(ms, &item)) /* cleartext IP packet */
     {
-        unsigned int pip_flags = PIPV4_PASSTOS;
+        unsigned int pip_flags = PIPV4_PASSTOS|PIPV6_IMCP_NOHOST_SERVER;
 
         set_prefix(item.instance);
         item.instance->context.c2.buf = item.buffer->buf;
src/openvpn/options.c +11 -0
index 11b9dc15..d2944ccb 100644
@@ -224,6 +224,8 @@ static const char usage_message[] =
     "                  Add 'bypass-dns' flag to similarly bypass tunnel for DNS.\n"
     "--redirect-private [flags]: Like --redirect-gateway, but omit actually changing\n"
     "                  the default gateway.  Useful when pushing private subnets.\n"
+    "--block-ipv6     : (client only) Instead sending IPv6 to the server generate\n"
+    "                   ICMPv6 host unreachable messages.\n"
     "--client-nat snat|dnat network netmask alias : on client add 1-to-1 NAT rule.\n"
     "--push-peer-info : (client only) push client info to server.\n"
     "--setenv name value : Set a custom environmental variable to pass to script.\n"
@@ -2085,6 +2087,10 @@ options_postprocess_verify_ce(const struct options *options, const struct connec
         msg(M_USAGE, "--lladdr can only be used in --dev tap mode");
     }
 
+    if (options->block_ipv6 && dev != DEV_TYPE_TUN)
+    {
+        msg(M_USAGE, "--block-ipv6 can be only used in --dev tun mode");
+    }
     /*
      * Sanity check on MTU parameters
      */
@@ -6336,6 +6342,11 @@ add_option(struct options *options,
 #endif
         options->routes->flags |= RG_ENABLE;
     }
+    else if (streq(p[0], "block-ipv6") && !p[1])
+    {
+        VERIFY_PERMISSION(OPT_P_ROUTE);
+        options->block_ipv6 = true;
+    }
     else if (streq(p[0], "remote-random-hostname") && !p[1])
     {
         VERIFY_PERMISSION(OPT_P_GENERAL);
src/openvpn/options.h +1 -0
index 3a6c33f8..8cfc95fb 100644
@@ -336,6 +336,7 @@ struct options
     bool route_delay_defined;
     struct route_option_list *routes;
     struct route_ipv6_option_list *routes_ipv6;                 /* IPv6 */
+    bool block_ipv6;
     bool route_nopull;
     bool route_gateway_via_dhcp;
     bool allow_pull_fqdn; /* as a client, allow server to push a FQDN for certain parameters */
src/openvpn/proto.c +45 -0

51 lines not on this page · raw message

src/openvpn/proto.h +52 -0

70 lines not on this page · raw message

src/openvpn/socket.h +0 -19
index 479d1150..294dddef 100644
@@ -848,25 +848,6 @@ addr_inet4or6(struct sockaddr *addr)
 
 int addr_guess_family(sa_family_t af,const char *name);
 
-static inline int
-af_addr_size(sa_family_t af)
-{
-    switch (af)
-    {
-        case AF_INET: return sizeof(struct sockaddr_in);
-
-        case AF_INET6: return sizeof(struct sockaddr_in6);
-
-        default:
-#if 0
-            /* could be called from socket_do_accept() with empty addr */
-            msg(M_ERR, "Bad address family: %d\n", af);
-            ASSERT(0);
-#endif
-            return 0;
-    }
-}
-
 static inline bool
 link_socket_actual_match(const struct link_socket_actual *a1, const struct link_socket_actual *a2)
 {
Signature · 1 line
2.17.1
#9 lore raw

Hi,

Quoted 136 lines · Arne Schwabe

This can be used to redirect all IPv6 traffic to the tun interface, effectively black holing the IPv6 traffic. Without ICMPv6 error messages this will result in timeouts when the server does not send error codes. block-ipv6 allows client side only blocking on all platforms that OpenVPN supports IPv6. On Android it is only way to do sensible IPv6 blocking on Android < 5.0 and broken devices (Samsung).

PATCH V4:
- Fix more style issues reported by Antonio
- Clarify parts of the patch in comments and manpage

PATCH V3:
- Fix style iusses reported by Antonio and accidentily commited parts
- merge udp_checksum and ipv6_checkusm into common ip_checksum method
- Use fake ff80::7 address when no other address is configured.
- Make block-ipv6 also work for server by replying block-ipv6 to all ipv6 traffic send to the server

Note for the server the process_ip happens before the ipv6 route lookup so every ipv6 packet, regardless of its source address is replyied to with a no route to host packet.

---
 doc/openvpn.8         |  36 +++++++++++
 src/openvpn/dhcp.c    |  51 ++--------------
 src/openvpn/forward.c | 138 +++++++++++++++++++++++++++++++++++++++++-
 src/openvpn/forward.h |   4 +-
 src/openvpn/multi.c   |   2 +-
 src/openvpn/options.c |  11 ++++
 src/openvpn/options.h |   1 +
 src/openvpn/proto.c   |  45 ++++++++++++++
 src/openvpn/proto.h   |  52 ++++++++++++++++
 src/openvpn/socket.h  |  19 ------
 10 files changed, 288 insertions(+), 71 deletions(-)
doc/openvpn.8 +36 -0
index 8a987b37..bad66754 100644
@@ -1240,6 +1240,42 @@ Like \-\-redirect\-gateway, but omit actually changing the default
 gateway.  Useful when pushing private subnets.
 .\"*********************************************************
 .TP
+.B \-\-block\-ipv6
+On the client, instead of sending IPv6 packets over the VPN tunnel, all
+IPv6 packets are answered with an ICMPv6 no route host message. On the
+server, all IPv6 packets from clients are answered with an ICMPv6
+no route to host message. This options is intended for cases when IPv6
+should be blocked and other options are not available.
+\.B \-\-block\-ipv6
+will use the remote IPv6 as source address of the ICMPv6 packets if set,
+otherwise will use fe80::7 as source address.
+
+For this option to make sense you actually have to route traffic to the tun
+interface. The following example config block would send all IPv6 traffic to
+OpenVPN and answer all requests with no route to host, effectively blocking
+IPv6.
+
+# client config
+.br
+.B \-\-ifconfig-ipv6
+fd15:53b6:dead::2/64  fd15:53b6:dead::1
+.br
+.B \-\-redirect\-gateway
+ipv6
+.br
+.B \-\-block\-ipv6
+
+# Server config, push a "valid" ipv6 config to the client and block on the server
+.br
+.B \-\-push
+"ifconfig-ipv6 fd15:53b6:dead::2/64  fd15:53b6:dead::1"
+.br
+.B \-\-push
+"redirect\-gateway ipv6"
+.br
+.B \-\-block\-ipv6
+.\"*********************************************************
+.TP
 .B \-\-tun\-mtu n
 Take the TUN device MTU to be
 .B n
src/openvpn/dhcp.c +0 -43
index fb28b27d..24c45c76 100644
@@ -147,49 +147,6 @@ do_extract(struct dhcp *dhcp, int optlen)
     return ret;
 }
 
-static uint16_t
-udp_checksum(const uint8_t *buf,
-             const int len_udp,
-             const uint8_t *src_addr,
-             const uint8_t *dest_addr)
-{
-    uint16_t word16;
-    uint32_t sum = 0;
-    int i;
-
-    /* make 16 bit words out of every two adjacent 8 bit words and  */
-    /* calculate the sum of all 16 bit words */
-    for (i = 0; i < len_udp; i += 2)
-    {
-        word16 = ((buf[i] << 8) & 0xFF00) + ((i + 1 < len_udp) ? (buf[i+1] & 0xFF) : 0);
-        sum += word16;
-    }
-
-    /* add the UDP pseudo header which contains the IP source and destination addresses */
-    for (i = 0; i < 4; i += 2)
-    {
-        word16 = ((src_addr[i] << 8) & 0xFF00) + (src_addr[i+1] & 0xFF);
-        sum += word16;
-    }
-    for (i = 0; i < 4; i += 2)
-    {
-        word16 = ((dest_addr[i] << 8) & 0xFF00) + (dest_addr[i+1] & 0xFF);
-        sum += word16;
-    }
-
-    /* the protocol number and the length of the UDP packet */
-    sum += (uint16_t) OPENVPN_IPPROTO_UDP + (uint16_t) len_udp;
-
-    /* keep only the last 16 bits of the 32 bit calculated sum and add the carries */
-    while (sum >> 16)
-    {
-        sum = (sum & 0xFFFF) + (sum >> 16);
-    }
-
-    /* Take the one's complement of sum */
-    return ((uint16_t) ~sum);
-}
-
 in_addr_t
 dhcp_extract_router_msg(struct buffer *ipbuf)
 {

src/openvpn/dhcp.c dhcp_extract_router_msg(struct buffer *ipbuf) · line 170

Quoted 8 lines
src/openvpn/dhcp.c +1 -4
@@ -210,10 +167,10 @@ dhcp_extract_router_msg(struct buffer *ipbuf)
 
             /* recompute the UDP checksum */
             df->udp.check = 0;
-            df->udp.check = htons(udp_checksum((uint8_t *) &df->udp,
-                                               sizeof(struct openvpn_udphdr) + sizeof(struct dhcp) + optlen,
-                                               (uint8_t *)&df->ip.saddr,
-                                               (uint8_t *)&df->ip.daddr));
+            df->udp.check = htons(ip_checksum(AF_INET, (uint8_t *) &df->udp,

please remove the space after the cast.

Quoted 33 lines
src/openvpn/dhcp.c +3 -0
+                                  sizeof(struct openvpn_udphdr) + sizeof(struct dhcp) + optlen,
+                                  (uint8_t *)&df->ip.saddr, (uint8_t *)&df->ip.daddr,
+                                  OPENVPN_IPPROTO_UDP));
 
             /* only return the extracted Router address if DHCPACK */
             if (message_type == DHCPACK)
src/openvpn/forward.c +5 -1
index 00112275..d3e6eede 100644
@@ -1194,7 +1194,8 @@ process_incoming_tun(struct context *c)
          * The --passtos and --mssfix options require
          * us to examine the IP header (IPv4 or IPv6).
          */
-        process_ip_header(c, PIPV4_PASSTOS|PIP_MSSFIX|PIPV4_CLIENT_NAT, &c->c2.buf);
+        unsigned int flags = PIPV4_PASSTOS|PIP_MSSFIX|PIPV4_CLIENT_NAT|PIPV6_IMCP_NOHOST_CLIENT;
+        process_ip_header(c, flags, &c->c2.buf);
 
 #ifdef PACKET_TRUNCATION_CHECK
         /* if (c->c2.buf.len > 1) --c->c2.buf.len; */
@@ -1205,6 +1206,9 @@ process_incoming_tun(struct context *c)
                                 &c->c2.n_trunc_pre_encrypt);
 #endif
 
+    }
+    if (c->c2.buf.len > 0)
+    {
         encrypt_sign(c, true);
     }
     else
@@ -1215,6 +1219,123 @@ process_incoming_tun(struct context *c)
     gc_free(&gc);
 }

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1230

Quoted 10 lines
src/openvpn/forward.c +9 -0
 
+/**
+ * Forges a IPv6 ICMP packet with a no route to host error code from the
+ * IPv6 packetin buf and sends it directly back to the client via the tun device
+ * when used on a client and via the link if used on the server.
+ *
+ * @param buf The buf containing the packet for which the icmp6 unreachable
+ *              should be contrusted.
+ *
+ *  @param client determines whether to the send packet back via tun or link

This @param is misaligned. On top of that, we normally use a tab to separate the description and we align all the descriptions. Check ssl_backend.h as a reference.

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1235

Quoted 5 lines
src/openvpn/forward.c +5 -0
+ */
+void
+ipv6_send_icmp_unreachable(struct context *c, struct buffer *buf, bool client)
+{
+#define MAX_ICMPV6LEN  1280

We have

/usr/include/linux/ipv6.h:11:#define IPV6_MIN_MTU 1280

Don't you think it may make sense to define ICMPV6LEN as IPV6_MIN_MTU to give it a better meaning?

Quoted 17 lines
src/openvpn/forward.c +17 -0
+    struct openvpn_icmp6hdr icmp6out;
+    CLEAR(icmp6out);
+
+    /*
+     * Get a buffer to the ip packet, is_ipv6 automatically forwards
+     * the buffer to the ip packet
+     */
+    struct buffer inputipbuf = *buf;
+
+    is_ipv6(TUNNEL_TYPE(c->c1.tuntap), &inputipbuf);
+
+    if (BLEN(&inputipbuf) < (int)sizeof(struct openvpn_ipv6hdr))
+    {
+        return;
+    }
+
+    const struct openvpn_ipv6hdr* pip6 = (struct openvpn_ipv6hdr *)BPTR(&inputipbuf);

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1262

Quoted 10 lines
src/openvpn/forward.c +10 -0
+
+    /* Copy version, traffic class, flow label from input packet */
+    struct openvpn_ipv6hdr pip6out = *pip6;
+
+    pip6out.version_prio = pip6->version_prio;
+    pip6out.daddr = pip6->saddr;
+
+    /* Use the IPv6 remote address if we have one, otherwise use a fake one
+     * using the remote address is prefered since it makes debugging and understanding
+     * where the ICMPv6 error originates easier */

Keep the multiline comment style consistent. Put the final '*/' on a new line please.

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1266

Quoted 4 lines
src/openvpn/forward.c +4 -0
+    if (c->options.ifconfig_ipv6_remote)
+        inet_pton(AF_INET6, c->options.ifconfig_ipv6_remote, &pip6out.saddr);
+    else
+        inet_pton(AF_INET6, "fe80::7", &pip6out.saddr);

We agreed on using curly brackets also for branches having one line only.

Quoted 13 lines
src/openvpn/forward.c +13 -0
+
+    pip6out.nexthdr = OPENVPN_IPPROTO_ICMPV6;
+
+    /*
+     * The ICMPv6 unreachable code worked best in my tests with Windows, Linux and Android (arne)
+     * Windows did not like the administratively prohibited return code (no fast fail)
+     */
+    icmp6out.icmp6_type = OPENVPN_ICMP6_DESTINATION_UNREACHABLE;
+    icmp6out.icmp6_code = OPENVPN_ICMP6_DU_NOROUTE;
+
+    int icmpheader_len = sizeof(struct openvpn_ipv6hdr) + sizeof(struct openvpn_icmp6hdr);
+    int totalheader_len = icmpheader_len;
+

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1289

Quoted 10 lines
src/openvpn/forward.c +10 -0
+    if (TUNNEL_TYPE(c->c1.tuntap) == DEV_TYPE_TAP)
+        totalheader_len += sizeof(struct openvpn_ethhdr);
+
+    /*
+     * Calculate size for payload, defined in the standard that the resulting frame
+     * should be <= 1280 and have as much as possible of the original packet
+     */
+    int payload_len = min_int(
+        min_int(MAX_ICMPV6LEN, TUN_MTU_SIZE(&c->c2.frame) - icmpheader_len),
+        BLEN(&inputipbuf));

This is kind of funky to read - how about adding another temp variable instead of writing all those operations within the min_int() call ? Temp variable names can help understanding what you are doing and they are all optimized out by the compiler anyway (in most of the cases).

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1294

Quoted 5 lines
src/openvpn/forward.c +5 -0
+
+    pip6out.payload_len = htons(sizeof(struct openvpn_icmp6hdr) + payload_len);
+
+    /* Construct the packet as outgoing packet back to the client */
+    struct buffer * outbuf;

No space after *

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1295

Quoted 1 line
src/openvpn/forward.c +1 -0
+    if (client) {

bracket goes on the new line

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1298

Quoted 3 lines
src/openvpn/forward.c +3 -0
+        c->c2.to_tun = c->c2.buffers->aux_buf;
+        outbuf = &(c->c2.to_tun);
+    } else {

bracket goes on the new line

Quoted 6 lines
src/openvpn/forward.c +6 -0
+        c->c2.to_link = c->c2.buffers->aux_buf;
+        outbuf = &(c->c2.to_link);
+    }
+    ASSERT(buf_init(outbuf, totalheader_len));
+
+    /* Fill the end of the buffer with original packet */

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1314

Quoted 10 lines
src/openvpn/forward.c +10 -0
+    ASSERT(buf_safe(outbuf, payload_len));
+    ASSERT(buf_copy_n(outbuf, &inputipbuf, payload_len));
+
+    /* ICMP Header, copy into buffer to allow checksum calculation */
+    ASSERT(buf_write_prepend(outbuf, &icmp6out, sizeof(struct openvpn_icmp6hdr)));
+
+    /* Calculate checksum over the packet and write to header */
+    ((struct openvpn_icmp6hdr*) BPTR(outbuf))->icmp6_cksum =
+            htons(ip_checksum(AF_INET6, BPTR(outbuf), BLEN(outbuf), (const uint8_t*)&pip6out.saddr,
+                               (uint8_t*)&pip6out.daddr, OPENVPN_IPPROTO_ICMPV6));

not sure about this, but maybe you can use the same temp variable trick I suggested above?

src/openvpn/forward.c process_incoming_tun(struct context *c) · line 1325

Quoted 11 lines
src/openvpn/forward.c +11 -0
+
+    /* IPv6 Header */
+    ASSERT(buf_write_prepend(outbuf, &pip6out, sizeof(struct openvpn_ipv6hdr)));
+
+    /*
+     * Tap mode, we also need to create an Ethernet header.
+     */
+    if (TUNNEL_TYPE(c->c1.tuntap) == DEV_TYPE_TAP)
+    {
+        if (BLEN(buf) < (int)sizeof(struct openvpn_ethhdr))
+            return;

brackets

Quoted 37 lines
src/openvpn/forward.c +18 -1
+
+        const struct openvpn_ethhdr* orig_ethhdr = (struct openvpn_ethhdr *) BPTR(buf);
+
+        /* Copy frametype and reverse source/destination for the response */
+        struct openvpn_ethhdr ethhdr;
+        memcpy(ethhdr.source, orig_ethhdr->dest, OPENVPN_ETH_ALEN);
+        memcpy(ethhdr.dest, orig_ethhdr->source, OPENVPN_ETH_ALEN);
+        ethhdr.proto = htons(OPENVPN_ETH_P_IPV6);
+        ASSERT(buf_write_prepend(outbuf, &ethhdr, sizeof(struct openvpn_ethhdr)));
+    }
+#undef MAX_ICMPV6LEN
+}
+
 void
 process_ip_header(struct context *c, unsigned int flags, struct buffer *buf)
 {
@@ -1236,6 +1357,10 @@ process_ip_header(struct context *c, unsigned int flags, struct buffer *buf)
     {
         flags &= ~PIPV4_EXTRACT_DHCP_ROUTER;
     }
+    if (!c->options.block_ipv6)
+    {
+        flags &= ~(PIPV6_IMCP_NOHOST_CLIENT|PIPV6_IMCP_NOHOST_SERVER);
+    }
 
     if (buf->len > 0)
     {
@@ -1271,7 +1396,7 @@ process_ip_header(struct context *c, unsigned int flags, struct buffer *buf)
                 /* possibly do NAT on packet */
                 if ((flags & PIPV4_CLIENT_NAT) && c->options.client_nat)
                 {
-                    const int direction = (flags & PIPV4_OUTGOING) ? CN_INCOMING : CN_OUTGOING;
+                    const int direction = (flags & PIP_OUTGOING) ? CN_INCOMING : CN_OUTGOING;
                     client_nat_transform(c->options.client_nat, &ipbuf, direction);
                 }
                 /* possibly extract a DHCP router message */

src/openvpn/forward.c process_ip_header(struct context *c, unsigned int flags, struct buffer *buf) · line 1419

Quoted 4 lines
src/openvpn/forward.c +1 -0
@@ -1291,6 +1416,13 @@ process_ip_header(struct context *c, unsigned int flags, struct buffer *buf)
                 {
                     mss_fixup_ipv6(&ipbuf, MTU_TO_MSS(TUN_MTU_SIZE_DYNAMIC(&c->c2.frame)));
                 }
+                if (!(flags & PIP_OUTGOING) && (flags & (PIPV6_IMCP_NOHOST_CLIENT|PIPV6_IMCP_NOHOST_SERVER)))

the | should be surrounded by spaces.

Quoted 10 lines
src/openvpn/forward.c +6 -0
+                {
+                    ipv6_send_icmp_unreachable(c, buf, (bool)(flags & PIPV6_IMCP_NOHOST_CLIENT));
+                    /* Drop the IPv6 packet */
+                    buf->len=0;
+                }
+
             }
         }
     }

src/openvpn/forward.c process_outgoing_tun(struct context *c) · line 1606

Quoted 5 lines
src/openvpn/forward.c +1 -1
@@ -1471,7 +1603,7 @@ process_outgoing_tun(struct context *c)
      * The --mssfix option requires
      * us to examine the IP header (IPv4 or IPv6).
      */
-    process_ip_header(c, PIP_MSSFIX|PIPV4_EXTRACT_DHCP_ROUTER|PIPV4_CLIENT_NAT|PIPV4_OUTGOING, &c->c2.to_tun);
+    process_ip_header(c, PIP_MSSFIX|PIPV4_EXTRACT_DHCP_ROUTER|PIPV4_CLIENT_NAT|PIP_OUTGOING, &c->c2.to_tun);

same here. I know a lot of these things were there already, but I think while touching the same line it is good to fix little style hitchups like this.

Quoted 8 lines
src/openvpn/forward.c +0 -0
     if (c->c2.to_tun.len <= MAX_RW_SIZE_TUN(&c->c2.frame))
     {
src/openvpn/forward.h +0 -0
index 924cc5e2..6d0414f6 100644

src/openvpn/forward.h bool send_control_channel_string(struct context *c, const char *str, int msgleve · line 255

Quoted 6 lines
src/openvpn/forward.h +1 -1
@@ -251,9 +251,11 @@ bool send_control_channel_string(struct context *c, const char *str, int msgleve
 
 #define PIPV4_PASSTOS         (1<<0)
 #define PIP_MSSFIX            (1<<1)         /* v4 and v6 */
-#define PIPV4_OUTGOING        (1<<2)
+#define PIP_OUTGOING          (1<<2)
 #define PIPV4_EXTRACT_DHCP_ROUTER (1<<3)

I know this one above was already misaligned, but now that you are adding new constants, you could re-align them all with another tab.

Quoted 11 lines
src/openvpn/forward.h +2 -0
 #define PIPV4_CLIENT_NAT      (1<<4)
+#define PIPV6_IMCP_NOHOST_CLIENT (1<<5)
+#define PIPV6_IMCP_NOHOST_SERVER (1<<6)
 
 void process_ip_header(struct context *c, unsigned int flags, struct buffer *buf);
 
src/openvpn/multi.c +0 -0
index 3da8c110..fcb54f19 100644

src/openvpn/multi.c multi_get_queue(struct mbuf_set *ms) · line 2841

Quoted 5 lines
src/openvpn/multi.c +1 -1
@@ -2838,7 +2838,7 @@ multi_get_queue(struct mbuf_set *ms)
 
     if (mbuf_extract_item(ms, &item)) /* cleartext IP packet */
     {
-        unsigned int pip_flags = PIPV4_PASSTOS;
+        unsigned int pip_flags = PIPV4_PASSTOS|PIPV6_IMCP_NOHOST_SERVER;

spaces around |

Quoted 56 lines
src/openvpn/multi.c +0 -0
         set_prefix(item.instance);
         item.instance->context.c2.buf = item.buffer->buf;
src/openvpn/options.c +11 -0
index 11b9dc15..d2944ccb 100644
@@ -224,6 +224,8 @@ static const char usage_message[] =
     "                  Add 'bypass-dns' flag to similarly bypass tunnel for DNS.\n"
     "--redirect-private [flags]: Like --redirect-gateway, but omit actually changing\n"
     "                  the default gateway.  Useful when pushing private subnets.\n"
+    "--block-ipv6     : (client only) Instead sending IPv6 to the server generate\n"
+    "                   ICMPv6 host unreachable messages.\n"
     "--client-nat snat|dnat network netmask alias : on client add 1-to-1 NAT rule.\n"
     "--push-peer-info : (client only) push client info to server.\n"
     "--setenv name value : Set a custom environmental variable to pass to script.\n"
@@ -2085,6 +2087,10 @@ options_postprocess_verify_ce(const struct options *options, const struct connec
         msg(M_USAGE, "--lladdr can only be used in --dev tap mode");
     }
 
+    if (options->block_ipv6 && dev != DEV_TYPE_TUN)
+    {
+        msg(M_USAGE, "--block-ipv6 can be only used in --dev tun mode");
+    }
     /*
      * Sanity check on MTU parameters
      */
@@ -6336,6 +6342,11 @@ add_option(struct options *options,
 #endif
         options->routes->flags |= RG_ENABLE;
     }
+    else if (streq(p[0], "block-ipv6") && !p[1])
+    {
+        VERIFY_PERMISSION(OPT_P_ROUTE);
+        options->block_ipv6 = true;
+    }
     else if (streq(p[0], "remote-random-hostname") && !p[1])
     {
         VERIFY_PERMISSION(OPT_P_GENERAL);
src/openvpn/options.h +1 -0
index 3a6c33f8..8cfc95fb 100644
@@ -336,6 +336,7 @@ struct options
     bool route_delay_defined;
     struct route_option_list *routes;
     struct route_ipv6_option_list *routes_ipv6;                 /* IPv6 */
+    bool block_ipv6;
     bool route_nopull;
     bool route_gateway_via_dhcp;
     bool allow_pull_fqdn; /* as a client, allow server to push a FQDN for certain parameters */
src/openvpn/proto.c +0 -0
index 87c18e84..f1bddeaa 100644

src/openvpn/proto.c is_ipv6(int tunnel_type, struct buffer *buf) · line 103

Quoted 6 lines
src/openvpn/proto.c +3 -0
@@ -98,6 +98,51 @@ is_ipv6(int tunnel_type, struct buffer *buf)
     return is_ipv_X( tunnel_type, buf, 6 );
 }
 
+
+uint16_t
+ip_checksum(const sa_family_t af, const uint8_t *payload, const int len_payload, const uint8_t *src_addr,

I think this is longer than 80 chars.

src/openvpn/proto.c is_ipv6(int tunnel_type, struct buffer *buf) · line 115

Quoted 12 lines
src/openvpn/proto.c +12 -0
+            const uint8_t *dest_addr, const int proto)
+{
+    uint32_t sum = 0;
+    int addr_len = (af == AF_INET) ? 4 : 16;
+
+    /*
+     * make 16 bit words out of every two adjacent 8 bit words and  */
+    /* calculate the sum of all 16 bit words
+     */
+    for (int i = 0; i < len_payload; i += 2)
+    {
+        sum +=  (uint16_t) (((payload[i] << 8) & 0xFF00) + ((i + 1 < len_payload) ? (payload[i + 1] & 0xFF) : 0));

no space after the cast. And maybe you could split it on two lines.

src/openvpn/proto.c is_ipv6(int tunnel_type, struct buffer *buf) · line 122

Quoted 7 lines
src/openvpn/proto.c +7 -0
+
+    }
+
+    /* add the pseudo header which contains the IP source and destination addresses */
+    for (int i = 0; i < addr_len; i += 2)
+    {
+        sum += (uint16_t) ((src_addr[i] << 8) & 0xFF00) + (src_addr[i + 1] & 0xFF);

no space after the cast

src/openvpn/proto.c is_ipv6(int tunnel_type, struct buffer *buf) · line 127

Quoted 5 lines
src/openvpn/proto.c +5 -0
+
+    }
+    for (int i = 0; i < addr_len; i += 2)
+    {
+        sum += (uint16_t) ((dest_addr[i] << 8) & 0xFF00) + (dest_addr[i + 1] & 0xFF);

no space after the cast

src/openvpn/proto.c is_ipv6(int tunnel_type, struct buffer *buf) · line 131

Quoted 4 lines
src/openvpn/proto.c +4 -0
+    }
+
+    /* the length of the payload */
+    sum += (uint16_t) len_payload;

no space after the cast

src/openvpn/proto.c is_ipv6(int tunnel_type, struct buffer *buf) · line 134

Quoted 3 lines
src/openvpn/proto.c +3 -0
+
+    /* The next header or proto field*/
+    sum += (uint16_t) proto;

no space after the cast

src/openvpn/proto.c is_ipv6(int tunnel_type, struct buffer *buf) · line 143

Quoted 9 lines
src/openvpn/proto.c +9 -0
+
+    /* keep only the last 16 bits of the 32 bit calculated sum and add the carries */
+    while (sum >> 16)
+    {
+        sum = (sum & 0xFFFF) + (sum >> 16);
+    }
+
+    /* Take the one's complement of sum */
+    return ((uint16_t) ~sum);

no space after the cast

Quoted 10 lines
src/openvpn/proto.c +2 -0
+}
+
 #ifdef PACKET_TRUNCATION_CHECK
 
 void
src/openvpn/proto.h +0 -0
index 985aa99b..7a634490 100644

src/openvpn/proto.h struct openvpn_iphdr { · line 101

Quoted 4 lines
src/openvpn/proto.h +1 -0
@@ -98,6 +98,7 @@ struct openvpn_iphdr {
 #define OPENVPN_IPPROTO_IGMP 2  /* IGMP protocol */
 #define OPENVPN_IPPROTO_TCP  6  /* TCP protocol */
 #define OPENVPN_IPPROTO_UDP 17  /* UDP protocol */
+#define OPENVPN_IPPROTO_ICMPV6 58 /* ICMPV6 protocol */

I think the constants above were all aligned - keep them that way please.

Quoted 8 lines
src/openvpn/proto.h +1 -0
     uint8_t protocol;
 
     uint16_t check;
@@ -120,6 +121,24 @@ struct openvpn_ipv6hdr {
     struct  in6_addr daddr;
 };
 
+/*

src/openvpn/proto.h struct openvpn_ipv6hdr { · line 134

Quoted 10 lines
src/openvpn/proto.h +10 -0
+ * ICMPv6 header
+ */
+struct openvpn_icmp6hdr {
+# define OPENVPN_ICMP6_DESTINATION_UNREACHABLE 1
+# define OPENVPN_ND_ROUTER_SOLICIT	133
+# define OPENVPN_ND_ROUTER_ADVERT	134
+# define OPENVPN_ND_NEIGHBOR_SOLICIT	135
+# define OPENVPN_ND_NEIGHBOR_ADVERT	136
+# define OPENVPN_ND_INVERSE_SOLICIT	141
+# define OPENVPN_ND_INVERSE_ADVERT	142

not sure why you are adding a space between the # and the word 'define', we never do that.

src/openvpn/proto.h struct openvpn_ipv6hdr { · line 137

Quoted 3 lines
src/openvpn/proto.h +3 -0
+	uint8_t		icmp6_type;
+# define OPENVPN_ICMP6_DU_NOROUTE 0
+# define OPENVPN_ICMP6_DU_COMMUNICATION_PROHIBTED 1

same as above

Quoted 9 lines
src/openvpn/proto.h +4 -0
+	uint8_t		icmp6_code;
+	uint16_t	icmp6_cksum;
+	uint8_t		icmp6_dataun[4];
+};
 
 /*
  * UDP header
@@ -265,6 +284,39 @@ bool is_ipv4(int tunnel_type, struct buffer *buf);
 

src/openvpn/proto.h bool is_ipv4(int tunnel_type, struct buffer *buf); · line 294

Quoted 10 lines
src/openvpn/proto.h +8 -0
 bool is_ipv6(int tunnel_type, struct buffer *buf);
 
+static inline int
+af_addr_size(sa_family_t af)
+{
+    switch (af)
+    {
+        case AF_INET: return sizeof(struct sockaddr_in);
+
+        case AF_INET6: return sizeof(struct sockaddr_in6);

since you are moving this function I'd suggest to make it look slightly
better: move the return on their own line.

src/openvpn/proto.h bool is_ipv4(int tunnel_type, struct buffer *buf); · line 301

Quoted 7 lines
src/openvpn/proto.h +7 -0
+
+        default:
+#if 0
+            /* could be called from socket_do_accept() with empty addr */
+            msg(M_ERR, "Bad address family: %d\n", af);
+            ASSERT(0);
+#endif

... and maybe we can get rid of this #if 0 section ?

src/openvpn/proto.h bool is_ipv4(int tunnel_type, struct buffer *buf); · line 314

Quoted 13 lines
src/openvpn/proto.h +13 -0
+            return 0;
+    }
+}
+
+/**
+ *  Calculates an IP or IPv6 checksum with a pseudo header as required by TCP, UDP and ICMPv6
+ * @param af Address family for which the checksum is calculated AF_INET or AF_INET6
+ * @param payload the TCP, ICMPv6 or UDP packet
+ * @param len_payload length of payload
+ * @param src_addr Source address of the packet
+ * @param dest_addr Destination address of the packet
+ * @param proto next header or IP protocol of the packet
+ * @return calculated checksum in host order

This doc has to be completely re-aligned. Please keep ssl_backend.h as reference.

src/openvpn/proto.h bool is_ipv4(int tunnel_type, struct buffer *buf); · line 317

Quoted 3 lines
src/openvpn/proto.h +3 -0
+ */
+uint16_t
+ip_checksum(const sa_family_t af, const uint8_t *payload, const int len_payload, const uint8_t *src_addr,

too long line :)

Quoted 26 lines
src/openvpn/proto.h +2 -0
+            const uint8_t *dest_addr,  const int proto);
+
 #ifdef PACKET_TRUNCATION_CHECK
 void ipv4_packet_size_verify(const uint8_t *data,
                              const int size,
src/openvpn/socket.h +0 -12
index 479d1150..294dddef 100644
@@ -848,25 +848,6 @@ addr_inet4or6(struct sockaddr *addr)
 
 int addr_guess_family(sa_family_t af,const char *name);
 
-static inline int
-af_addr_size(sa_family_t af)
-{
-    switch (af)
-    {
-        case AF_INET: return sizeof(struct sockaddr_in);
-
-        case AF_INET6: return sizeof(struct sockaddr_in6);
-
-        default:
-#if 0
-            /* could be called from socket_do_accept() with empty addr */

src/openvpn/socket.h addr_inet4or6(struct sockaddr *addr) · line 853

Quoted 10 lines
src/openvpn/socket.h +0 -7
-            msg(M_ERR, "Bad address family: %d\n", af);
-            ASSERT(0);
-#endif
-            return 0;
-    }
-}
-
 static inline bool
 link_socket_actual_match(const struct link_socket_actual *a1, const struct link_socket_actual *a2)
 {

I think we need to focus a bit more on style, otherwise I'll always have something to complain about :-P

Cheers,

Signature · 1 line
Antonio Quartulli
#10 lore raw

PATCH v5 1/1 Implement block-ipv6

This can be used to redirect all IPv6 traffic to the tun interface, effectively black holing the IPv6 traffic. Without ICMPv6 error messages this will result in timeouts when the server does not send error codes. block-ipv6 allows client side only blocking on all platforms that OpenVPN supports IPv6. On Android it is only way to do sensible IPv6 blocking on Android < 5.0 and broken devices (Samsung).

PATCH V4:
- Fix more style issues reported by Antonio
- Clarify parts of the patch in comments and manpage

PATCH V3:
- Fix style iusses reported by Antonio and accidentily commited parts
- merge udp_checksum and ipv6_checkusm into common ip_checksum method
- Use fake ff80::7 address when no other address is configured.
- Make block-ipv6 also work for server by replying block-ipv6 to all ipv6 traffic send to the server

Note for the server the process_ip happens before the ipv6 route lookup so every ipv6 packet, regardless of its source address is replyied to with a no route to host packet.

---
 doc/openvpn.8         |  37 ++++++++++
 src/openvpn/dhcp.c    |  51 ++-----------
 src/openvpn/forward.c | 163 ++++++++++++++++++++++++++++++++++++++++--
 src/openvpn/forward.h |  12 ++--
 src/openvpn/multi.c   |   2 +-
 src/openvpn/options.c |   9 +++
 src/openvpn/options.h |   1 +
 src/openvpn/proto.c   |  52 ++++++++++++++
 src/openvpn/proto.h   |  58 ++++++++++++++-
 src/openvpn/socket.h  |  19 -----
 10 files changed, 325 insertions(+), 79 deletions(-)
doc/openvpn.8 +37 -0
index 29801aa8..f9413a0d 100644
@@ -1243,6 +1243,43 @@ Like \-\-redirect\-gateway, but omit actually changing the default
 gateway.  Useful when pushing private subnets.
 .\"*********************************************************
 .TP
+.B \-\-block\-ipv6
+On the client, instead of sending IPv6 packets over the VPN tunnel, all
+IPv6 packets are answered with an ICMPv6 no route host message. On the
+server, all IPv6 packets from clients are answered with an ICMPv6
+no route to host message. This options is intended for cases when IPv6
+should be blocked and other options are not available.
+\.B \-\-block\-ipv6
+will use the remote IPv6 as source address of the ICMPv6 packets if set,
+otherwise will use fe80::7 as source address.
+
+For this option to make sense you actually have to route traffic to the tun
+interface. The following example config block would send all IPv6 traffic to
+OpenVPN and answer all requests with no route to host, effectively blocking
+IPv6.
+
+# client config
+.br
+.B \-\-ifconfig-ipv6
+fd15:53b6:dead::2/64  fd15:53b6:dead::1
+.br
+.B \-\-redirect\-gateway
+ipv6
+.br
+.B \-\-block\-ipv6
+
+# Server config, push a "valid" ipv6 config to the client and block
+# on the server
+.br
+.B \-\-push
+"ifconfig-ipv6 fd15:53b6:dead::2/64  fd15:53b6:dead::1"
+.br
+.B \-\-push
+"redirect\-gateway ipv6"
+.br
+.B \-\-block\-ipv6
+.\"*********************************************************
+.TP
 .B \-\-tun\-mtu n
 Take the TUN device MTU to be
 .B n
src/openvpn/dhcp.c +4 -47

63 lines not on this page · raw message

src/openvpn/forward.c +159 -4

206 lines not on this page · raw message

src/openvpn/forward.h +7 -5
index 58b75d64..a7913088 100644
@@ -286,11 +286,13 @@ void process_outgoing_tun(struct context *c);
 
 bool send_control_channel_string(struct context *c, const char *str, int msglevel);
 
-#define PIPV4_PASSTOS         (1<<0)
-#define PIP_MSSFIX            (1<<1)         /* v4 and v6 */
-#define PIPV4_OUTGOING        (1<<2)
-#define PIPV4_EXTRACT_DHCP_ROUTER (1<<3)
-#define PIPV4_CLIENT_NAT      (1<<4)
+#define PIPV4_PASSTOS                   (1<<0)
+#define PIP_MSSFIX                      (1<<1)         /* v4 and v6 */
+#define PIP_OUTGOING                    (1<<2)
+#define PIPV4_EXTRACT_DHCP_ROUTER       (1<<3)
+#define PIPV4_CLIENT_NAT                (1<<4)
+#define PIPV6_IMCP_NOHOST_CLIENT        (1<<5)
+#define PIPV6_IMCP_NOHOST_SERVER        (1<<6)
 
 void process_ip_header(struct context *c, unsigned int flags, struct buffer *buf);
 
src/openvpn/multi.c +1 -1
index 8440f311..d3278938 100644
@@ -2855,7 +2855,7 @@ multi_get_queue(struct mbuf_set *ms)
 
     if (mbuf_extract_item(ms, &item)) /* cleartext IP packet */
     {
-        unsigned int pip_flags = PIPV4_PASSTOS;
+        unsigned int pip_flags = PIPV4_PASSTOS | PIPV6_IMCP_NOHOST_SERVER;
 
         set_prefix(item.instance);
         item.instance->context.c2.buf = item.buffer->buf;
src/openvpn/options.c +9 -0
index 777ef4c6..aaf21fc5 100644
@@ -225,6 +225,10 @@ static const char usage_message[] =
     "                  Add 'bypass-dns' flag to similarly bypass tunnel for DNS.\n"
     "--redirect-private [flags]: Like --redirect-gateway, but omit actually changing\n"
     "                  the default gateway.  Useful when pushing private subnets.\n"
+    "--block-ipv6     : (Client) Instead sending IPv6 to the server generate\n"
+    "                   ICMPv6 host unreachable messages on the client.\n"
+    "                   (Server) Instead of forwarding IPv6 packets send\n"
+    "                   ICMPv6 host unreachable packets to the client.\n"
     "--client-nat snat|dnat network netmask alias : on client add 1-to-1 NAT rule.\n"
     "--push-peer-info : (client only) push client info to server.\n"
     "--setenv name value : Set a custom environmental variable to pass to script.\n"
@@ -6368,6 +6372,11 @@ add_option(struct options *options,
 #endif
         options->routes->flags |= RG_ENABLE;
     }
+    else if (streq(p[0], "block-ipv6") && !p[1])
+    {
+        VERIFY_PERMISSION(OPT_P_ROUTE);
+        options->block_ipv6 = true;
+    }
     else if (streq(p[0], "remote-random-hostname") && !p[1])
     {
         VERIFY_PERMISSION(OPT_P_GENERAL);
src/openvpn/options.h +1 -0
index 575777f5..93df21b4 100644
@@ -350,6 +350,7 @@ struct options
     bool route_delay_defined;
     struct route_option_list *routes;
     struct route_ipv6_option_list *routes_ipv6;                 /* IPv6 */
+    bool block_ipv6;
     bool route_nopull;
     bool route_gateway_via_dhcp;
     bool allow_pull_fqdn; /* as a client, allow server to push a FQDN for certain parameters */
src/openvpn/proto.c +52 -0

58 lines not on this page · raw message

src/openvpn/proto.h +55 -3

76 lines not on this page · raw message

src/openvpn/socket.h +0 -19
index 0f22d479..19b38c92 100644
@@ -860,25 +860,6 @@ addr_inet4or6(struct sockaddr *addr)
 
 int addr_guess_family(sa_family_t af,const char *name);
 
-static inline int
-af_addr_size(sa_family_t af)
-{
-    switch (af)
-    {
-        case AF_INET: return sizeof(struct sockaddr_in);
-
-        case AF_INET6: return sizeof(struct sockaddr_in6);
-
-        default:
-#if 0
-            /* could be called from socket_do_accept() with empty addr */
-            msg(M_ERR, "Bad address family: %d\n", af);
-            ASSERT(0);
-#endif
-            return 0;
-    }
-}
-
 static inline bool
 link_socket_actual_match(const struct link_socket_actual *a1, const struct link_socket_actual *a2)
 {
Signature · 1 line
2.19.0
#11 lore raw

Will this feature break VPNs that use NAT64 to connect to IPv4-only OpenVPN servers?

-----Original Message-----
From: Arne Schwabe [mailto:[email protected]...]
Sent: October 29, 2018 13:21
To: [email protected]
Subject: [Openvpn-devel] [PATCH v5] Implement block-ipv6

This can be used to redirect all IPv6 traffic to the tun interface, effectively black holing the IPv6 traffic. Without ICMPv6 error messages this will result in timeouts when the server does not send error codes. block-ipv6 allows client side only blocking on all platforms that OpenVPN supports IPv6. On Android it is only way to do sensible IPv6 blocking on Android < 5.0 and broken devices (Samsung).

PATCH V4:
- Fix more style issues reported by Antonio
- Clarify parts of the patch in comments and manpage

PATCH V3:
- Fix style iusses reported by Antonio and accidentily commited parts
- merge udp_checksum and ipv6_checkusm into common ip_checksum method
- Use fake ff80::7 address when no other address is configured.
- Make block-ipv6 also work for server by replying block-ipv6 to all ipv6 traffic send to the server

Note for the server the process_ip happens before the ipv6 route lookup so every ipv6 packet, regardless of its source address is replyied to with a no route to host packet.

---
 doc/openvpn.8         |  37 ++++++++++
 src/openvpn/dhcp.c    |  51 ++-----------
 src/openvpn/forward.c | 163 ++++++++++++++++++++++++++++++++++++++++--
 src/openvpn/forward.h |  12 ++--
 src/openvpn/multi.c   |   2 +-
 src/openvpn/options.c |   9 +++
 src/openvpn/options.h |   1 +
 src/openvpn/proto.c   |  52 ++++++++++++++
 src/openvpn/proto.h   |  58 ++++++++++++++-
 src/openvpn/socket.h  |  19 -----
 10 files changed, 325 insertions(+), 79 deletions(-)
doc/openvpn.8 +33 -0
@@ -1243,6 +1243,43 @@ Like \-\-redirect\-gateway, but omit actually changing the default  gateway.  Useful when pushing private subnets.
 .\"*********************************************************
 .TP
+.B \-\-block\-ipv6
+On the client, instead of sending IPv6 packets over the VPN tunnel, all
+IPv6 packets are answered with an ICMPv6 no route host message. On the 
+server, all IPv6 packets from clients are answered with an ICMPv6 no 
+route to host message. This options is intended for cases when IPv6 
+should be blocked and other options are not available.
+\.B \-\-block\-ipv6
+will use the remote IPv6 as source address of the ICMPv6 packets if 
+set, otherwise will use fe80::7 as source address.
+
+For this option to make sense you actually have to route traffic to the 
+tun interface. The following example config block would send all IPv6 
+traffic to OpenVPN and answer all requests with no route to host, 
+effectively blocking IPv6.
+
+# client config
+.br
+.B \-\-ifconfig-ipv6
+fd15:53b6:dead::2/64  fd15:53b6:dead::1 .br .B \-\-redirect\-gateway
+ipv6
+.br
+.B \-\-block\-ipv6
+
+# Server config, push a "valid" ipv6 config to the client and block # 
+on the server .br .B \-\-push
+"ifconfig-ipv6 fd15:53b6:dead::2/64  fd15:53b6:dead::1"
+.br
+.B \-\-push
+"redirect\-gateway ipv6"
+.br
+.B \-\-block\-ipv6
+.\"*********************************************************
+.TP
 .B \-\-tun\-mtu n
 Take the TUN device MTU to be
 .B n
src/openvpn/dhcp.c +0 -43

49 lines not on this page · raw message

             /* recompute the UDP checksum */
             df->udp.check = 0;
-            df->udp.check = htons(udp_checksum((uint8_t *) &df->udp,
-                                               sizeof(struct openvpn_udphdr) + sizeof(struct dhcp) + optlen,
-                                               (uint8_t *)&df->ip.saddr,
-                                               (uint8_t *)&df->ip.daddr));
+            df->udp.check = htons(ip_checksum(AF_INET, (uint8_t *)&df->udp,
+                                  sizeof(struct openvpn_udphdr) + sizeof(struct dhcp) + optlen,
+                                  (uint8_t *)&df->ip.saddr, (uint8_t *)&df->ip.daddr,
+                                  OPENVPN_IPPROTO_UDP));
             /* only return the extracted Router address if DHCPACK */
             if (message_type == DHCPACK) diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index 941ba49f..9c0e98a8 100644
src/openvpn/forward.c +3 -1
@@ -1420,7 +1420,9 @@ process_incoming_tun(struct context *c)
          * The --passtos and --mssfix options require
          * us to examine the IP header (IPv4 or IPv6).
          */
-        process_ip_header(c, PIPV4_PASSTOS|PIP_MSSFIX|PIPV4_CLIENT_NAT, &c->c2.buf);
+        unsigned int flags = PIPV4_PASSTOS | PIP_MSSFIX | PIPV4_CLIENT_NAT
+            | PIPV6_IMCP_NOHOST_CLIENT;
+        process_ip_header(c, flags, &c->c2.buf);
 
 #ifdef PACKET_TRUNCATION_CHECK
         /* if (c->c2.buf.len > 1) --c->c2.buf.len; */ @@ -1431,6 +1433,9 @@ process_incoming_tun(struct context *c)
                                 &c->c2.n_trunc_pre_encrypt);  #endif
+    }
+    if (c->c2.buf.len > 0)
+    {
         encrypt_sign(c, true);
     }
     else
@@ -1441,6 +1446,140 @@ process_incoming_tun(struct context *c)
     gc_free(&gc);

}

+/**
+ * Forges a IPv6 ICMP packet with a no route to host error code from +the
+ * IPv6 packetin buf and sends it directly back to the client via the +tun device
+ * when used on a client and via the link if used on the server.
+ *
+ * @param buf - The buf containing the packet for which the icmp6
+ * unreachable should be constructed.
+ *
+ * @param client - determines whether to the send packet back via tun or link
+ */
+void
+ipv6_send_icmp_unreachable(struct context *c, struct buffer *buf, bool +client) { #define MAX_ICMPV6LEN IPV6_MIN_MTU
+ struct openvpn_icmp6hdr icmp6out;
+ CLEAR(icmp6out);
+
+ /*

+     * Get a buffer to the ip packet, is_ipv6 automatically forwards
+     * the buffer to the ip packet
+     */

+ struct buffer inputipbuf = *buf;
+
+ is_ipv6(TUNNEL_TYPE(c->c1.tuntap), &inputipbuf);
+
+ if (BLEN(&inputipbuf) < (int)sizeof(struct openvpn_ipv6hdr))
+ {
+ return;
+ }
+
+ const struct openvpn_ipv6hdr* pip6 = (struct openvpn_ipv6hdr
+ *)BPTR(&inputipbuf);
+
+ /* Copy version, traffic class, flow label from input packet */
+ struct openvpn_ipv6hdr pip6out = *pip6;
+
+ pip6out.version_prio = pip6->version_prio;
+ pip6out.daddr = pip6->saddr;
+
+ /*

+     * Use the IPv6 remote address if we have one, otherwise use a fake one
+     * using the remote address is preferred since it makes debugging and
+     * understanding where the ICMPv6 error originates easier
+     */
+    if (c->options.ifconfig_ipv6_remote)
+    {
+        inet_pton(AF_INET6, c->options.ifconfig_ipv6_remote, &pip6out.saddr);
+    }
+    else
+    {

+ inet_pton(AF_INET6, "fe80::7", &pip6out.saddr);
+ }
+
+ pip6out.nexthdr = OPENVPN_IPPROTO_ICMPV6;
+
+ /*

+     * The ICMPv6 unreachable code worked best in my (arne) tests with Windows,
+     * Linux and Android. Windows did not like the administratively prohibited
+     * return code (no fast fail)
+     */

+ icmp6out.icmp6_type = OPENVPN_ICMP6_DESTINATION_UNREACHABLE;
+ icmp6out.icmp6_code = OPENVPN_ICMP6_DU_NOROUTE;
+
+ int icmpheader_len = sizeof(struct openvpn_ipv6hdr)
+ + sizeof(struct openvpn_icmp6hdr);
+ int totalheader_len = icmpheader_len;
+
+ if (TUNNEL_TYPE(c->c1.tuntap) == DEV_TYPE_TAP)
+ totalheader_len += sizeof(struct openvpn_ethhdr);
+
+ /*

+     * Calculate size for payload, defined in the standard that the resulting
+     * frame should be <= 1280 and have as much as possible of the original
+     * packet
+     */

+ int max_payload_size = min_int(MAX_ICMPV6LEN,
+ TUN_MTU_SIZE(&c->c2.frame) - icmpheader_len);
+ int payload_len = min_int(max_payload_size, BLEN(&inputipbuf));
+
+ pip6out.payload_len = htons(sizeof(struct openvpn_icmp6hdr) +
+ payload_len);
+

+    /* Construct the packet as outgoing packet back to the client */
+    struct buffer * outbuf;
+    if (client)
+    {
+        c->c2.to_tun = c->c2.buffers->aux_buf;
+        outbuf = &(c->c2.to_tun);
+    }
+    else
+    {

+ c->c2.to_link = c->c2.buffers->aux_buf;
+ outbuf = &(c->c2.to_link);
+ }
+ ASSERT(buf_init(outbuf, totalheader_len));
+

+    /* Fill the end of the buffer with original packet */
+    ASSERT(buf_safe(outbuf, payload_len));
+    ASSERT(buf_copy_n(outbuf, &inputipbuf, payload_len));

+
+ /* ICMP Header, copy into buffer to allow checksum calculation */
+ ASSERT(buf_write_prepend(outbuf, &icmp6out, sizeof(struct
+ openvpn_icmp6hdr)));
+
+ /* Calculate checksum over the packet and write to header */
+
+ uint16_t new_csum = ip_checksum(AF_INET6, BPTR(outbuf), BLEN(outbuf),
+ (const uint8_t*)&pip6out.saddr,
+ (uint8_t*)&pip6out.daddr, OPENVPN_IPPROTO_ICMPV6);
+ ((struct openvpn_icmp6hdr*) BPTR(outbuf))->icmp6_cksum =
+ htons(new_csum);
+
+
+ /* IPv6 Header */
+ ASSERT(buf_write_prepend(outbuf, &pip6out, sizeof(struct
+ openvpn_ipv6hdr)));
+
+ /*
+ * Tap mode, we also need to create an Ethernet header.
+ */
+ if (TUNNEL_TYPE(c->c1.tuntap) == DEV_TYPE_TAP)
+ {
+ if (BLEN(buf) < (int)sizeof(struct openvpn_ethhdr))
+ {
+ return;
+ }
+
+ const struct openvpn_ethhdr* orig_ethhdr = (struct
+ openvpn_ethhdr *) BPTR(buf);
+

+        /* Copy frametype and reverse source/destination for the response */
+        struct openvpn_ethhdr ethhdr;
+        memcpy(ethhdr.source, orig_ethhdr->dest, OPENVPN_ETH_ALEN);
+        memcpy(ethhdr.dest, orig_ethhdr->source, OPENVPN_ETH_ALEN);
+        ethhdr.proto = htons(OPENVPN_ETH_P_IPV6);
+        ASSERT(buf_write_prepend(outbuf, &ethhdr, sizeof(struct openvpn_ethhdr)));

+ }
+#undef MAX_ICMPV6LEN
+}
+
void
process_ip_header(struct context *c, unsigned int flags, struct buffer *buf) { @@ -1462,6 +1601,10 @@ process_ip_header(struct context *c, unsigned int flags, struct buffer *buf)

     {
         flags &= ~PIPV4_EXTRACT_DHCP_ROUTER;
     }

+ if (!c->options.block_ipv6)
+ {
+ flags &= ~(PIPV6_IMCP_NOHOST_CLIENT | PIPV6_IMCP_NOHOST_SERVER);
+ }

     if (buf->len > 0)
     {
@@ -1497,7 +1640,7 @@ process_ip_header(struct context *c, unsigned int flags, struct buffer *buf)
                 /* possibly do NAT on packet */
                 if ((flags & PIPV4_CLIENT_NAT) && c->options.client_nat)
                 {
-                    const int direction = (flags & PIPV4_OUTGOING) ? CN_INCOMING : CN_OUTGOING;
+                    const int direction = (flags & PIP_OUTGOING) ? 
+ CN_INCOMING : CN_OUTGOING;
                     client_nat_transform(c->options.client_nat, &ipbuf, direction);
                 }
                 /* possibly extract a DHCP router message */ @@ -1515,8 +1658,18 @@ process_ip_header(struct context *c, unsigned int flags, struct buffer *buf)
                 /* possibly alter the TCP MSS */
                 if (flags & PIP_MSSFIX)
                 {

- mss_fixup_ipv6(&ipbuf, MTU_TO_MSS(TUN_MTU_SIZE_DYNAMIC(&c->c2.frame)));
+ mss_fixup_ipv6(&ipbuf,
+ MTU_TO_MSS(TUN_MTU_SIZE_DYNAMIC(&c->c2.frame)));
+ }
+ if (!(flags & PIP_OUTGOING) && (flags &
+ (PIPV6_IMCP_NOHOST_CLIENT | PIPV6_IMCP_NOHOST_SERVER)))
+ {
+ ipv6_send_icmp_unreachable(c, buf,
+ (bool)(flags & PIPV6_IMCP_NOHOST_CLIENT));
+ /* Drop the IPv6 packet */

+                    buf->len=0;
                 }
+
             }
         }
     }

@@ -1697,7 +1850,9 @@ process_outgoing_tun(struct context *c)
* The --mssfix option requires
* us to examine the IP header (IPv4 or IPv6). */
- process_ip_header(c, PIP_MSSFIX|PIPV4_EXTRACT_DHCP_ROUTER|PIPV4_CLIENT_NAT|PIPV4_OUTGOING, &c->c2.to_tun);
+ process_ip_header(c,
+ PIP_MSSFIX | PIPV4_EXTRACT_DHCP_ROUTER | PIPV4_CLIENT_NAT | PIP_OUTGOING,
+ &c->c2.to_tun);

     if (c->c2.to_tun.len <= MAX_RW_SIZE_TUN(&c->c2.frame))
     {
src/openvpn/forward.h +7 -5
@@ -286,11 +286,13 @@ void process_outgoing_tun(struct context *c);
 
 bool send_control_channel_string(struct context *c, const char *str, int msglevel);
 
-#define PIPV4_PASSTOS         (1<<0)
-#define PIP_MSSFIX            (1<<1)         /* v4 and v6 */
-#define PIPV4_OUTGOING        (1<<2)
-#define PIPV4_EXTRACT_DHCP_ROUTER (1<<3)
-#define PIPV4_CLIENT_NAT      (1<<4)
+#define PIPV4_PASSTOS                   (1<<0)
+#define PIP_MSSFIX                      (1<<1)         /* v4 and v6 */
+#define PIP_OUTGOING                    (1<<2)
+#define PIPV4_EXTRACT_DHCP_ROUTER       (1<<3)
+#define PIPV4_CLIENT_NAT                (1<<4)
+#define PIPV6_IMCP_NOHOST_CLIENT        (1<<5)
+#define PIPV6_IMCP_NOHOST_SERVER        (1<<6)
 
 void process_ip_header(struct context *c, unsigned int flags, struct buffer *buf);
 
src/openvpn/multi.c +2 -1
@@ -2855,7 +2855,7 @@ multi_get_queue(struct mbuf_set *ms)
 
     if (mbuf_extract_item(ms, &item)) /* cleartext IP packet */
     {
-        unsigned int pip_flags = PIPV4_PASSTOS;
+        unsigned int pip_flags = PIPV4_PASSTOS | 
+ PIPV6_IMCP_NOHOST_SERVER;
 
         set_prefix(item.instance);
         item.instance->context.c2.buf = item.buffer->buf; diff --git a/src/openvpn/options.c b/src/openvpn/options.c index 777ef4c6..aaf21fc5 100644
src/openvpn/options.c +11 -1
@@ -225,6 +225,10 @@ static const char usage_message[] =
     "                  Add 'bypass-dns' flag to similarly bypass tunnel for DNS.\n"
     "--redirect-private [flags]: Like --redirect-gateway, but omit actually changing\n"
     "                  the default gateway.  Useful when pushing private subnets.\n"
+    "--block-ipv6     : (Client) Instead sending IPv6 to the server generate\n"
+    "                   ICMPv6 host unreachable messages on the client.\n"
+    "                   (Server) Instead of forwarding IPv6 packets send\n"
+    "                   ICMPv6 host unreachable packets to the client.\n"
     "--client-nat snat|dnat network netmask alias : on client add 1-to-1 NAT rule.\n"
     "--push-peer-info : (client only) push client info to server.\n"
     "--setenv name value : Set a custom environmental variable to pass to script.\n"
@@ -6368,6 +6372,11 @@ add_option(struct options *options,  #endif
         options->routes->flags |= RG_ENABLE;
     }
+    else if (streq(p[0], "block-ipv6") && !p[1])
+    {
+        VERIFY_PERMISSION(OPT_P_ROUTE);
+        options->block_ipv6 = true;
+    }
     else if (streq(p[0], "remote-random-hostname") && !p[1])
     {
         VERIFY_PERMISSION(OPT_P_GENERAL); diff --git a/src/openvpn/options.h b/src/openvpn/options.h index 575777f5..93df21b4 100644
--- a/src/openvpn/options.h
+++ b/src/openvpn/options.h
@@ -350,6 +350,7 @@ struct options
     bool route_delay_defined;
     struct route_option_list *routes;
     struct route_ipv6_option_list *routes_ipv6;                 /* IPv6 */
+    bool block_ipv6;
     bool route_nopull;
     bool route_gateway_via_dhcp;
     bool allow_pull_fqdn; /* as a client, allow server to push a FQDN for certain parameters */ diff --git a/src/openvpn/proto.c b/src/openvpn/proto.c index 87c18e84..5324eda4 100644
src/openvpn/proto.c +53 -0

58 lines not on this page · raw message

src/openvpn/proto.h +55 -1

74 lines not on this page · raw message

src/openvpn/socket.h +0 -20
@@ -860,25 +860,6 @@ addr_inet4or6(struct sockaddr *addr)
 
 int addr_guess_family(sa_family_t af,const char *name);
 
-static inline int
-af_addr_size(sa_family_t af)
-{
-    switch (af)
-    {
-        case AF_INET: return sizeof(struct sockaddr_in);
-
-        case AF_INET6: return sizeof(struct sockaddr_in6);
-
-        default:
-#if 0
-            /* could be called from socket_do_accept() with empty addr */
-            msg(M_ERR, "Bad address family: %d\n", af);
-            ASSERT(0);
-#endif
-            return 0;
-    }
-}
-
 static inline bool
 link_socket_actual_match(const struct link_socket_actual *a1, const struct link_socket_actual *a2)  {
--

2.19.0

_______________________________________________
Openvpn-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

#12 lore raw

Hi,

Quoted 1 line · Kristian McColm

Will this feature break VPNs that use NAT64 to connect to IPv4-only OpenVPN servers?

No.

This is an opt-in feature which you can enable by pushing "block-ipv6" from the server to the client, to avoid IPv6 traffic (to, say, youtube) bypassing your IPv4-only VPN.

If your VPN is dual-stacked *inside* the tunnel, you wouldn't enable this. If your VPN is IPv4-only, but the client has external IPv6 connectivity, you might consider enabling this.

gert

Signature · 6 lines
"If was one thing all people took for granted, was conviction that if you 
 feed honest figures into a computer, honest figures come out. Never doubted 
 it myself till I met a computer with a sense of humor."
                             Robert A. Heinlein, The Moon is a Harsh Mistress

Gert Doering - Munich, Germany                             [email protected]...
#13 lore raw

Am 29.10.18 um 23:09 schrieb Gert Doering:

Quoted 11 lines

Hi,

Quoted 1 line · Kristian McColm

Will this feature break VPNs that use NAT64 to connect to IPv4-only OpenVPN servers?

No.

This is an opt-in feature which you can enable by pushing "block-ipv6" from the server to the client, to avoid IPv6 traffic (to, say, youtube) bypassing your IPv4-only VPN.

Quoted 5 lines

If your VPN is dual-stacked *inside* the tunnel, you wouldn't enable this. If your VPN is IPv4-only, but the client has external IPv6 connectivity, you might consider enabling this.

gert

Sure?

NAT64 means the client has (only) IPv6 connectivity. I am not sure about, if a openvpn connections survive from an IPv6-only/NAT64 endpoint to an IPv4-only server (transport protocol is changing during the transport from 6 to 4 and vice versa). May be they do it via NAT64 or via 464xlat. But if you block IPv6 ("external") at the client, you will lose also you connectivity. (except 464xlat which generates a v4 socket)

I cannot test it at the moment. I have two IPv6-only configured ovpn servers and two NAT64-ISP (tm and lrz) but no time to build an IPv4-only-openvpn-Server.

Regards,
Thomas

#14 lore raw

PATCH v5 1/1 Implement block-ipv6

Am 30.10.18 um 11:07 schrieb Thomas Schäfer:

Quoted 29 lines

Am 29.10.18 um 23:09 schrieb Gert Doering:

Quoted 12 lines

Hi,

Quoted 2 lines · Kristian McColm

Will this feature break VPNs that use NAT64 to connect to IPv4-only OpenVPN servers?

No.

This is an opt-in feature which you can enable by pushing "block-ipv6" from the server to the client, to avoid IPv6 traffic (to, say, youtube) bypassing your IPv4-only VPN.

Quoted 5 lines

If your VPN is dual-stacked *inside* the tunnel, you wouldn't enable this.  If your VPN is IPv4-only, but the client has external IPv6 connectivity, you might consider enabling this.

gert

Sure?

NAT64 means the client has (only) IPv6 connectivity. I am not sure about, if a openvpn connections survive from an IPv6-only/NAT64 endpoint to an IPv4-only server (transport protocol is changing during the transport from 6 to 4 and vice versa). May be they do it via NAT64 or via 464xlat. But if you block IPv6 ("external") at the client, you will lose also you connectivity. (except 464xlat which generates a v4 socket)

Quoted 3 lines

I cannot test it at the moment. I have two IPv6-only configured ovpn servers and two NAT64-ISP (tm and lrz) but no time to build an IPv4-only-openvpn-Server.

This patch only adds code to reject ipv6 packets that are *already* in the tunnel. It still depends on having redirect-gateway ipv6 or a default route to your tun device to actually do anything. If your packets to the VPN server ended up inside the tunnel something else is wrong already. I can add a followup patch to clarify this.

Arne

#15 lore raw

Am 30.10.18 um 11:22 schrieb Arne Schwabe:

Quoted 34 lines

Am 30.10.18 um 11:07 schrieb Thomas Schäfer:

Quoted 29 lines

Am 29.10.18 um 23:09 schrieb Gert Doering:

Quoted 12 lines

Hi,

Quoted 2 lines · Kristian McColm

Will this feature break VPNs that use NAT64 to connect to IPv4-only OpenVPN servers?

No.

This is an opt-in feature which you can enable by pushing "block-ipv6" from the server to the client, to avoid IPv6 traffic (to, say, youtube) bypassing your IPv4-only VPN.

Quoted 5 lines

If your VPN is dual-stacked *inside* the tunnel, you wouldn't enable this.  If your VPN is IPv4-only, but the client has external IPv6 connectivity, you might consider enabling this.

gert

Sure?

NAT64 means the client has (only) IPv6 connectivity. I am not sure about, if a openvpn connections survive from an IPv6-only/NAT64 endpoint to an IPv4-only server (transport protocol is changing during the transport from 6 to 4 and vice versa). May be they do it via NAT64 or via 464xlat. But if you block IPv6 ("external") at the client, you will lose also you connectivity. (except 464xlat which generates a v4 socket)

Quoted 3 lines

I cannot test it at the moment. I have two IPv6-only configured ovpn servers and two NAT64-ISP (tm and lrz) but no time to build an IPv4-only-openvpn-Server.

Quoted 6 lines

This patch only adds code to reject ipv6 packets that are *already* in the tunnel. It still depends on having redirect-gateway ipv6 or a default route to your tun device to actually do anything. If your packets to the VPN server ended up inside the tunnel something else is wrong already. I can add a followup patch to clarify this.

Actually the man page already in the patch has a section about this:

.\"********************************************************* .TP
.B \-\-block\-ipv6
On the client, instead of sending IPv6 packets over the VPN tunnel, all IPv6 packets are answered with an ICMPv6 no route host message. On the server, all IPv6 packets from clients are answered with an ICMPv6 no route to host message. This options is intended for cases when IPv6 should be blocked and other options are not available.
\.B \-\-block\-ipv6
will use the remote IPv6 as source address of the ICMPv6 packets if set, otherwise will use fe80::7 as source address.

For this option to make sense you actually have to route traffic to the tun interface. The following example config block would send all IPv6 traffic to OpenVPN and answer all requests with no route to host, effectively blocking IPv6.

# client config
.br
.B \-\-ifconfig-ipv6
fd15:53b6:dead::2/64  fd15:53b6:dead::1
.br
.B \-\-redirect\-gateway
ipv6
.br
.B \-\-block\-ipv6

# Server config, push a "valid" ipv6 config to the client and block

# on the server
.br
.B \-\-push
"ifconfig-ipv6 fd15:53b6:dead::2/64  fd15:53b6:dead::1"
.br
.B \-\-push
"redirect\-gateway ipv6"
.br
.B \-\-block\-ipv6
.\"*********************************************************

That should answer your questions, doesn't it?

Arne

#16 lore raw

Am 30.10.18 um 11:53 schrieb Arne Schwabe:

Quoted 1 line

That should answer your questions, doesn't it?

Thanks for clarification. It doesn't harm IPv6 transport. But it may (if intended ) block IPv6 payload.

Thomas

#17 lore raw
In reply to Arne Schwabe

Am 29.10.18 um 18:20 schrieb Arne Schwabe:

Quoted 1 line

+#define MAX_ICMPV6LEN IPV6_MIN_MTU

This should be

#define MAX_ICMPV6LEN 1280

Antonios suggestion of using IPV6_MIN_MTU works on Android on my linux test box but breaks on other Linux machines and FreeBSD does not even have this define.

Arne

#18 lore raw
In reply to Arne Schwabe

Hi,

Quoted 6 lines · Arne Schwabe

This can be used to redirect all IPv6 traffic to the tun interface, effectively black holing the IPv6 traffic. Without ICMPv6 error messages this will result in timeouts when the server does not send error codes. block-ipv6 allows client side only blocking on all platforms that OpenVPN supports IPv6. On Android it is only way to do sensible IPv6 blocking on Android < 5.0 and broken devices (Samsung).

Generally I agree with the need for this, so feature-ACK.

Functionally, I've tested this on Linux, and it does what it says on the lid (I've only tested client, but if that works, I expect server to work as well).

Style-wise, there is, unfortunately, still way to go - missing brackets, non-pretty alignment, etc. -> so please run uncrustify, then send us a v6.

Besides the uncrustify things, please leave af_addr_size() in socket.h - its only callers are in socket.c, so the move to proto.h does not make any sense anymore (I assume you wanted to call it from ip_checksum(), but since that one does a local decision for 4/16 byte address size, moving the other function is no longer needed).

Lastly, the context of the hunk in options.c has no
#ifdef ENABLE_PUSH_PEER_INFO
anymore, but "master" still has that - so please send the patch that removes this #ifdef to the list as well :-)

So, "changes requested" :-) - thanks.

As a side note, how did you test the "without ifconfig-ipv6" variant?

When I tried this it refused to install the IPv6 routes...

gert

Signature · 6 lines
"If was one thing all people took for granted, was conviction that if you 
 feed honest figures into a computer, honest figures come out. Never doubted 
 it myself till I met a computer with a sense of humor."
                             Robert A. Heinlein, The Moon is a Harsh Mistress

Gert Doering - Munich, Germany                             [email protected]...
#19 lore raw

PATCH v7 1/1 Implement block-ipv6

This can be used to redirect all IPv6 traffic to the tun interface, effectively black holing the IPv6 traffic. Without ICMPv6 error messages this will result in timeouts when the server does not send error codes. block-ipv6 allows client side only blocking on all platforms that OpenVPN supports IPv6. On Android it is only way to do sensible IPv6 blocking on Android < 5.0 and broken devices (Samsung).

PATCH V6:
- Rebase on master and run uncrustify on the patch

PATCH V5:
- Fix even more style issues by Antonio
- Remove check for dev == tun as this also works for tap

PATCH V4:
- Fix more style issues reported by Antonio
- Clarify parts of the patch in comments and manpage

PATCH V3:
- Fix style iusses reported by Antonio and accidentily commited parts
- merge udp_checksum and ipv6_checkusm into common ip_checksum method
- Use fake ff80::7 address when no other address is configured.
- Make block-ipv6 also work for server by replying block-ipv6 to all ipv6 traffic send to the server

Note for the server the process_ip happens before the ipv6 route lookup so every ipv6 packet, regardless of its source address is replyied to with a no route to host packet.

---
 doc/openvpn.8         |  37 ++++++++++
 src/openvpn/dhcp.c    |  51 +------------
 src/openvpn/forward.c | 165 +++++++++++++++++++++++++++++++++++++++++-
 src/openvpn/forward.h |  12 +--
 src/openvpn/multi.c   |   2 +-
 src/openvpn/options.c |   9 +++
 src/openvpn/options.h |   1 +
 src/openvpn/proto.c   |  52 +++++++++++++
 src/openvpn/proto.h   |  42 ++++++++++-
 9 files changed, 311 insertions(+), 60 deletions(-)
doc/openvpn.8 +37 -0
index eae2bd94..11ebdbe5 100644
@@ -1243,6 +1243,43 @@ Like \-\-redirect\-gateway, but omit actually changing the default
 gateway.  Useful when pushing private subnets.
 .\"*********************************************************
 .TP
+.B \-\-block\-ipv6
+On the client, instead of sending IPv6 packets over the VPN tunnel, all
+IPv6 packets are answered with an ICMPv6 no route host message. On the
+server, all IPv6 packets from clients are answered with an ICMPv6
+no route to host message. This options is intended for cases when IPv6
+should be blocked and other options are not available.
+\.B \-\-block\-ipv6
+will use the remote IPv6 as source address of the ICMPv6 packets if set,
+otherwise will use fe80::7 as source address.
+
+For this option to make sense you actually have to route traffic to the tun
+interface. The following example config block would send all IPv6 traffic to
+OpenVPN and answer all requests with no route to host, effectively blocking
+IPv6.
+
+# client config
+.br
+.B \-\-ifconfig-ipv6
+fd15:53b6:dead::2/64  fd15:53b6:dead::1
+.br
+.B \-\-redirect\-gateway
+ipv6
+.br
+.B \-\-block\-ipv6
+
+# Server config, push a "valid" ipv6 config to the client and block
+# on the server
+.br
+.B \-\-push
+"ifconfig-ipv6 fd15:53b6:dead::2/64  fd15:53b6:dead::1"
+.br
+.B \-\-push
+"redirect\-gateway ipv6"
+.br
+.B \-\-block\-ipv6
+.\"*********************************************************
+.TP
 .B \-\-tun\-mtu n
 Take the TUN device MTU to be
 .B n
src/openvpn/dhcp.c +4 -47

63 lines not on this page · raw message

src/openvpn/forward.c +161 -4

208 lines not on this page · raw message

src/openvpn/forward.h +7 -5
index 58b75d64..a7913088 100644
@@ -286,11 +286,13 @@ void process_outgoing_tun(struct context *c);
 
 bool send_control_channel_string(struct context *c, const char *str, int msglevel);
 
-#define PIPV4_PASSTOS         (1<<0)
-#define PIP_MSSFIX            (1<<1)         /* v4 and v6 */
-#define PIPV4_OUTGOING        (1<<2)
-#define PIPV4_EXTRACT_DHCP_ROUTER (1<<3)
-#define PIPV4_CLIENT_NAT      (1<<4)
+#define PIPV4_PASSTOS                   (1<<0)
+#define PIP_MSSFIX                      (1<<1)         /* v4 and v6 */
+#define PIP_OUTGOING                    (1<<2)
+#define PIPV4_EXTRACT_DHCP_ROUTER       (1<<3)
+#define PIPV4_CLIENT_NAT                (1<<4)
+#define PIPV6_IMCP_NOHOST_CLIENT        (1<<5)
+#define PIPV6_IMCP_NOHOST_SERVER        (1<<6)
 
 void process_ip_header(struct context *c, unsigned int flags, struct buffer *buf);
 
src/openvpn/multi.c +1 -1
index 8440f311..d3278938 100644
@@ -2855,7 +2855,7 @@ multi_get_queue(struct mbuf_set *ms)
 
     if (mbuf_extract_item(ms, &item)) /* cleartext IP packet */
     {
-        unsigned int pip_flags = PIPV4_PASSTOS;
+        unsigned int pip_flags = PIPV4_PASSTOS | PIPV6_IMCP_NOHOST_SERVER;
 
         set_prefix(item.instance);
         item.instance->context.c2.buf = item.buffer->buf;
src/openvpn/options.c +9 -0
index 9ee1836b..38f668ff 100644
@@ -225,6 +225,10 @@ static const char usage_message[] =
     "                  Add 'bypass-dns' flag to similarly bypass tunnel for DNS.\n"
     "--redirect-private [flags]: Like --redirect-gateway, but omit actually changing\n"
     "                  the default gateway.  Useful when pushing private subnets.\n"
+    "--block-ipv6     : (Client) Instead sending IPv6 to the server generate\n"
+    "                   ICMPv6 host unreachable messages on the client.\n"
+    "                   (Server) Instead of forwarding IPv6 packets send\n"
+    "                   ICMPv6 host unreachable packets to the client.\n"
     "--client-nat snat|dnat network netmask alias : on client add 1-to-1 NAT rule.\n"
     "--push-peer-info : (client only) push client info to server.\n"
     "--setenv name value : Set a custom environmental variable to pass to script.\n"
@@ -6364,6 +6368,11 @@ add_option(struct options *options,
 #endif
         options->routes->flags |= RG_ENABLE;
     }
+    else if (streq(p[0], "block-ipv6") && !p[1])
+    {
+        VERIFY_PERMISSION(OPT_P_ROUTE);
+        options->block_ipv6 = true;
+    }
     else if (streq(p[0], "remote-random-hostname") && !p[1])
     {
         VERIFY_PERMISSION(OPT_P_GENERAL);
src/openvpn/options.h +1 -0
index 575777f5..93df21b4 100644
@@ -350,6 +350,7 @@ struct options
     bool route_delay_defined;
     struct route_option_list *routes;
     struct route_ipv6_option_list *routes_ipv6;                 /* IPv6 */
+    bool block_ipv6;
     bool route_nopull;
     bool route_gateway_via_dhcp;
     bool allow_pull_fqdn; /* as a client, allow server to push a FQDN for certain parameters */
src/openvpn/proto.c +52 -0

58 lines not on this page · raw message

src/openvpn/proto.h +39 -3

60 lines not on this page · raw message

Signature · 1 line
2.19.2
Acked-by on v7 1/1
#20 lore raw

Stared at the code (long and hard, especially the changes to udp_checksum()
- which is a welcome generalization). Looks all reasonable. One of the changes was uncrustify wrapping the call to process_ip_header() - which looked ugly before, and looks ugly afterwards, but that's not your fault.

What I like quite a lot is that this patch does not auto-add an IPv6 default route - it looked like a "why, we could do this automatically?" initially, but is coming handy if you want more selective blocking.

Tested with tun on linux, and tap on FreeBSD. Works nicely (*).

Stared at the synthesized ICMPv6 packet with tcpdump and wireshark, and both tools tell me "the packet is good" ;-)

I have test compiled for Windows (mingw). Builds fine. Have not actually run the resulting binary, but given that the changes are fully platform independent, I expect no issues here.

Server mode --block-ipv6 doesn't work correctly for me, though (but I've decided to move this towards a separate patch as it isn't blocking client side functionality and not breaking "normal" operation).

I run a "standard tun server" on linux, with --server, and --block-ipv6.

The server assigns a v6 address to the client, the client sends a packet into the tunnel:

21:15:47.179356 IP6 2001:608:3:814::1000 > 2001:608::2: ICMP6, echo request, seq 1, length 64

.. and I can see it come out of the server's tun interface towards linux:

21:15:47.192493 IP6 2001:608:3:814::1000 > 2001:608::2: ICMP6, echo request, seq 1, length 64

.. the target hosts answers, and the answer is routed into the server's tun interface:

21:15:47.192993 IP6 2001:608::2 > 2001:608:3:814::1000: ICMP6, echo reply, seq 1, length 64

.. and then the server tells the remote IPv6 host that "no, my client is not there"

21:15:47.193030 IP6 2001:608:3:814::2 > 2001:608::2: ICMP6, destination unreachable, unreachable route 2001:608:3:814::1000, length 112

.. but no ICMPv6 packet is ever sent towards the client. Looks like the multi.c code is somewhat confused about directions... without block-ipv6 on the server, things work normally...

21:20:28.961067 IP6 2001:608:3:814::1000 > 2001:608::2: ICMP6, echo request, seq 1, length 64 21:20:28.961568 IP6 2001:608::2 > 2001:608:3:814::1000: ICMP6, echo reply, seq 1, length 64 21:20:29.963421 IP6 2001:608:3:814::1000 > 2001:608::2: ICMP6, echo request, seq 2, length 64 21:20:29.963933 IP6 2001:608::2 > 2001:608:3:814::1000: ICMP6, echo reply, seq 2, length 64

Tap mode might need a bit of tweaking as well - it does work, but it blocks ND packets, instead of "user payload" (TCP SYN)...

21:22:23.923360 IP6 fd00:abcd:194:4::1007 > ff02::1:ff00:1: ICMP6, neighbor solicitation, who has fd00:abcd:194:4::1, length 32 21:22:23.923375 IP6 fd00:abcd:194:4::1 > fd00:abcd:194:4::1007: ICMP6, destination unreachable, unreachable route ff02::1:ff00:1, length 80

.. so you do not get back a "fast fail" on the payload, but it fails after ND timeout...

traceroute6 to fd00:abcd:194:4::1 (fd00:abcd:194:4::1) from fd00:abcd:194:4::1007, 64 hops max, 12 byte packets
1 fd00:abcd:194:4::1007 (fd00:abcd:194:4::1007) 3058.434 ms !A 3038.031 ms !A 3055.928 ms !A

.. so while it works, it's not as nice as it could be - maybe we should multicast packets (destination ff02::) just pass through in tap mode, so a ND relationship with the remote can be established? If I install a manual ND entry, I get the proper "quick fail from the right source":

21:27:38.686757 IP6 fd00:abcd:194:4::1007.29319 > fd00:abcd:194:4::1.33437: UDP, length 12
21:27:38.686765 IP6 fd00:abcd:194:4::1 > fd00:abcd:194:4::1007: ICMP6, destination unreachable, unreachable route fd00:abcd:194:4::1, length 68

traceroute6 to fd00:abcd:194:4::1 (fd00:abcd:194:4::1) from fd00:abcd:194:4::1007, 64 hops max, 12 byte packets
1 fd00:abcd:194:4::1 (fd00:abcd:194:4::1) 0.113 ms !N 0.072 ms !N 0.057 ms !N

$ telnet fd00:abcd:194:4::1 22
Trying fd00:abcd:194:4::1...
telnet: connect to address fd00:abcd:194:4::1: No route to host
telnet: Unable to connect to remote host

Anyway. Room for improvement :-)

Your patch has been applied to the master branch.

commit e11d2d14a9ef5311f791a9a614ab367c6f50ff11 (master)

Author: Arne Schwabe
Date:   Mon Dec 3 17:48:18 2018 +0100
     Implement block-ipv6

     Signed-off-by: Arne Schwabe <[email protected]...>
     Acked-by: Gert Doering <[email protected]...>
     Message-Id: <[email protected]...>
     URL: https://www.mail-archive.com/[email protected]/msg17977.html
     Signed-off-by: Gert Doering <[email protected]...>

--
kind regards,

Gert Doering