mirror of
https://github.com/netbirdio/netbird.git
synced 2025-08-09 23:27:58 +02:00
Refactor UDP mux to handle STUN only messages
This commit is contained in:
@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
type BindMux interface {
|
||||
HandlePacket(p []byte, n int, addr net.Addr) error
|
||||
HandleSTUNMessage(msg *stun.Message, addr net.Addr) error
|
||||
Type() string
|
||||
}
|
||||
|
||||
@ -96,6 +96,17 @@ func listenNet(network string, port int) (*net.UDPConn, int, error) {
|
||||
return conn, uaddr.Port, nil
|
||||
}
|
||||
|
||||
func parseStunMessage(raw []byte) (*stun.Message, error) {
|
||||
msg := &stun.Message{
|
||||
Raw: append([]byte{}, raw...),
|
||||
}
|
||||
if err := msg.Decode(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
func (b *ICEBind) makeReceiveIPv4(c net.PacketConn) conn.ReceiveFunc {
|
||||
return func(buff []byte) (int, conn.Endpoint, error) {
|
||||
n, endpoint, err := c.ReadFrom(buff)
|
||||
@ -114,37 +125,20 @@ func (b *ICEBind) makeReceiveIPv4(c net.PacketConn) conn.ReceiveFunc {
|
||||
Zone: e.Addr().Zone(),
|
||||
}), nil
|
||||
}
|
||||
|
||||
msg, err := parseStunMessage(buff[:n])
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
b.mu.Lock()
|
||||
|
||||
/* msg := &stun.Message{
|
||||
Raw: append([]byte{}, buff[:n]...),
|
||||
}
|
||||
if err := msg.Decode(); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
strAttrs := []string{}
|
||||
for _, attribute := range msg.Attributes {
|
||||
strAttrs = append(strAttrs, attribute.String())
|
||||
}
|
||||
|
||||
xorMapped := "EMPTY"
|
||||
_, err = msg.Get(stun.AttrXORMappedAddress)
|
||||
if err == nil {
|
||||
var addr stun.XORMappedAddress
|
||||
if err := addr.GetFrom(msg); err == nil {
|
||||
xorMapped = addr.String()
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("endpoint %s XORMAPPED %s mux type %s msg type %s, attributes %s", endpoint.String(), xorMapped, bindMux.Type(), msg.Type.String(), strings.Join(strAttrs[:], ";"))
|
||||
*/
|
||||
if _, ok := b.endpointMap[e.String()]; !ok {
|
||||
b.endpointMap[e.String()] = c
|
||||
log.Infof("added endpoint %s", e.String())
|
||||
}
|
||||
b.mu.Unlock()
|
||||
|
||||
err = b.udpMux.HandlePacket(buff, n, endpoint)
|
||||
err = b.udpMux.HandleSTUNMessage(msg, endpoint)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ func (m *UDPMuxDefault) Type() string {
|
||||
return "HOST"
|
||||
}
|
||||
|
||||
func (m *UDPMuxDefault) HandlePacket(p []byte, n int, addr net.Addr) error {
|
||||
func (m *UDPMuxDefault) HandleSTUNMessage(msg *stun.Message, addr net.Addr) error {
|
||||
|
||||
udpAddr, ok := addr.(*net.UDPAddr)
|
||||
if !ok {
|
||||
@ -89,45 +89,32 @@ func (m *UDPMuxDefault) HandlePacket(p []byte, n int, addr net.Addr) error {
|
||||
}
|
||||
m.addressMapMu.Unlock()
|
||||
|
||||
// If we haven't seen this address before but is a STUN packet lookup by ufrag
|
||||
if stun.IsMessage(p[:20]) {
|
||||
// This block is needed to discover Peer Reflexive Candidates for which we don't know the Endpoint upfront.
|
||||
// However, we can take a username attribute from the STUN message which contains ufrag.
|
||||
// We can use ufrag to identify the destination conn to route packet to.
|
||||
msg := &stun.Message{
|
||||
Raw: append([]byte{}, p[:n]...),
|
||||
}
|
||||
// This block is needed to discover Peer Reflexive Candidates for which we don't know the Endpoint upfront.
|
||||
// However, we can take a username attribute from the STUN message which contains ufrag.
|
||||
// We can use ufrag to identify the destination conn to route packet to.
|
||||
|
||||
if err := msg.Decode(); err != nil {
|
||||
log.Warnf("Failed to handle decode ICE from %s: %v\n", addr.String(), err)
|
||||
return err
|
||||
}
|
||||
attr, stunAttrErr := msg.Get(stun.AttrUsername)
|
||||
if stunAttrErr == nil {
|
||||
ufrag := strings.Split(string(attr), ":")[0]
|
||||
|
||||
attr, stunAttrErr := msg.Get(stun.AttrUsername)
|
||||
if stunAttrErr == nil {
|
||||
ufrag := strings.Split(string(attr), ":")[0]
|
||||
|
||||
m.mu.Lock()
|
||||
if destinationConn, ok := m.conns[ufrag]; ok {
|
||||
exists := false
|
||||
for _, conn := range destinationConnList {
|
||||
if conn.params.Key == destinationConn.params.Key {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !exists {
|
||||
destinationConnList = append(destinationConnList, destinationConn)
|
||||
m.mu.Lock()
|
||||
if destinationConn, ok := m.conns[ufrag]; ok {
|
||||
exists := false
|
||||
for _, conn := range destinationConnList {
|
||||
if conn.params.Key == destinationConn.params.Key {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
} else {
|
||||
//log.Warnf("No Username attribute in STUN message from %s\n", addr.String())
|
||||
if !exists {
|
||||
destinationConnList = append(destinationConnList, destinationConn)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
for _, conn := range destinationConnList {
|
||||
if err := conn.writePacket(p[:n], udpAddr); err != nil {
|
||||
if err := conn.writePacket(msg.Raw, udpAddr); err != nil {
|
||||
log.Errorf("could not write packet: %v", err)
|
||||
}
|
||||
}
|
||||
|
@ -79,34 +79,23 @@ func (m *UniversalUDPMuxDefault) Type() string {
|
||||
return "SRFLX"
|
||||
}
|
||||
|
||||
func (m *UniversalUDPMuxDefault) HandlePacket(p []byte, n int, addr net.Addr) error {
|
||||
if stun.IsMessage(p[:20]) {
|
||||
msg := &stun.Message{
|
||||
Raw: append([]byte{}, p[:n]...),
|
||||
}
|
||||
func (m *UniversalUDPMuxDefault) HandleSTUNMessage(msg *stun.Message, addr net.Addr) error {
|
||||
|
||||
if err := msg.Decode(); err != nil {
|
||||
log.Warnf("Failed to handle decode ICE from %s: %v\n", addr.String(), err)
|
||||
// todo proper error
|
||||
return nil
|
||||
}
|
||||
|
||||
udpAddr, ok := addr.(*net.UDPAddr)
|
||||
if !ok {
|
||||
// message about this err will be logged in the UDPMux
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.isXORMappedResponse(msg, udpAddr.String()) {
|
||||
err := m.handleXORMappedResponse(udpAddr, msg)
|
||||
if err != nil {
|
||||
log.Debugf("%w: %v", errors.New("failed to get XOR-MAPPED-ADDRESS response"), err)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
udpAddr, ok := addr.(*net.UDPAddr)
|
||||
if !ok {
|
||||
// message about this err will be logged in the UDPMux
|
||||
return nil
|
||||
}
|
||||
return m.UDPMuxDefault.HandlePacket(p, n, addr)
|
||||
|
||||
if m.isXORMappedResponse(msg, udpAddr.String()) {
|
||||
err := m.handleXORMappedResponse(udpAddr, msg)
|
||||
if err != nil {
|
||||
log.Debugf("%w: %v", errors.New("failed to get XOR-MAPPED-ADDRESS response"), err)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return m.UDPMuxDefault.HandleSTUNMessage(msg, addr)
|
||||
}
|
||||
|
||||
// isXORMappedResponse indicates whether the message is a XORMappedAddress and is coming from the known STUN server.
|
||||
|
Reference in New Issue
Block a user