Hi,
On 12/11/17 00:07, Arne Schwabe wrote:
> 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(-)
>
> diff --git a/doc/openvpn.8 b/doc/openvpn.8
> index a4189ac2..26b5f7de 100644
> --- a/doc/openvpn.8
> +++ b/doc/openvpn.8
> @@ -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
> diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c
> index 1b7455bb..21cce90b 100644
> --- a/src/openvpn/forward.c
> +++ b/src/openvpn/forward.c
> @@ -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
>
> + }
> + 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?
> 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;
> +
> + /* 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.
> +/**
> + * 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)
> +{
> +#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.
> + 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.
> +
> + 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.
> + pip6out.nexthdr = OPENVPN_IPPROTO_ICMPV6;
> +
> +
one empty line is enough.
> + /*
> + * 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.
> + 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));
> +
> + /* 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?
> + /* 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, ðhdr, 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 */
> @@ -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
> + 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))
> {
> diff --git a/src/openvpn/forward.h b/src/openvpn/forward.h
> index 9fde5a30..62199f9e 100644
> --- a/src/openvpn/forward.h
> +++ b/src/openvpn/forward.h
> @@ -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);
>
> diff --git a/src/openvpn/misc.c b/src/openvpn/misc.c
> index 001fe1c4..18f0cb5b 100644
> --- a/src/openvpn/misc.c
> +++ b/src/openvpn/misc.c
> @@ -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 '||'
> {
> 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?
> if (pid == (pid_t)0) /* child side */
> {
> execve(cmd, argv, envp);
> diff --git a/src/openvpn/options.c b/src/openvpn/options.c
> index 641a26e2..daa9ff05 100644
> --- a/src/openvpn/options.c
> +++ b/src/openvpn/options.c
> @@ -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");
> + }
> +
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.
> + 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?
> #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);
> diff --git a/src/openvpn/options.h b/src/openvpn/options.h
> index 496c1143..38e9e431 100644
> --- a/src/openvpn/options.h
> +++ b/src/openvpn/options.h
> @@ -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 */
> diff --git a/src/openvpn/proto.h b/src/openvpn/proto.h
> index 57f25c90..7d300af9 100644
> --- a/src/openvpn/proto.h
> +++ b/src/openvpn/proto.h
> @@ -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?
> 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
>
Cheers,
--
Antonio Quartulli
signature.asc
Description: OpenPGP digital signature
------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________ Openvpn-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/openvpn-devel
