@@ -768,6 +768,44 @@
return ret;
}
+static bool
+ovpn_parse_float_addr(struct nlattr **attrs, struct sockaddr *out)
+{
+ if (!attrs[OVPN_A_PEER_REMOTE_PORT])
+ {
+ msg(D_DCO, "ovpn-dco: no remote port in PEER_FLOAT_NTF message");
+ return false;
+ }
+
+ if (attrs[OVPN_A_PEER_REMOTE_IPV4])
+ {
+ struct sockaddr_in *addr4 = (struct sockaddr_in *)out;
+ CLEAR(*addr4);
+ addr4->sin_family = AF_INET;
+ addr4->sin_port = nla_get_u16(attrs[OVPN_A_PEER_REMOTE_PORT]);
+ addr4->sin_addr.s_addr = nla_get_u32(attrs[OVPN_A_PEER_REMOTE_IPV4]);
+ return true;
+ }
+ else if (attrs[OVPN_A_PEER_REMOTE_IPV6]
+ && nla_len(attrs[OVPN_A_PEER_REMOTE_IPV6]) == sizeof(struct in6_addr))
+ {
+ struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)out;
+ CLEAR(*addr6);
+ addr6->sin6_family = AF_INET6;
+ addr6->sin6_port = nla_get_u16(attrs[OVPN_A_PEER_REMOTE_PORT]);
+ memcpy(&addr6->sin6_addr, nla_data(attrs[OVPN_A_PEER_REMOTE_IPV6]),
+ sizeof(addr6->sin6_addr));
+ if (attrs[OVPN_A_PEER_REMOTE_IPV6_SCOPE_ID])
+ {
+ addr6->sin6_scope_id = nla_get_u32(attrs[OVPN_A_PEER_REMOTE_IPV6_SCOPE_ID]);
+ }
+ return true;
+ }
+
+ msg(D_DCO, "ovpn-dco: no valid remote IP address in PEER_FLOAT_NTF message");
+ return false;
+}
+
/* This function parses any netlink message sent by ovpn-dco to userspace */
static int
ovpn_handle_msg(struct nl_msg *msg, void *arg)
@@ -856,6 +894,45 @@
break;
}
+ case OVPN_CMD_PEER_FLOAT_NTF:
+ {
+ if (!attrs[OVPN_A_PEER])
+ {
+ msg(D_DCO, "ovpn-dco: no peer in PEER_FLOAT_NTF message");
+ return NL_STOP;
+ }
+
+ struct nlattr *fp_attrs[OVPN_A_PEER_MAX + 1];
+ if (nla_parse_nested(fp_attrs, OVPN_A_PEER_MAX, attrs[OVPN_A_PEER],
+ NULL))
+ {
+ msg(D_DCO, "ovpn-dco: can't parse peer in PEER_FLOAT_NTF messsage");
+ return NL_STOP;
+ }
+
+ if (!fp_attrs[OVPN_A_PEER_ID])
+ {
+ msg(D_DCO, "ovpn-dco: no peer-id in PEER_FLOAT_NTF message");
+ return NL_STOP;
+ }
+ uint32_t peerid = nla_get_u32(fp_attrs[OVPN_A_PEER_ID]);
+
+ if (!ovpn_parse_float_addr(fp_attrs, (struct sockaddr *)&dco->dco_float_peer_ss))
+ {
+ return NL_STOP;
+ }
+
+ struct gc_arena gc = gc_new();
+ msg(D_DCO_DEBUG,
+ "ovpn-dco: received CMD_PEER_FLOAT_NTF, ifindex: %u, peer-id %u, address: %s",
+ ifindex, peerid, print_sockaddr((struct sockaddr *)&dco->dco_float_peer_ss, &gc));
+ dco->dco_message_peer_id = (int)peerid;
+ dco->dco_message_type = OVPN_CMD_PEER_FLOAT_NTF;
+
+ gc_free(&gc);
+ break;
+ }
+
case OVPN_CMD_KEY_SWAP_NTF:
{
if (!attrs[OVPN_A_KEYCONF])
@@ -34,6 +34,7 @@
/* Defines to avoid mismatching with other platforms */
#define OVPN_CMD_DEL_PEER OVPN_CMD_PEER_DEL_NTF
#define OVPN_CMD_SWAP_KEYS OVPN_CMD_KEY_SWAP_NTF
+#define OVPN_CMD_FLOAT_PEER OVPN_CMD_PEER_FLOAT_NTF
typedef enum ovpn_key_slot dco_key_slot_t;
typedef enum ovpn_cipher_alg dco_cipher_t;
@@ -75,6 +76,7 @@
int dco_message_peer_id;
int dco_message_key_id;
int dco_del_peer_reason;
+ struct sockaddr_storage dco_float_peer_ss;
uint64_t dco_read_bytes;
uint64_t dco_write_bytes;
} dco_context_t;
@@ -663,6 +663,7 @@
dco->dco_message_peer_id = dco->notif_buf.PeerId;
dco->dco_message_type = dco->notif_buf.Cmd;
dco->dco_del_peer_reason = dco->notif_buf.DelPeerReason;
+ dco->dco_float_peer_ss = dco->notif_buf.FloatAddress;
}
else
{
@@ -52,6 +52,7 @@
int dco_message_peer_id;
int dco_message_type;
int dco_del_peer_reason;
+ struct sockaddr_storage dco_float_peer_ss;
uint64_t dco_read_bytes;
uint64_t dco_write_bytes;
@@ -1243,6 +1243,37 @@
perf_pop();
}
+void
+extract_dco_float_peer_addr(const uint32_t peer_id,
+ struct openvpn_sockaddr *out_osaddr,
+ const struct sockaddr *float_sa)
+{
+ if (float_sa->sa_family == AF_INET)
+ {
+ struct sockaddr_in *float4 = (struct sockaddr_in *)float_sa;
+ /* DCO treats IPv4-mapped IPv6 addresses as pure IPv4. However, we need
+ * to preserve the mapping, otherwise openvpn will not be able to find
+ * the peer by its trasnport address.
+ */
+ if (out_osaddr->addr.sa.sa_family == AF_INET6
+ && IN6_IS_ADDR_V4MAPPED(&out_osaddr->addr.in6.sin6_addr))
+ {
+ memcpy(&out_osaddr->addr.in6.sin6_addr.s6_addr[12],
+ &float4->sin_addr.s_addr, sizeof(in_addr_t));
+ out_osaddr->addr.in6.sin6_port = float4->sin_port;
+ }
+ else
+ {
+ memcpy(&out_osaddr->addr.in4, float4, sizeof(struct sockaddr_in));
+ }
+ }
+ else
+ {
+ struct sockaddr_in6 *float6 = (struct sockaddr_in6 *)float_sa;
+ memcpy(&out_osaddr->addr.in6, float6, sizeof(struct sockaddr_in6));
+ }
+}
+
static void
process_incoming_dco(struct context *c)
{
@@ -196,6 +196,21 @@
void process_incoming_link_part2(struct context *c, struct link_socket_info *lsi, const uint8_t *orig_buf);
/**
+ * Transfers \c float_sa data extracted from an incoming DCO
+ * PEER_FLOAT_NTF to \c out_osaddr for later processing.
+ *
+ * @param peer_id - The id of the floating peer.
+ * @param out_osaddr - openvpn_sockaddr struct that will be filled the new
+ * address data
+ * @param float_sa - The sockaddr struct containing the data received from the
+ * DCO notification
+ *
+ */
+void
+extract_dco_float_peer_addr(uint32_t peer_id, struct openvpn_sockaddr *out_osaddr,
+ const struct sockaddr *float_sa);
+
+/**
* Write a packet to the external network interface.
* @ingroup external_multiplexer
*
@@ -3384,6 +3384,16 @@
{
process_incoming_del_peer(m, mi, dco);
}
+#if defined(TARGET_LINUX) || defined(TARGET_WIN32)
+ else if (dco->dco_message_type == OVPN_CMD_FLOAT_PEER)
+ {
+ extract_dco_float_peer_addr(peer_id, &m->top.c2.from.dest,
+ (struct sockaddr *)&dco->dco_float_peer_ss);
+ ASSERT(mi->context.c2.link_sockets[0]);
+ multi_process_float(m, mi, mi->context.c2.link_sockets[0]);
+ CLEAR(dco->dco_float_peer_ss);
+ }
+#endif /* if defined(TARGET_LINUX) || defined(TARGET_WIN32) */
else if (dco->dco_message_type == OVPN_CMD_SWAP_KEYS)
{
tls_session_soft_reset(mi->context.c2.tls_multi);
@@ -322,7 +322,7 @@
/**
* Process an incoming DCO message (from kernel space).
*
- * @param m - The single \c multi_context structur.e
+ * @param m - The single \c multi_context structure.
*
* @return
* - True, if the message was received correctly.
@@ -99,6 +99,7 @@
OVPN_CMD_KEY_SWAP,
OVPN_CMD_KEY_SWAP_NTF,
OVPN_CMD_KEY_DEL,
+ OVPN_CMD_PEER_FLOAT_NTF,
__OVPN_CMD_MAX,
OVPN_CMD_MAX = (__OVPN_CMD_MAX - 1)
@@ -149,7 +149,8 @@
typedef enum {
OVPN_CMD_DEL_PEER,
- OVPN_CMD_SWAP_KEYS
+ OVPN_CMD_SWAP_KEYS,
+ OVPN_CMD_FLOAT_PEER
} OVPN_NOTIFY_CMD;
typedef enum {
@@ -164,6 +165,7 @@
OVPN_NOTIFY_CMD Cmd;
int PeerId;
OVPN_DEL_PEER_REASON DelPeerReason;
+ struct sockaddr_storage FloatAddress;
} OVPN_NOTIFY_EVENT, * POVPN_NOTIFY_EVENT;
typedef struct _OVPN_MP_DEL_PEER {