Acked-by: Gert Doering <gert@greenie.muc.de>
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 <arne@rfc2549.org>
Acked-by: Gert Doering <gert@greenie.muc.de>
Message-Id: <20181203164818.15756-1-arne@rfc2549.org>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg17977.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>
--
kind regards,
Gert Doering
@@ -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
@@ -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)
{
@@ -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,
+ 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)
@@ -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 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 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))
{
@@ -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);
@@ -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;
@@ -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);
@@ -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 */
@@ -98,6 +98,58 @@ 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
@@ -95,9 +95,10 @@ struct openvpn_iphdr {
uint8_t ttl;
-#define OPENVPN_IPPROTO_IGMP 2 /* IGMP protocol */
-#define OPENVPN_IPPROTO_TCP 6 /* TCP protocol */
-#define OPENVPN_IPPROTO_UDP 17 /* UDP protocol */
+#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
@@ -265,6 +284,23 @@ bool is_ipv4(int tunnel_type, struct buffer *buf);
bool is_ipv6(int tunnel_type, struct buffer *buf);
+/**
+ * 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 The calculated checksum in host order
+ */
+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);
+
#ifdef PACKET_TRUNCATION_CHECK
void ipv4_packet_size_verify(const uint8_t *data,
const int size,