diff options
Diffstat (limited to 'src/openvpn/forward.c')
-rw-r--r-- | src/openvpn/forward.c | 165 |
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, ðhdr, 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)) { |