Skip to content

Commit 6b49371

Browse files
committed
Add set_multicast_send_interface() to PacketPeerUDP
1 parent 6d6e822 commit 6b49371

10 files changed

Lines changed: 153 additions & 0 deletions

File tree

core/io/net_socket.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ class NetSocket : public RefCounted {
117117
virtual void set_reuse_address_enabled(bool p_enabled) = 0;
118118
virtual Error join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) = 0;
119119
virtual Error leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) = 0;
120+
virtual Error set_multicast_send_interface(const String &p_if_name) = 0;
120121

121122
virtual ~NetSocket() {}
122123
};

core/io/packet_peer_udp.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,30 @@ Error PacketPeerUDP::leave_multicast_group(IPAddress p_multi_address, const Stri
6868
return _sock->leave_multicast_group(p_multi_address, p_if_name);
6969
}
7070

71+
Error PacketPeerUDP::set_multicast_send_interface(const String &p_if_name) {
72+
ERR_FAIL_COND_V(_sock.is_null(), ERR_UNAVAILABLE);
73+
ERR_FAIL_COND_V(p_if_name.is_empty(), ERR_INVALID_PARAMETER);
74+
75+
Error err = OK;
76+
77+
if (!_sock->is_open()) {
78+
IP::Type ip_type = IP::TYPE_ANY;
79+
// Later, in this file, this is used:
80+
// IP::Type ip_type = peer_addr.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6
81+
// But, TYPE_ANY is be the default when calling PacketPeerUDP.bind(), so that is why I used it here
82+
err = _sock->open(NetSocket::Family::INET, NetSocket::TYPE_UDP, ip_type);
83+
ERR_FAIL_COND_V(err != OK, err);
84+
_sock->set_blocking_enabled(false);
85+
_sock->set_broadcasting_enabled(broadcast);
86+
}
87+
88+
err = _sock->set_multicast_send_interface(p_if_name);
89+
ERR_FAIL_COND_V(err != OK, err);
90+
91+
// err is at this point guaranteed to be OK, but keep it simple.
92+
return err;
93+
}
94+
7195
String PacketPeerUDP::_get_packet_ip() const {
7296
return String(get_packet_address());
7397
}
@@ -373,6 +397,7 @@ void PacketPeerUDP::_bind_methods() {
373397
ClassDB::bind_method(D_METHOD("set_broadcast_enabled", "enabled"), &PacketPeerUDP::set_broadcast_enabled);
374398
ClassDB::bind_method(D_METHOD("join_multicast_group", "multicast_address", "interface_name"), &PacketPeerUDP::join_multicast_group);
375399
ClassDB::bind_method(D_METHOD("leave_multicast_group", "multicast_address", "interface_name"), &PacketPeerUDP::leave_multicast_group);
400+
ClassDB::bind_method(D_METHOD("set_multicast_send_interface", "interface_name"), &PacketPeerUDP::set_multicast_send_interface);
376401
}
377402

378403
PacketPeerUDP::PacketPeerUDP() :

core/io/packet_peer_udp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ class PacketPeerUDP : public PacketPeer {
9292
void set_broadcast_enabled(bool p_enabled);
9393
Error join_multicast_group(IPAddress p_multi_address, const String &p_if_name);
9494
Error leave_multicast_group(IPAddress p_multi_address, const String &p_if_name);
95+
Error set_multicast_send_interface(const String &p_if_name);
9596

9697
PacketPeerUDP();
9798
~PacketPeerUDP();

doc/classes/PacketPeerUDP.xml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,22 @@
127127
[b]Note:[/b] [method set_broadcast_enabled] must be enabled before sending packets to a broadcast address (e.g. [code]255.255.255.255[/code]).
128128
</description>
129129
</method>
130+
<method name="set_multicast_send_interface">
131+
<return type="int" enum="Error" />
132+
<param index="0" name="interface_name" type="String" />
133+
<description>
134+
Selects the interface that will be used to send multicast packets.
135+
To get interface names, use [method IP.get_local_interfaces]
136+
[codeblock lang=gdscript]
137+
var peer := PacketPeerUDP.new()
138+
peer.bind(0)
139+
peer.set_dest_address("ff12::1234", 44444)
140+
# Forces the wlan0 interface
141+
peer.set_multicast_send_interface("wlan0")
142+
peer.put_packet("Join me!".to_utf8_buffer())
143+
[/codeblock]
144+
</description>
145+
</method>
130146
<method name="wait">
131147
<return type="int" enum="Error" />
132148
<description>

drivers/unix/net_socket_unix.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,4 +853,55 @@ Error NetSocketUnix::leave_multicast_group(const IPAddress &p_multi_address, con
853853
return _change_multicast_group(p_multi_address, p_if_name, false);
854854
}
855855

856+
Error NetSocketUnix::set_multicast_send_interface(const String &p_if_name) {
857+
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
858+
859+
HashMap<String, IP::Interface_Info> ifaces;
860+
IP::get_singleton()->get_local_interfaces(&ifaces);
861+
862+
const IP::Interface_Info *match = nullptr;
863+
for (const KeyValue<String, IP::Interface_Info> &E : ifaces) {
864+
if (E.value.name == p_if_name) {
865+
match = &E.value;
866+
break;
867+
}
868+
}
869+
ERR_FAIL_COND_V_MSG(!match, ERR_INVALID_PARAMETER, "Interface not found: " + p_if_name);
870+
871+
int if_index = match->index.to_int();
872+
873+
bool do_ipv4 = (_ip_type == IP::TYPE_IPV4) || (_ip_type == IP::TYPE_ANY);
874+
bool do_ipv6 = (_ip_type == IP::TYPE_IPV6) || (_ip_type == IP::TYPE_ANY);
875+
876+
if (do_ipv6) {
877+
if (setsockopt(_sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &if_index, sizeof(if_index)) != 0) {
878+
// TODO: Try IP_MULTICAST_IF even if this failed.
879+
WARN_PRINT("Error when setting IPV6_MULTICAST_IF.");
880+
return FAILED;
881+
}
882+
}
883+
884+
if (do_ipv4) {
885+
IPAddress ipv4;
886+
for (const IPAddress &ip : match->ip_addresses) {
887+
if (ip.is_ipv4()) {
888+
ipv4 = ip;
889+
break;
890+
}
891+
}
892+
if (!ipv4.is_valid()) {
893+
WARN_PRINT("Unable to find the corresponding IPv4 address.");
894+
return ERR_CANT_RESOLVE;
895+
}
896+
struct in_addr addr;
897+
memcpy(&addr, ipv4.get_ipv4(), sizeof(addr));
898+
if (setsockopt(_sock, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr)) != 0) {
899+
WARN_PRINT("Error when setting IP_MULTICAST_IF.");
900+
return FAILED;
901+
}
902+
}
903+
904+
return OK;
905+
}
906+
856907
#endif // UNIX_ENABLED && !UNIX_SOCKET_UNAVAILABLE

drivers/unix/net_socket_unix.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ class NetSocketUnix : public NetSocket {
111111
virtual void set_reuse_address_enabled(bool p_enabled) override;
112112
virtual Error join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override;
113113
virtual Error leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override;
114+
virtual Error set_multicast_send_interface(const String &p_if_name) override;
114115

115116
NetSocketUnix();
116117
~NetSocketUnix() override;

drivers/windows/net_socket_winsock.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,4 +621,55 @@ Error NetSocketWinSock::leave_multicast_group(const IPAddress &p_multi_address,
621621
return _change_multicast_group(p_multi_address, p_if_name, false);
622622
}
623623

624+
Error NetSocketWinSock::set_multicast_send_interface(const String &p_if_name) {
625+
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
626+
627+
HashMap<String, IP::Interface_Info> ifaces;
628+
IP::get_singleton()->get_local_interfaces(&ifaces);
629+
630+
const IP::Interface_Info *match = nullptr;
631+
for (const KeyValue<String, IP::Interface_Info> &E : ifaces) {
632+
if (E.value.name == p_if_name) {
633+
match = &E.value;
634+
break;
635+
}
636+
}
637+
ERR_FAIL_COND_V_MSG(!match, ERR_INVALID_PARAMETER, "Interface not found: " + p_if_name);
638+
639+
int if_index = match->index.to_int();
640+
641+
bool do_ipv4 = (_ip_type == IP::TYPE_IPV4) || (_ip_type == IP::TYPE_ANY);
642+
bool do_ipv6 = (_ip_type == IP::TYPE_IPV6) || (_ip_type == IP::TYPE_ANY);
643+
644+
if (do_ipv6) {
645+
if (setsockopt(_sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char *)&if_index, sizeof(if_index)) != 0) {
646+
// TODO: Try IP_MULTICAST_IF even if this failed.
647+
WARN_PRINT("Error when setting IPV6_MULTICAST_IF.");
648+
return FAILED;
649+
}
650+
}
651+
652+
if (do_ipv4) {
653+
IPAddress ipv4;
654+
for (const IPAddress &ip : match->ip_addresses) {
655+
if (ip.is_ipv4()) {
656+
ipv4 = ip;
657+
break;
658+
}
659+
}
660+
if (!ipv4.is_valid()) {
661+
WARN_PRINT("Unable to find the corresponding IPv4 address.");
662+
return ERR_CANT_RESOLVE;
663+
}
664+
DWORD addr; // DWORD is 4 bytes
665+
memcpy(&addr, ipv4.get_ipv4(), 4);
666+
if (setsockopt(_sock, IPPROTO_IP, IP_MULTICAST_IF, (char *)&addr, sizeof(addr)) != 0) {
667+
WARN_PRINT("Error when setting IP_MULTICAST_IF.");
668+
return FAILED;
669+
}
670+
}
671+
672+
return OK;
673+
}
674+
624675
#endif // WINDOWS_ENABLED

drivers/windows/net_socket_winsock.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ class NetSocketWinSock : public NetSocket {
9393
virtual void set_reuse_address_enabled(bool p_enabled) override;
9494
virtual Error join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override;
9595
virtual Error leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override;
96+
virtual Error set_multicast_send_interface(const String &p_if_name) override;
9697

9798
NetSocketWinSock();
9899
~NetSocketWinSock() override;

platform/web/net_socket_web.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,5 @@ class NetSocketWeb : public NetSocket {
7070
virtual void set_reuse_address_enabled(bool p_enabled) override {}
7171
virtual Error join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override { return ERR_UNAVAILABLE; }
7272
virtual Error leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override { return ERR_UNAVAILABLE; }
73+
virtual Error set_multicast_send_interface(const String &p_if_name) override { return ERR_UNAVAILABLE; }
7374
};

tests/core/io/test_stream_peer_tcp.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ class MockNetSocket : public NetSocket {
6767
virtual void set_reuse_address_enabled(bool p_enabled) override;
6868
virtual Error join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override;
6969
virtual Error leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override;
70+
virtual Error set_multicast_send_interface(const String &p_if_name) override;
7071

7172
Address host_addr;
7273
Address dest_ip;
@@ -207,6 +208,10 @@ Error MockNetSocket::leave_multicast_group(const IPAddress &p_multi_address, con
207208
return OK;
208209
}
209210

211+
Error MockNetSocket::set_multicast_send_interface(const String &p_if_name) {
212+
return OK;
213+
}
214+
210215
MockNetSocket::MockNetSocket() {}
211216

212217
MockNetSocket::~MockNetSocket() {}

0 commit comments

Comments
 (0)