@@ -139,9 +139,101 @@ struct ovpn_peer *ovpn_peer_new(struct ovpn_struct *ovpn, u32 id)
return ERR_PTR(ret);
}
+int ovpn_peer_reset_sockaddr(struct ovpn_peer *peer, const struct sockaddr_storage *ss,
+ const u8 *local_ip)
+{
+ struct ovpn_bind *bind;
+ size_t ip_len;
+
+ /* create new ovpn_bind object */
+ bind = ovpn_bind_from_sockaddr(ss);
+ if (IS_ERR(bind))
+ return PTR_ERR(bind);
+
+ if (local_ip) {
+ if (ss->ss_family == AF_INET) {
+ ip_len = sizeof(struct in_addr);
+ } else if (ss->ss_family == AF_INET6) {
+ ip_len = sizeof(struct in6_addr);
+ } else {
+ netdev_dbg(peer->ovpn->dev, "%s: invalid family for remote endpoint\n",
+ __func__);
+ kfree(bind);
+ return -EINVAL;
+ }
+
+ memcpy(&bind->local, local_ip, ip_len);
+ }
+
+ /* set binding */
+ ovpn_bind_reset(peer, bind);
+
+ return 0;
+}
+
#define ovpn_peer_index(_tbl, _key, _key_len) \
(jhash(_key, _key_len, 0) % HASH_SIZE(_tbl)) \
+void ovpn_peer_float(struct ovpn_peer *peer, struct sk_buff *skb)
+{
+ struct sockaddr_storage ss;
+ const u8 *local_ip = NULL;
+ struct sockaddr_in6 *sa6;
+ struct sockaddr_in *sa;
+ struct ovpn_bind *bind;
+ sa_family_t family;
+ size_t salen;
+ u32 index;
+
+ rcu_read_lock();
+ bind = rcu_dereference(peer->bind);
+ if (unlikely(!bind))
+ goto unlock;
+
+ if (likely(ovpn_bind_skb_src_match(bind, skb)))
+ goto unlock;
+
+ family = skb_protocol_to_family(skb);
+
+ if (bind->sa.in4.sin_family == family)
+ local_ip = (u8 *)&bind->local;
+
+ switch (family) {
+ case AF_INET:
+ sa = (struct sockaddr_in *)&ss;
+ sa->sin_family = AF_INET;
+ sa->sin_addr.s_addr = ip_hdr(skb)->saddr;
+ sa->sin_port = udp_hdr(skb)->source;
+ salen = sizeof(*sa);
+ break;
+ case AF_INET6:
+ sa6 = (struct sockaddr_in6 *)&ss;
+ sa6->sin6_family = AF_INET6;
+ sa6->sin6_addr = ipv6_hdr(skb)->saddr;
+ sa6->sin6_port = udp_hdr(skb)->source;
+ sa6->sin6_scope_id = ipv6_iface_scope_id(&ipv6_hdr(skb)->saddr, skb->skb_iif);
+ salen = sizeof(*sa6);
+ break;
+ default:
+ goto unlock;
+ }
+
+ netdev_dbg(peer->ovpn->dev, "%s: peer %d floated to %pIScp", __func__, peer->id, &ss);
+ ovpn_peer_reset_sockaddr(peer, (struct sockaddr_storage *)&ss, local_ip);
+
+ spin_lock_bh(&peer->ovpn->peers.lock);
+ /* remove old hashing */
+ hlist_del_init_rcu(&peer->hash_entry_transp_addr);
+ /* re-add with new transport address */
+ index = ovpn_peer_index(peer->ovpn->peers.by_transp_addr, &ss, salen);
+ hlist_add_head_rcu(&peer->hash_entry_transp_addr,
+ &peer->ovpn->peers.by_transp_addr[index]);
+ spin_unlock_bh(&peer->ovpn->peers.lock);
+
+unlock:
+ rcu_read_unlock();
+}
+
static void ovpn_peer_timer_delete_all(struct ovpn_peer *peer)
{
del_timer_sync(&peer->keepalive_xmit);
@@ -166,6 +166,7 @@ struct ovpn_peer *ovpn_peer_lookup_by_src(struct ovpn_struct *ovpn, struct sk_bu
struct ovpn_peer *ovpn_peer_lookup_id(struct ovpn_struct *ovpn, u32 peer_id);
void ovpn_peer_update_local_endpoint(struct ovpn_peer *peer, struct sk_buff *skb);
+void ovpn_peer_float(struct ovpn_peer *peer, struct sk_buff *skb);
int ovpn_peer_reset_sockaddr(struct ovpn_peer *peer, const struct sockaddr_storage *ss,
const u8 *local_ip);
@@ -81,6 +81,9 @@ static int ovpn_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
__func__, peer_id);
goto drop;
}
+
+ /* check if this peer changed it's IP address and update state */
+ ovpn_peer_float(peer, skb);
}
if (!peer) {
A peer connected via UDP may change its IP address without reconnecting (float). Add support for detecting and updating the new peer IP/port in case of floating. Signed-off-by: Antonio Quartulli <antonio@openvpn.net> --- drivers/net/ovpn/peer.c | 92 +++++++++++++++++++++++++++++++++++++++++ drivers/net/ovpn/peer.h | 1 + drivers/net/ovpn/udp.c | 3 ++ 3 files changed, 96 insertions(+)