Skip to content

Commit cf1044c

Browse files
committed
Fix EPERM and socket mismatch between selected and succeeded pairs when using bridge (Docker) interfaces. (#84)
1 parent 4854d81 commit cf1044c

File tree

2 files changed

+160
-52
lines changed

2 files changed

+160
-52
lines changed

lib/ex_ice/priv/ice_agent.ex

Lines changed: 57 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2210,38 +2210,70 @@ defmodule ExICE.Priv.ICEAgent do
22102210
end
22112211

22122212
defp get_or_create_local_cand(ice_agent, xor_addr, conn_check_pair) do
2213+
conn_check_local_cand = Map.fetch!(ice_agent.local_cands, conn_check_pair.local_cand_id)
2214+
22132215
local_cand =
22142216
find_local_cand(Map.values(ice_agent.local_cands), xor_addr.address, xor_addr.port)
22152217

2216-
if local_cand do
2217-
{local_cand, ice_agent}
2218-
else
2219-
# prflx candidate sec 7.2.5.3.1
2220-
# TODO calculate correct prio and foundation
2221-
local_cand = Map.fetch!(ice_agent.local_cands, conn_check_pair.local_cand_id)
2218+
cond do
2219+
# When we try to send UDP datagram from bridge interfaces, that can be used to create local candidates,
2220+
# our source IP address is translated from bridge one to our physical network interface card address.
22222221

2223-
priority =
2224-
Candidate.priority!(ice_agent.local_preferences, local_cand.base.base_address, :prflx)
2225-
2226-
cand =
2227-
Candidate.Prflx.new(
2228-
address: xor_addr.address,
2229-
port: xor_addr.port,
2230-
base_address: local_cand.base.base_address,
2231-
base_port: local_cand.base.base_port,
2232-
priority: priority,
2233-
transport_module: ice_agent.transport_module,
2234-
socket: local_cand.base.socket
2235-
)
2222+
# This behavior can cause specific scenarios to arise:
22362223

2237-
Logger.debug("Adding new local prflx candidate: #{inspect(cand)}")
2224+
# L - local side
2225+
# R - remote side
2226+
# RC1 - remote candidate
22382227

2239-
ice_agent = %__MODULE__{
2240-
ice_agent
2241-
| local_cands: Map.put(ice_agent.local_cands, cand.base.id, cand)
2242-
}
2228+
# 1. L opens socket on interface 1 (I1), port 5000 - first local candidate (LC1)
2229+
# 2. L opens socket on interface 2 (I2), port 5000 - second local candidate (LC2)
2230+
# 3. L sends a connectivity check from LC1 to RC1.
2231+
# Given LC1 operates via I1, which is a bridge interface, its source address is rewritten to I2.
2232+
# This also creates a mapping in host's NAT from I1:5000 to I2:5000.
2233+
# 4. R perceives the request from L as originating from I2, port 5000, and responds successfully to I2, port 5000
2234+
# 5. This response arrives to the I1 port 5000 (because of the mapping in host's NAT).
2235+
# L notices that R recognized its check as one coming from I2, port 5000.
2236+
2237+
# At this moment, sending anything from I2:5000 would require OS to create another mapping in its NAT table from I2:5000 to I2:5000.
2238+
# However, because there is already an existing NAT mapping from I1:5000 to I2:5000 this send operation will fail and return an EPERM error.
2239+
2240+
# We consistently use the discovered pair socket for sending.
2241+
# Therefore, we cannot use LC2-RC1 as a valid pair discovered through a check on LC1-RC1.
2242+
# Attempting to send anything from LC1-RC1 would actually involve using the LC2 socket.
2243+
# This action is not possible while the mapping from I1:5000 to I2:5000 exists.
2244+
local_cand && local_cand.base.socket == conn_check_local_cand.base.socket ->
2245+
{local_cand, ice_agent}
2246+
2247+
local_cand ->
2248+
{conn_check_local_cand, ice_agent}
2249+
2250+
true ->
2251+
# prflx candidate sec 7.2.5.3.1
2252+
# TODO calculate correct prio and foundation
2253+
local_cand = conn_check_local_cand
22432254

2244-
{cand, ice_agent}
2255+
priority =
2256+
Candidate.priority!(ice_agent.local_preferences, local_cand.base.base_address, :prflx)
2257+
2258+
cand =
2259+
Candidate.Prflx.new(
2260+
address: xor_addr.address,
2261+
port: xor_addr.port,
2262+
base_address: local_cand.base.base_address,
2263+
base_port: local_cand.base.base_port,
2264+
priority: priority,
2265+
transport_module: ice_agent.transport_module,
2266+
socket: local_cand.base.socket
2267+
)
2268+
2269+
Logger.debug("Adding new local prflx candidate: #{inspect(cand)}")
2270+
2271+
ice_agent = %__MODULE__{
2272+
ice_agent
2273+
| local_cands: Map.put(ice_agent.local_cands, cand.base.id, cand)
2274+
}
2275+
2276+
{cand, ice_agent}
22452277
end
22462278
end
22472279

0 commit comments

Comments
 (0)