From b0c644c71ebce930d3f6f2c3eb40f711a4cf094e Mon Sep 17 00:00:00 2001 From: CTFang Date: Tue, 11 Jun 2024 19:16:36 +0800 Subject: [PATCH] fix: Add multiple GRE tunnel when have multiple QFI settings (#7) * feat: add multiple tunnel for different QFI * fix: fix bugs and add ping test * fix: use map for linkGREs and add route at default path * fix: fix linter error * fix: error handling for linkGREs map * feat: add specific route to each qfi --- internal/gre/gre.go | 64 +++++++++++++++++++-------- internal/nwucp/handler/handler.go | 48 +++++++++++++++----- internal/packet/nasPacket/nasPduDl.go | 34 ++++++++++++++ pkg/procedure/Procedure.go | 6 +++ 4 files changed, 121 insertions(+), 31 deletions(-) diff --git a/internal/gre/gre.go b/internal/gre/gre.go index 5dc07c9..0b969f7 100644 --- a/internal/gre/gre.go +++ b/internal/gre/gre.go @@ -3,6 +3,7 @@ package gre import ( "fmt" "net" + "strconv" "github.com/vishvananda/netlink" @@ -11,27 +12,56 @@ import ( "github.com/free5gc/n3iwue/pkg/context" ) -func SetupGreTunnel(greIfaceName, parentIfaceName string, ueTunnelAddr, n3iwfTunnelAddr, - pduAddr net.IP, qoSInfo *Qos.PDUQoSInfo, +func SetupGreTunnels( + greIfaceName, parentIfaceName string, + ueTunnelAddr, n3iwfTunnelAddr, pduAddr net.IP, + qoSInfo *Qos.PDUQoSInfo, +) (map[uint8]*netlink.Link, error) { + parent, err := netlink.LinkByName(parentIfaceName) + if err != nil { + return nil, fmt.Errorf("netlink.LinkByName: [%+v] %+v", parentIfaceName, err) + } + + if qoSInfo == nil { + linkGre, err := SetupGreTunnel(greIfaceName, parent, ueTunnelAddr, n3iwfTunnelAddr, pduAddr, 0) + return map[uint8]*netlink.Link{ + 1: &linkGre, + }, err + } + + n3ueSelf := context.N3UESelf() + netlinks := map[uint8]*netlink.Link{} + + for _, qfi := range qoSInfo.QfiList { + linkGRE, err := SetupGreTunnel(greIfaceName, parent, ueTunnelAddr, n3iwfTunnelAddr, pduAddr, qfi) + if err != nil { + return nil, fmt.Errorf("SetupGreTunnel(): [%s]", err) + } + + n3ueSelf.CreatedIface = append(n3ueSelf.CreatedIface, &linkGRE) + netlinks[qfi] = &linkGRE + } + return netlinks, nil +} + +func SetupGreTunnel( + greIfaceName string, + parent netlink.Link, + ueTunnelAddr, n3iwfTunnelAddr, pduAddr net.IP, + qfi uint8, ) (netlink.Link, error) { var ( - parent netlink.Link greKeyField uint32 err error ) - if qoSInfo != nil { - greKeyField |= (uint32(qoSInfo.QfiList[0]) & 0x3F) << 24 - } - - if parent, err = netlink.LinkByName(parentIfaceName); err != nil { - return nil, err - } + greKeyField = (uint32(qfi) & 0x3F) << 24 + newGreIfaceName := greIfaceName + "-" + strconv.Itoa(int(qfi)) // New GRE tunnel interface newGRETunnel := &netlink.Gretun{ LinkAttrs: netlink.LinkAttrs{ - Name: greIfaceName, + Name: newGreIfaceName, MTU: 1438, // remain for endpoint IP header(most 40 bytes if IPv6) and ESP header (22 bytes) }, Link: uint32(parent.Attrs().Index), // PHYS_DEV in iproute2; IFLA_GRE_LINK in linux kernel @@ -40,17 +70,16 @@ func SetupGreTunnel(greIfaceName, parentIfaceName string, ueTunnelAddr, n3iwfTun IKey: greKeyField, OKey: greKeyField, } - - logger.AppLog.Infof("GRE Key Field: 0x%x", greKeyField) + logger.AppLog.Infof("New GRE Tunnel, Key Field: [0x%x], IfaceName: [%+v]", greKeyField, newGreIfaceName) if err = netlink.LinkAdd(newGRETunnel); err != nil { - return nil, err + return nil, fmt.Errorf("netlink.LinkAdd: [%+v] %+v", newGreIfaceName, err) } // Get link info - linkGRE, err := netlink.LinkByName(greIfaceName) + linkGRE, err := netlink.LinkByName(newGreIfaceName) if err != nil { - return nil, fmt.Errorf("No link named %s", greIfaceName) + return nil, fmt.Errorf("no link named: [%s]", newGreIfaceName) } linkGREAddr := &netlink.Addr{ @@ -69,8 +98,5 @@ func SetupGreTunnel(greIfaceName, parentIfaceName string, ueTunnelAddr, n3iwfTun return nil, err } - n3ueSelf := context.N3UESelf() - n3ueSelf.CreatedIface = append(n3ueSelf.CreatedIface, &parent) - return linkGRE, nil } diff --git a/internal/nwucp/handler/handler.go b/internal/nwucp/handler/handler.go index 0d620c7..4fe45a8 100644 --- a/internal/nwucp/handler/handler.go +++ b/internal/nwucp/handler/handler.go @@ -13,6 +13,7 @@ import ( "github.com/free5gc/n3iwue/internal/packet/nasPacket" context "github.com/free5gc/n3iwue/pkg/context" "github.com/free5gc/nas" + "github.com/free5gc/nas/nasType" ) var naslog *logrus.Entry @@ -57,24 +58,47 @@ func HandleDLNASTransport(n3ueSelf *context.N3UE, nasMsg *nas.Message) { newGREName := fmt.Sprintf("%s-id-%d", n3ueSelf.N3ueInfo.GreIfaceName, n3ueSelf.N3ueInfo.XfrmiId) - var linkGRE netlink.Link - if linkGRE, err = gre.SetupGreTunnel(newGREName, n3ueSelf.TemporaryXfrmiName, n3ueSelf.UEInnerAddr.IP, + var linkGREs map[uint8]*netlink.Link + if linkGREs, err = gre.SetupGreTunnels(newGREName, n3ueSelf.TemporaryXfrmiName, n3ueSelf.UEInnerAddr.IP, n3ueSelf.TemporaryUPIPAddr, pduAddress, n3ueSelf.TemporaryQosInfo); err != nil { - naslog.Errorf("Setup GRE tunnel %s Fail %+v", newGREName, err) + naslog.Errorf("Setup GRE tunnel %s Fail: %+v", newGREName, err) return } - // Add route - upRoute := &netlink.Route{ - LinkIndex: linkGRE.Attrs().Index, - Dst: &net.IPNet{ - IP: net.IPv4zero, - Mask: net.IPv4Mask(0, 0, 0, 0), - }, + qfiToTargetMap, err := nasPacket.GetQFItoTargetMap(nasMsg.PDUSessionEstablishmentAccept) + if err != nil { + naslog.Errorf("GetQFItoTargetMap Fail: %+v", err) + return } - if err := netlink.RouteAdd(upRoute); err != nil { - naslog.Warnf("netlink.RouteAdd: %+v", err) + // Add route + for qfi, link := range linkGREs { + tunnel := *link + priority := 1 // lower is higher (1 ~ 7) + + var remoteAddress nasType.PacketFilterIPv4RemoteAddress + var ok bool + if qfi == uint8(1) { // default qfi + remoteAddress.Address = net.IPv4zero + remoteAddress.Mask = net.IPv4Mask(0, 0, 0, 0) + priority = 7 + } else if remoteAddress, ok = qfiToTargetMap[qfi]; !ok { + naslog.Errorf("not found target address for QFI [%v] from NAS", qfi) + continue + } + + naslog.Infof("Add route: QFI[%+v] remote address[%+v]", qfi, remoteAddress) + upRoute := &netlink.Route{ + LinkIndex: tunnel.Attrs().Index, + Dst: &net.IPNet{ + IP: remoteAddress.Address, + Mask: remoteAddress.Mask, + }, + Priority: priority, + } + if err := netlink.RouteAdd(upRoute); err != nil { + naslog.Warnf("netlink.RouteAdd: %+v", err) + } } n3ueSelf.PduSessionCount++ diff --git a/internal/packet/nasPacket/nasPduDl.go b/internal/packet/nasPacket/nasPduDl.go index a7a2e38..edac870 100644 --- a/internal/packet/nasPacket/nasPduDl.go +++ b/internal/packet/nasPacket/nasPduDl.go @@ -8,6 +8,7 @@ import ( n3ue_security "github.com/free5gc/n3iwue/internal/security" "github.com/free5gc/nas" "github.com/free5gc/nas/nasMessage" + "github.com/free5gc/nas/nasType" ) func DecodePDUSessionEstablishmentAccept( @@ -50,3 +51,36 @@ func GetPDUAddress(accept *nasMessage.PDUSessionEstablishmentAccept) (net.IP, er return nil, fmt.Errorf("PDUAddress is nil") } + +func GetQFItoTargetMap( + accept *nasMessage.PDUSessionEstablishmentAccept, +) ( + map[uint8]nasType.PacketFilterIPv4RemoteAddress, error, +) { + qfiMap := map[uint8]nasType.PacketFilterIPv4RemoteAddress{} + + var rules nasType.QoSRules + if err := rules.UnmarshalBinary(accept.AuthorizedQosRules.Buffer); err != nil { + return nil, fmt.Errorf("rules UnmarshalBinary: %+v", err) + } + + for _, rule := range rules { + for _, pfList := range rule.PacketFilterList { + for _, component := range pfList.Components { + if component.Type() == nasType.PacketFilterComponentTypeIPv4RemoteAddress { + var b []byte + var err error + if b, err = component.MarshalBinary(); err != nil { + return nil, fmt.Errorf("MarshalBinary err: %+v", err) + } + var addr nasType.PacketFilterIPv4RemoteAddress + if err = addr.UnmarshalBinary(b); err != nil { + return nil, fmt.Errorf("UnmarshalBinary err: %+v", err) + } + qfiMap[rule.QFI] = addr + } + } + } + } + return qfiMap, nil +} diff --git a/pkg/procedure/Procedure.go b/pkg/procedure/Procedure.go index d9ef801..2aa5a65 100644 --- a/pkg/procedure/Procedure.go +++ b/pkg/procedure/Procedure.go @@ -65,6 +65,12 @@ func StartProcedure() { } case context.PduSessionCreated: AppLog.Info("PduSession Created") + if err := TestConnectivity("9.9.9.9"); err != nil { + AppLog.Errorf("ping fail : %+v", err) + } + if err := TestConnectivity("1.1.1.1"); err != nil { + AppLog.Errorf("ping fail : %+v", err) + } if err := TestConnectivity("8.8.8.8"); err != nil { AppLog.Errorf("ping fail : %+v", err) } else {