Skip to contentSkip to conversation

PF: implement support for IPv6 subnets

PATCH In review Antonio Quartulli 2 participants 4 messages Dec 2, 2017 – Feb 23, 2018 1 message not in the archive

lore
Share & tools
lore
#0 lore raw

PATCH 1/2 PF: implement support for IPv6 subnets

The PF subnets component has been extended to also accept IPv6 networks. The syntax is exactly the same as the IPv4 subnets.

The user only needs to list the IPv6 networks in the same "[SUBNETS DROP/ACCEPT]" block as the IPv4 ones.

Example:

[SUBNETS ACCEPT]
-180.180.0.0/16
-2001:caca:beef::/48

The PF rule parser was improved to also accept subnets without any
netmask: a host address (full netmask) is assumed in this case.

---
 src/openvpn/mroute.c |   1 +
 src/openvpn/mroute.h |  19 +++
 src/openvpn/pf.c     | 447 ++++++++++++++++++++++++++++++++++++++++++++-------
 src/openvpn/pf.h     |  13 +-
 src/openvpn/route.h  |  23 +++
 src/openvpn/socket.c |  16 ++
 src/openvpn/socket.h |   9 ++
 7 files changed, 465 insertions(+), 63 deletions(-)
src/openvpn/mroute.c +1 -0
index 74ee360c..8b364efd 100644
@@ -265,6 +265,7 @@ mroute_extract_addr_ether(struct mroute_addr *src,
             {
                 switch (ntohs(eth->proto))
                 {
+                    case OPENVPN_ETH_P_IPV6:
                     case OPENVPN_ETH_P_IPV4:
                         ret |= (mroute_extract_addr_ip(esrc, edest, &b) << MROUTE_SEC_SHIFT);
                         break;
src/openvpn/mroute.h +19 -0
index 35361fbd..eacb1239 100644
@@ -257,6 +257,25 @@ in_addr_t_from_mroute_addr(const struct mroute_addr *addr)
     }
 }
 
+/**
+ * Extract host address from mroute_addr object.
+ *
+ * @param addr	mroute object to extract the address from
+ */
+static inline struct in6_addr
+in6_addr_from_mroute_addr(const struct mroute_addr *addr)
+{
+    if (((addr->type & MR_ADDR_MASK) == MR_ADDR_IPV6) && (addr->netbits == 0) &&
+        (addr->len == 16))
+    {
+        return addr->v6.addr;
+    }
+    else
+    {
+        return in6addr_any;
+    }
+}
+
 static inline void
 mroute_addr_reset(struct mroute_addr *ma)
 {
src/openvpn/pf.c +386 -61

531 lines not on this page · raw message

src/openvpn/pf.h +11 -2
index b839fd2e..de21381f 100644
@@ -34,14 +34,23 @@
 struct context;
 
 struct ipv4_subnet {
-    bool exclude;
     in_addr_t network;
     in_addr_t netmask;
 };
 
+struct ipv6_subnet {
+    struct in6_addr network;
+    struct in6_addr netmask;
+};
+
 struct pf_subnet {
     struct pf_subnet *next;
-    struct ipv4_subnet rule;
+    int addr_family;
+    bool exclude;
+    union {
+        struct ipv4_subnet v4;
+        struct ipv6_subnet v6;
+    } rule;
 };
 
 struct pf_subnet_set {
src/openvpn/route.h +23 -0
index 6414d6c9..91520c65 100644
@@ -375,6 +375,29 @@ netbits_to_netmask(const int netbits)
     return mask;
 }
 
+/**
+ * Convert a netmask in the form of "netbits" to an actual bitmask
+ *
+ * @param netbits number of bits representing the netmask
+ */
+static inline struct in6_addr
+netbits_to_netmask_v6(const int netbits)
+{
+    struct in6_addr mask = {0};
+    int full = netbits / 8;
+    int rest = netbits % 8;
+
+    /* fill whole bytes first.. */
+    memset(&mask, 0xff, full);
+    if (rest != 0)
+    {
+        /* ..partly fill the last byte now */
+        mask.s6_addr[full] = (0xff00 >> rest);
+    }
+
+    return mask;
+}
+
 static inline bool
 route_list_vpn_gateway_needed(const struct route_list *rl)
 {
src/openvpn/socket.c +16 -0
index 0fc91f21..c4f37b3b 100644
@@ -564,6 +564,22 @@ openvpn_inet_aton(const char *dotted_quad, struct in_addr *addr)
     }
 }
 
+int
+openvpn_inet_aton_v6(const char *addr_str, struct in6_addr *addr)
+{
+    CLEAR (*addr);
+
+    if (inet_pton (AF_INET6, addr_str, addr) == 1)
+    {
+        return OIA_IP; /* good well formatted IPv6 */
+    }
+
+    if (string_class (addr_str, CC_XDIGIT|CC_COLON, 0))
+        return OIA_ERROR;    /* probably a badly formatted IPv6 addr */
+    else
+        return OIA_HOSTNAME; /* probably a hostname */
+}
+
 bool
 ip_addr_dotted_quad_safe(const char *dotted_quad)
 {
src/openvpn/socket.h +9 -0
index 2d7f2187..2369956b 100644
@@ -462,6 +462,15 @@ void link_socket_update_buffer_sizes(struct link_socket *ls, int rcvbuf, int snd
 #define OIA_ERROR     -1
 int openvpn_inet_aton(const char *dotted_quad, struct in_addr *addr);
 
+/**
+ * Convert an IPv6 from text notation to in6_addr. Error reporting is
+ * enhanced compared to the pure inet_pton.
+ *
+ * @param addr_str	the address in text form
+ * @param addr		pointer where the result has to be stored on success
+ */
+int openvpn_inet_aton_v6(const char *addr_str, struct in6_addr *addr);
+
 /* integrity validation on pulled options */
 bool ip_addr_dotted_quad_safe(const char *dotted_quad);
 
Signature · 1 line
2.15.1
#1 lore raw

PATCH 2/2 PF: never drop essential ICMPv6 packets

Some ICMPv6 packets can't be dropped otherwise the entire overlaying network layer (IPv6) would just stop working.

Such packets are described in RFC4890, sec. 4.4.1.

Improve the mroute packet parsing routine in order to detect these specific packets types and thus avoid PF to drop them.

This way, when PF is enabled, the user won't need to whitelist any specific multicats IPv6 address. PF will just work as expected.

---
 src/openvpn/mroute.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/openvpn/mroute.h |  4 ++-
 src/openvpn/multi.c  | 19 +++++++++++---
 src/openvpn/proto.h  | 16 ++++++++++++
 4 files changed, 106 insertions(+), 6 deletions(-)
src/openvpn/mroute.c +72 -1
index 8b364efd..68a440f6 100644
@@ -126,6 +126,71 @@ mroute_is_mcast_ipv6(const struct in6_addr addr)
 }
 
 #ifdef ENABLE_PF
+/**
+ * Return the ICMPv6 sub-packet type
+ *
+ * @param buf packet buffer pointing to the beginning of the IMCPv6 header
+ */
+static uint8_t
+mroute_icmp6_get_type(const struct buffer *buf)
+{
+    const struct openvpn_icmp6hdr *icmp6;
+
+    if (BLEN (buf) < (int) sizeof (struct openvpn_icmp6hdr))
+    {
+        return 0;
+    }
+
+    icmp6 = (const struct openvpn_icmp6hdr *) BPTR (buf);
+
+    return icmp6->icmp6_type;
+}
+
+/**
+ * Check if this IPv6 packet is essential to IPv6 basic communications and,
+ * therefore, should not be dropped (based on RFC4890, sec. 4.4.1)
+ *
+ * @param ipv6	pointer to the current IPv6 header
+ * @param buf	packet buffer pointing to the beginning of the IPv6 header
+ */
+static bool
+mroute_ipv6_should_not_drop(const struct openvpn_ipv6hdr *ipv6,
+                            const struct buffer *buf)
+{
+    struct buffer b = *buf;
+    uint8_t type;
+
+    /* packets to "save" are a subset of ICMPv6 */
+    if (ipv6->nexthdr != IPPROTO_ICMPV6)
+    {
+        return false;
+    }
+
+    if (!buf_advance (&b, sizeof (struct openvpn_ipv6hdr)))
+    {
+        return false;
+    }
+
+    type = mroute_icmp6_get_type (&b);
+
+    switch (type)
+    {
+        /*
+         * By following the guideline of RFC4890, sec. 4.4.1, the
+         * following are the ICMPv6 packet types that are strictly
+         * required to let a host join a IPv6 network.
+         * Therefore, such packets can't be dropped by PF.
+         */
+        case OPENVPN_ND_ROUTER_SOLICIT:
+        case OPENVPN_ND_ROUTER_ADVERT:
+        case OPENVPN_ND_NEIGHBOR_SOLICIT:
+        case OPENVPN_ND_NEIGHBOR_ADVERT:
+        case OPENVPN_ND_INVERSE_SOLICIT:
+        case OPENVPN_ND_INVERSE_ADVERT:
+            return true;
+    }
+    return false;
+}
 
 static unsigned int
 mroute_extract_addr_arp(struct mroute_addr *src,
@@ -212,9 +277,15 @@ mroute_extract_addr_ip(struct mroute_addr *src, struct mroute_addr *dest,
                     }
 
                     ret |= MROUTE_EXTRACT_SUCCEEDED;
+
+#ifdef ENABLE_PF
+                    if (mroute_ipv6_should_not_drop (ipv6, buf))
+                    {
+                        ret |= MROUTE_EXTRACT_NO_DROP;
+                    }
+#endif
                 }
                 break;
-
             default:
                 msg(M_WARN, "IP packet with unknown IP version=%d seen",
                     OPENVPN_IPH_GET_VER(*BPTR(buf)));
src/openvpn/mroute.h +3 -1
index eacb1239..ab04c98a 100644
@@ -41,13 +41,15 @@
 #define MROUTE_EXTRACT_BCAST     (1<<1)
 #define MROUTE_EXTRACT_MCAST     (1<<2)
 #define MROUTE_EXTRACT_IGMP      (1<<3)
+#define MROUTE_EXTRACT_NO_DROP   (1<<4)
 
 #define MROUTE_SEC_EXTRACT_SUCCEEDED (1<<(0+MROUTE_SEC_SHIFT))
 #define MROUTE_SEC_EXTRACT_BCAST     (1<<(1+MROUTE_SEC_SHIFT))
 #define MROUTE_SEC_EXTRACT_MCAST     (1<<(2+MROUTE_SEC_SHIFT))
 #define MROUTE_SEC_EXTRACT_IGMP      (1<<(3+MROUTE_SEC_SHIFT))
+#define MROUTE_SEC_EXTRACT_NO_DROP   (1<<(4+MROUTE_SEC_SHIFT))
 
-#define MROUTE_SEC_SHIFT         4
+#define MROUTE_SEC_SHIFT         5
 
 /*
  * Choose the largest address possible with
src/openvpn/multi.c +15 -4
index 82a0b9d9..8b2afcd5 100644
@@ -2627,7 +2627,8 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst
             {
 #ifdef ENABLE_PF
                 struct mroute_addr edest;
-                mroute_addr_reset(&edest);
+                mroute_addr_reset (&edest);
+                unsigned int no_drop;
 #endif
                 /* extract packet source and dest addresses */
                 mroute_flags = mroute_extract_addr_from_packet(&src,
@@ -2676,10 +2677,20 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst
                             }
                         }
 #ifdef ENABLE_PF
-                        if (c->c2.to_tun.len && !pf_addr_test(c, &edest, "tap_dest_addr"))
+                        /*
+                         * If a packet is marked as "do not drop", then no PF
+                         * test has to be performed on it, but has to be allowed
+                         * right away.
+                         */
+                        no_drop = MROUTE_EXTRACT_NO_DROP | MROUTE_SEC_EXTRACT_NO_DROP;
+                        if ((mroute_flags & MROUTE_SEC_EXTRACT_SUCCEEDED) &&
+                            !(mroute_flags & no_drop) && c->c2.to_tun.len &&
+                            !pf_addr_test(c, &edest, "tap_dest_addr"))
                         {
-                            msg(D_PF_DROPPED, "PF: client -> addr[%s] packet dropped by TAP packet filter",
-                                mroute_addr_print_ex(&edest, MAPF_SHOW_ARP, &gc));
+                            msg(D_PF_DROPPED,
+                                "PF: client -> addr[%s] packet dropped by TAP packet filter",
+                                mroute_addr_print_ex(&edest, MAPF_SHOW_ARP,
+                                                     &gc));
                             c->c2.to_tun.len = 0;
                         }
 #endif
src/openvpn/proto.h +16 -0
index 57f25c90..48acc39e 100644
@@ -120,6 +120,22 @@ struct openvpn_ipv6hdr {
     struct  in6_addr daddr;
 };
 
+/*
+ * ICMPv6 header
+ */
+struct openvpn_icmp6hdr {
+#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;
+
+    uint8_t		icmp6_code;
+    uint16_t	icmp6_cksum;
+    uint8_t		icmp6_dataun[4];
+};
 
 /*
  * UDP header
Signature · 1 line
2.15.1

A message that is not in the archive (sent off-list or not yet received): [email protected]...

#3 lore raw

Re: [Openvpn-devel] [PATCH 2/2] PF: never drop essential ICMPv6 packets

Quoted 30 lines · Arne Schwabe

Am 02.12.17 um 17:24 schrieb Antonio Quartulli:

Quoted 11 lines

Some ICMPv6 packets can't be dropped otherwise the entire overlaying network layer (IPv6) would just stop working.

Such packets are described in RFC4890, sec. 4.4.1.

Improve the mroute packet parsing routine in order to detect these specific packets types and thus avoid PF to drop them.

This way, when PF is enabled, the user won't need to whitelist any specific multicats IPv6 address. PF will just work as expected.

(&b);

Quoted 16 lines
src/openvpn/mroute.c +16 -0
+
+    switch (type)
+    {
+        /*
+         * By following the guideline of RFC4890, sec. 4.4.1, the
+         * following are the ICMPv6 packet types that are strictly
+         * required to let a host join a IPv6 network.
+         * Therefore, such packets can't be dropped by PF.
+         */
+        case OPENVPN_ND_ROUTER_SOLICIT:
+        case OPENVPN_ND_ROUTER_ADVERT:
+        case OPENVPN_ND_NEIGHBOR_SOLICIT:
+        case OPENVPN_ND_NEIGHBOR_ADVERT:
+        case OPENVPN_ND_INVERSE_SOLICIT:
+        case OPENVPN_ND_INVERSE_ADVERT:
+            return true;
Quoted 8 lines

src/openvpn/mroute.c mroute_is_mcast_ipv6(const struct in6_addr addr) · line 167

Quoted 3 lines
src/openvpn/mroute.c +3 -0
+    }
+    return false;
+}

I think we should still drop tehse if they do not target the local network. Otherwise you can just send all information hidden in these packet types.

You mean dropping packets not a having link-local address as destination (for unicasts)?

Cheers,

Signature · 1 line
Antonio Quartulli
#4 lore raw

Re: [Openvpn-devel] [PATCH 2/2] PF: never drop essential ICMPv6 packets

Hi,

any chance this can be moved forward ?

I have tested a server on Windows 10 and Linux (Various) and it all appears to work ok.

The question below appears to be an outstanding issue.

Thanks

Quoted 45 lines · Antonio Quartulli
Quoted 30 lines · Arne Schwabe

Am 02.12.17 um 17:24 schrieb Antonio Quartulli:

Quoted 11 lines

Some ICMPv6 packets can't be dropped otherwise the entire overlaying network layer (IPv6) would just stop working.

Such packets are described in RFC4890, sec. 4.4.1.

Improve the mroute packet parsing routine in order to detect these specific packets types and thus avoid PF to drop them.

This way, when PF is enabled, the user won't need to whitelist any specific multicats IPv6 address. PF will just work as expected.

(&b);

Quoted 16 lines
src/openvpn/mroute.c +16 -0
+
+    switch (type)
+    {
+        /*
+         * By following the guideline of RFC4890, sec. 4.4.1, the
+         * following are the ICMPv6 packet types that are strictly
+         * required to let a host join a IPv6 network.
+         * Therefore, such packets can't be dropped by PF.
+         */
+        case OPENVPN_ND_ROUTER_SOLICIT:
+        case OPENVPN_ND_ROUTER_ADVERT:
+        case OPENVPN_ND_NEIGHBOR_SOLICIT:
+        case OPENVPN_ND_NEIGHBOR_ADVERT:
+        case OPENVPN_ND_INVERSE_SOLICIT:
+        case OPENVPN_ND_INVERSE_ADVERT:
+            return true;
Quoted 8 lines

src/openvpn/mroute.c mroute_is_mcast_ipv6(const struct in6_addr addr) · line 167

Quoted 3 lines
src/openvpn/mroute.c +3 -0
+    }
+    return false;
+}

I think we should still drop tehse if they do not target the local network. Otherwise you can just send all information hidden in these packet types.

You mean dropping packets not a having link-local address as destination (for unicasts)?