aboutsummaryrefslogtreecommitdiff
path: root/src/openvpn/forward.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/openvpn/forward.c')
-rw-r--r--src/openvpn/forward.c165
1 files changed, 161 insertions, 4 deletions
diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c
index f8faa81..ba0797f 100644
--- a/src/openvpn/forward.c
+++ b/src/openvpn/forward.c
@@ -1412,7 +1412,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; */
@@ -1423,6 +1425,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
@@ -1433,6 +1438,142 @@ 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 packet in 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 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;
+
+ 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)
{
@@ -1454,6 +1595,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)
{
@@ -1489,7 +1634,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 */
@@ -1507,8 +1652,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;
}
+
}
}
}
@@ -1689,7 +1844,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))
{