diff --git a/clean.sh b/clean.sh index 9feeca5..8b59a19 100755 --- a/clean.sh +++ b/clean.sh @@ -18,3 +18,11 @@ for XFRMI in ${XFRMIs}; do sudo ip link del ${XFRMI} echo del ${XFRMI} done + +# Remove all VRF interfaces +echo "[Info] Remove all VRF interfaces" +VRFs=$(ip link show type vrf | awk 'NR%2==1 {print $2}' | cut -d @ -f 1) +for VRF in ${VRFs}; do + sudo ip link del ${VRF} + echo del ${VRF} +done diff --git a/go.mod b/go.mod index cd90b57..a83ba1b 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,6 @@ require ( github.com/free5gc/ngap v1.1.2 github.com/free5gc/openapi v1.2.3 github.com/free5gc/util v1.0.7-0.20241017071924-da29aef99a1c - github.com/go-ping/ping v0.0.0-20211014180314-6e2b003bffdd github.com/google/gopacket v1.1.19 github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.9.3 diff --git a/internal/nwucp/handler.go b/internal/nwucp/handler.go index c5c5fb7..d270ba9 100755 --- a/internal/nwucp/handler.go +++ b/internal/nwucp/handler.go @@ -9,6 +9,7 @@ import ( "github.com/free5gc/n3iwue/internal/gre" "github.com/free5gc/n3iwue/internal/logger" "github.com/free5gc/n3iwue/internal/packet/nasPacket" + "github.com/free5gc/n3iwue/internal/vrf" context "github.com/free5gc/n3iwue/pkg/context" "github.com/free5gc/nas" "github.com/free5gc/nas/nasType" @@ -87,6 +88,17 @@ func (s *Server) handleDLNASTransport(evt *context.HandleDLNASTransportEvt) { newGREName := fmt.Sprintf("%s-id-%d", n3ueSelf.N3ueInfo.GreIfaceName, n3ueSelf.N3ueInfo.XfrmiId) + // Create VRF + vrfName := fmt.Sprintf("vrf-pdu-%d", n3ueSelf.PduSessionCount) + vrfTableID := 100 + int(n3ueSelf.PduSessionCount) + + vrfLink, err := vrf.CreateOrGetVRF(vrfName, vrfTableID) + if err != nil { + nwucpLog.Errorf("Create VRF failed:%s: %v", vrfName, err) + return + } + nwucpLog.Infof("Use VRF %s (Table %d)", vrfName, vrfTableID) + var linkGREs map[uint8]*netlink.Link if linkGREs, err = gre.SetupGreTunnels(newGREName, n3ueSelf.TemporaryXfrmiName, n3ueSelf.UEInnerAddr.IP, n3ueSelf.TemporaryUPIPAddr, pduAddress, n3ueSelf.TemporaryQosInfo); err != nil { @@ -115,6 +127,12 @@ func (s *Server) handleDLNASTransport(evt *context.HandleDLNASTransportEvt) { nwucpLog.Errorf("not found target address for QFI [%v] from NAS", qfi) continue } + // Bind GRE interface to VRF + if err := netlink.LinkSetMaster(tunnel, vrfLink); err != nil { + nwucpLog.Errorf("Failed to bind %s to VRF %s", tunnel.Attrs().Name, vrfName) + continue + } + nwucpLog.Infof("Interface %s bound to VRF %s", tunnel.Attrs().Name, vrfName) nwucpLog.Infof("Add route: QFI[%+v] remote address[%+v]", qfi, remoteAddress) upRoute := &netlink.Route{ @@ -124,6 +142,7 @@ func (s *Server) handleDLNASTransport(evt *context.HandleDLNASTransportEvt) { Mask: remoteAddress.Mask, }, Priority: priority, + Table: vrfTableID, } if err := netlink.RouteAdd(upRoute); err != nil { nwucpLog.Warnf("netlink.RouteAdd: %+v", err) diff --git a/internal/vrf/vrf.go b/internal/vrf/vrf.go new file mode 100644 index 0000000..0e8329b --- /dev/null +++ b/internal/vrf/vrf.go @@ -0,0 +1,46 @@ +package vrf + +import ( + "fmt" + + "github.com/vishvananda/netlink" + + "github.com/free5gc/n3iwue/pkg/context" +) + +func CreateOrGetVRF(vrfName string, tableID int) (netlink.Link, error) { + // Try to get VRF + link, err := netlink.LinkByName(vrfName) + if err == nil { + // Found, check if it is a VRF + if vrf, ok := link.(*netlink.Vrf); ok { + if vrf.Table == uint32(tableID) { + return vrf, nil // VRF exists and is correct + } + return nil, fmt.Errorf("VRF %s exists but Table ID mismatch (need %d, actual %d)", vrfName, tableID, vrf.Table) + } + return nil, fmt.Errorf("device %s exists but is not a VRF", vrfName) + } + + // Create VRF (l3mdev) device + vrfLink := &netlink.Vrf{ + LinkAttrs: netlink.LinkAttrs{Name: vrfName}, + Table: uint32(tableID), + } + + if err := netlink.LinkAdd(vrfLink); err != nil { + return nil, fmt.Errorf("netlink.LinkAdd VRF %s failed: %v", vrfName, err) + } + + // Enable VRF device + if err := netlink.LinkSetUp(vrfLink); err != nil { + return nil, fmt.Errorf("netlink.LinkSetUp VRF %s failed: %v", vrfName, err) + } + + // Add to CreatedIface for auto cleanup + n3ueSelf := context.N3UESelf() + var createdLink netlink.Link = vrfLink + n3ueSelf.CreatedIface = append(n3ueSelf.CreatedIface, &createdLink) + + return vrfLink, nil +} diff --git a/pkg/procedure/Procedure.go b/pkg/procedure/Procedure.go index db34b95..49bd958 100755 --- a/pkg/procedure/Procedure.go +++ b/pkg/procedure/Procedure.go @@ -3,11 +3,10 @@ package procedure import ( "context" "fmt" + "os/exec" "runtime/debug" "sync" - "time" - "github.com/go-ping/ping" "github.com/sirupsen/logrus" "github.com/free5gc/n3iwue/internal/logger" @@ -127,46 +126,29 @@ func (s *Server) handleEvent(evt n3iwue_context.ProcedureEvt) { func (s *Server) TestConnectivity(addr string) error { n3ueSelf := s.Context() - // Ping remote - pinger, err := ping.NewPinger(addr) - if err != nil { - return err - } - - // Run with root - pinger.SetPrivileged(true) + // Calculate the VRF ID used for the last session + // Since PduSessionCount is incremented after session establishment, we subtract 1 + vrfID := n3ueSelf.PduSessionCount - 1 + vrfName := fmt.Sprintf("vrf-pdu-%d", vrfID) - pinger.OnRecv = func(pkt *ping.Packet) { - AppLog.Infof("%d bytes from %s: icmp_seq=%d time=%v\n", - pkt.Nbytes, pkt.IPAddr, pkt.Seq, pkt.Rtt) - } - pinger.OnFinish = func(stats *ping.Statistics) { - AppLog.Infof("\n--- %s ping statistics ---\n", stats.Addr) - AppLog.Infof("%d packets transmitted, %d packets received, %v%% packet loss\n", - stats.PacketsSent, stats.PacketsRecv, stats.PacketLoss) - AppLog.Infof("round-trip min/avg/max/stddev = %v/%v/%v/%v\n", - stats.MinRtt, stats.AvgRtt, stats.MaxRtt, stats.StdDevRtt) - } + AppLog.Infof("Ping %s using VRF %s", addr, vrfName) - pinger.Count = 5 - pinger.Timeout = 10 * time.Second + args := []string{"vrf", "exec", vrfName, "ping", "-c", "5"} if n3ueSelf.N3ueInfo.DnIPAddr != "" { - pinger.Source = n3ueSelf.N3ueInfo.DnIPAddr + args = append(args, "-I", n3ueSelf.N3ueInfo.DnIPAddr) } + args = append(args, addr) - time.Sleep(3 * time.Second) + // Use ip vrf exec to ping + cmd := exec.CommandContext(context.Background(), "ip", args...) - if err := pinger.Run(); err != nil { - return fmt.Errorf("running ping failed: %+v", err) - } - - time.Sleep(1 * time.Second) - - stats := pinger.Statistics() - if stats.PacketsSent != stats.PacketsRecv { - return fmt.Errorf("ping failed") + // We can capture output to log it + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("ping failed: %v, output: %s", err, string(output)) } + AppLog.Infof("Ping output:\n%s", string(output)) return nil }