From e6c0bf4c15e23c78221594aa64e7dd1b9efc3e83 Mon Sep 17 00:00:00 2001 From: Badhrinath Pa Date: Thu, 14 Jan 2021 17:14:55 -0800 Subject: [PATCH] UE ip pool allocation by UPF --- conf/upf.json | 8 ++- pfcpiface/bess.go | 7 ++- pfcpiface/go.mod | 2 +- pfcpiface/go.sum | 4 +- pfcpiface/ip_pool.go | 55 +++++++++++++++++++ pfcpiface/main.go | 19 ++++--- pfcpiface/messages.go | 26 ++++++--- pfcpiface/parse-pdr.go | 37 +++++++++---- pfcpiface/session-pdr.go | 35 ++++++++++++ pfcpiface/upf.go | 21 ++++--- pfcpiface/utils.go | 22 ++++++++ .../wmnsk/go-pfcp/ie/ue-ip-address.go | 2 +- .../github.com/wmnsk/go-pfcp/message/misc.go | 32 +---------- pfcpiface/vendor/modules.txt | 2 +- 14 files changed, 198 insertions(+), 74 deletions(-) create mode 100644 pfcpiface/ip_pool.go diff --git a/conf/upf.json b/conf/upf.json index 51920af0e..11e718b5a 100644 --- a/conf/upf.json +++ b/conf/upf.json @@ -53,9 +53,11 @@ "": "Control plane controller settings", "cpiface": { - "nb_dst_ip": "172.17.0.1", - "" : "nb_dst_ip: CPHostname", - "hostname": "spgwc" + "enable_ue_ip_alloc": false, + "ue_ip_pool": "10.250.0.0/16", + "nb_dst_ip": "172.17.0.1", + "" : "nb_dst_ip: CPHostname", + "hostname": "spgwc" }, "": "p4rtc interface settings", diff --git a/pfcpiface/bess.go b/pfcpiface/bess.go index 9aed5da23..61ccf963a 100644 --- a/pfcpiface/bess.go +++ b/pfcpiface/bess.go @@ -278,6 +278,12 @@ func (b *bess) setUpfInfo(u *upf, conf *Conf) { u.simInfo = simInfo } + u.ippool_cidr = conf.CPIface.UeIPPool + log.Println("IP pool : ", u.ippool_cidr) + errin := u.ippool.init_pool(u.ippool_cidr) + if errin != nil { + log.Println("ip pool init failed") + } u.accessIP = ParseIP(conf.AccessIface.IfName, "Access") u.coreIP = ParseIP(conf.CoreIface.IfName, "Core") if *n4SrcIPStr != "" { @@ -299,7 +305,6 @@ func (b *bess) setUpfInfo(u *upf, conf *Conf) { } } // get bess grpc client - var errin error log.Println("bessIP ", *bessIP) b.conn, errin = grpc.Dial(*bessIP, grpc.WithInsecure()) diff --git a/pfcpiface/go.mod b/pfcpiface/go.mod index 7dd1de59c..e2882b400 100644 --- a/pfcpiface/go.mod +++ b/pfcpiface/go.mod @@ -8,7 +8,7 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 github.com/p4lang/p4runtime v1.3.0 github.com/prometheus/client_golang v1.9.0 - github.com/wmnsk/go-pfcp v0.0.8 + github.com/wmnsk/go-pfcp v0.0.9-0.20210129064645-e1b1f34ebd9f google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 google.golang.org/grpc v1.35.0 google.golang.org/protobuf v1.25.0 diff --git a/pfcpiface/go.sum b/pfcpiface/go.sum index ecbd3fb84..074fe597c 100644 --- a/pfcpiface/go.sum +++ b/pfcpiface/go.sum @@ -288,8 +288,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/wmnsk/go-pfcp v0.0.8 h1:H/PDw2gvoYmXu3vltuIuJlckytUMpef4fAxl/ij2o64= -github.com/wmnsk/go-pfcp v0.0.8/go.mod h1:Hwc6b/KmDF6tGa5hwhP5UGfKgwnL2CICFs3PgFz5cRE= +github.com/wmnsk/go-pfcp v0.0.9-0.20210129064645-e1b1f34ebd9f h1:eof2jcYxj5zXKwR012IIeZ+odMCUMX/P+Oc163Ec6/s= +github.com/wmnsk/go-pfcp v0.0.9-0.20210129064645-e1b1f34ebd9f/go.mod h1:Hwc6b/KmDF6tGa5hwhP5UGfKgwnL2CICFs3PgFz5cRE= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= diff --git a/pfcpiface/ip_pool.go b/pfcpiface/ip_pool.go new file mode 100644 index 000000000..c63b3cd45 --- /dev/null +++ b/pfcpiface/ip_pool.go @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright(c) 2020 Intel Corporation + +package main + +import ( + "errors" + "log" + "net" + + "github.com/wmnsk/go-pfcp/ie" +) + +type ip_pool struct { + free_pool []string +} + +func needAllocIp(ueIPaddr *ie.UEIPAddressFields) bool { + if has2ndBit(ueIPaddr.Flags) && !has5thBit(ueIPaddr.Flags) { + return false + } + return true +} + +func (ipp *ip_pool) deallocIPV4(element net.IP) { + ipp.free_pool = append(ipp.free_pool, element.String()) // Simply append to enqueue. + log.Println("Enqueued:", element.String()) +} + +func (ipp *ip_pool) allocIPV4() (net.IP, error) { + if len(ipp.free_pool) == 0 { + err := errors.New("ip pool empty") + return nil, err + } + element := ipp.free_pool[0] // The first element is the one to be dequeued. + log.Println("Dequeued:", element) + ipp.free_pool = ipp.free_pool[1:] // Slice off the element once it is dequeued. + ipVal := net.ParseIP(element).To4() + return ipVal, nil +} + +func (ipp *ip_pool) init_pool(cidr string) error { + ip, ipnet, err := net.ParseCIDR(cidr) + if err != nil { + return err + } + + for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) { + ipp.free_pool = append(ipp.free_pool, ip.String()) + } + // remove network address and broadcast address + + ipp.free_pool = ipp.free_pool[1 : len(ipp.free_pool)-1] + return nil +} diff --git a/pfcpiface/main.go b/pfcpiface/main.go index 49532c5e2..e40047f02 100644 --- a/pfcpiface/main.go +++ b/pfcpiface/main.go @@ -52,9 +52,11 @@ type SimModeInfo struct { // CPIfaceInfo : CPIface interface settings type CPIfaceInfo struct { - DestIP string `json:"nb_dst_ip"` - SrcIP string `json:"nb_src_ip"` - FQDNHost string `json:"hostname"` + EnableUeIPAlloc bool `json:"enable_ue_ip_alloc"` + DestIP string `json:"nb_dst_ip"` + SrcIP string `json:"nb_src_ip"` + FQDNHost string `json:"hostname"` + UeIPPool string `json:"ue_ip_pool"` } // IfaceType : Gateway interface struct @@ -139,11 +141,12 @@ func main() { } upf := &upf{ - accessIface: conf.AccessIface.IfName, - coreIface: conf.CoreIface.IfName, - fqdnHost: fqdnh, - maxSessions: conf.MaxSessions, - intf: intf, + accessIface: conf.AccessIface.IfName, + coreIface: conf.CoreIface.IfName, + fqdnHost: fqdnh, + maxSessions: conf.MaxSessions, + intf: intf, + enableUeIPAlloc: conf.CPIface.EnableUeIPAlloc, } upf.setUpfInfo(&conf) diff --git a/pfcpiface/messages.go b/pfcpiface/messages.go index b98dd655b..edf0e3e44 100644 --- a/pfcpiface/messages.go +++ b/pfcpiface/messages.go @@ -57,7 +57,7 @@ func (pc *PFCPConn) handleAssociationSetupRequest(upf *upf, msg message.Message, // Build response message // Timestamp shouldn't be the time message is sent in the real deployment but anyway :D - asres, err := message.NewAssociationSetupResponse(asreq.SequenceNumber, + asresmsg := message.NewAssociationSetupResponse(asreq.SequenceNumber, ie.NewRecoveryTimeStamp(time.Now()), ie.NewNodeID(sourceIP, "", ""), /* node id (IPv4) */ ie.NewCause(cause), /* accept it blindly for the time being */ @@ -65,7 +65,15 @@ func (pc *PFCPConn) handleAssociationSetupRequest(upf *upf, msg message.Message, // = 01000001 ie.NewUserPlaneIPResourceInformation(0x41, 0, upf.accessIP.String(), "", "", ie.SrcInterfaceAccess), // ie.NewUserPlaneIPResourceInformation(0x41, 0, coreIP, "", "", ie.SrcInterfaceCore), - ).Marshal() /* userplane ip resource info */ + ) /* userplane ip resource info */ + + if upf.enableUeIPAlloc { + features := make([]uint8, 4) + setUeipFeature(features...) + asresmsg.UPFunctionFeatures = + ie.NewUPFunctionFeatures(features...) + } + asres, err := asresmsg.Marshal() if err != nil { log.Fatalln("Unable to create association setup response", err) } @@ -247,7 +255,7 @@ func (pc *PFCPConn) handleSessionEstablishmentRequest(upf *upf, msg message.Mess session := pc.mgr.sessions[localSEID] for _, cPDR := range sereq.CreatePDR { var p pdr - if err := p.parsePDR(cPDR, session.localSEID, pc.mgr.appPFDs); err != nil { + if err := p.parsePDR(cPDR, session.localSEID, pc.mgr.appPFDs, upf); err != nil { return sendError(err) } p.fseidIP = fseidIP @@ -270,7 +278,7 @@ func (pc *PFCPConn) handleSessionEstablishmentRequest(upf *upf, msg message.Mess } // Build response message - seres, err := message.NewSessionEstablishmentResponse(0, /* MO?? <-- what's this */ + seresMsg := message.NewSessionEstablishmentResponse(0, /* MO?? <-- what's this */ 0, /* FO <-- what's this? */ session.remoteSEID, /* seid */ sereq.SequenceNumber, /* seq # */ @@ -278,7 +286,10 @@ func (pc *PFCPConn) handleSessionEstablishmentRequest(upf *upf, msg message.Mess ie.NewNodeID(sourceIP, "", ""), /* node id (IPv4) */ ie.NewCause(ie.CauseRequestAccepted), /* accept it blindly for the time being */ ie.NewFSEID(session.localSEID, net.ParseIP(sourceIP), nil, nil), - ).Marshal() + ) + + addPdrInfo(seresMsg, &session) + seres, err := seresMsg.Marshal() if err != nil { log.Fatalln("Unable to create session establishment response", err) } @@ -336,7 +347,7 @@ func (pc *PFCPConn) handleSessionModificationRequest(upf *upf, msg message.Messa addFARs := make([]far, 0, MaxItems) for _, cPDR := range smreq.CreatePDR { var p pdr - if err := p.parsePDR(cPDR, localSEID, pc.mgr.appPFDs); err != nil { + if err := p.parsePDR(cPDR, localSEID, pc.mgr.appPFDs, upf); err != nil { return sendError(err) } p.fseidIP = fseidIP @@ -357,7 +368,7 @@ func (pc *PFCPConn) handleSessionModificationRequest(upf *upf, msg message.Messa for _, uPDR := range smreq.UpdatePDR { var p pdr var err error - if err = p.parsePDR(uPDR, localSEID, pc.mgr.appPFDs); err != nil { + if err = p.parsePDR(uPDR, localSEID, pc.mgr.appPFDs, upf); err != nil { return sendError(err) } err = session.UpdatePDR(p) @@ -477,6 +488,7 @@ func (pc *PFCPConn) handleSessionDeletionRequest(upf *upf, msg message.Message, return sendError(errors.New("Write to FastPath failed")) } + releaseAllocatedIPs(upf, &session) /* delete sessionRecord */ delete(pc.mgr.sessions, localSEID) diff --git a/pfcpiface/parse-pdr.go b/pfcpiface/parse-pdr.go index 7e11af167..2ad38cdfa 100644 --- a/pfcpiface/parse-pdr.go +++ b/pfcpiface/parse-pdr.go @@ -29,13 +29,14 @@ type pdr struct { dstPortMask uint16 protoMask uint8 - precedence uint32 - pdrID uint32 - fseID uint32 - fseidIP uint32 - ctrID uint32 - farID uint32 - needDecap uint8 + precedence uint32 + pdrID uint32 + fseID uint32 + fseidIP uint32 + ctrID uint32 + farID uint32 + needDecap uint8 + allocIpFlag bool } func (p *pdr) printPDR() { @@ -62,10 +63,11 @@ func (p *pdr) printPDR() { log.Println("ctrID:", p.ctrID) log.Println("farID:", p.farID) log.Println("needDecap:", p.needDecap) + log.Println("allocIpFlag:", p.allocIpFlag) log.Println("--------------------------------------------") } -func (p *pdr) parsePDI(pdiIEs []*ie.IE, appPFDs map[string]appPFD) error { +func (p *pdr) parsePDI(pdiIEs []*ie.IE, appPFDs map[string]appPFD, upf *upf) error { var ueIP4 net.IP for _, pdiIE := range pdiIEs { @@ -77,7 +79,20 @@ func (p *pdr) parsePDI(pdiIEs []*ie.IE, appPFDs map[string]appPFD) error { continue } - ueIP4 = ueIPaddr.IPv4Address + if needAllocIp(ueIPaddr) { + /* alloc IPV6 if CHV6 is enabled : TBD */ + log.Println("UPF should alloc UE IP. CHV4 flag set") + ueIP4, err = upf.ippool.allocIPV4() + if err != nil { + log.Println("failed to allocate UE IP") + return err + } + p.allocIpFlag = true + log.Println("ueipv4 : ", ueIP4.String()) + } else { + log.Println("CP has allocated UE IP.") + ueIP4 = ueIPaddr.IPv4Address + } case ie.SourceInterface: srcIface, err := pdiIE.SourceInterface() if err != nil { @@ -212,7 +227,7 @@ func (p *pdr) parsePDI(pdiIEs []*ie.IE, appPFDs map[string]appPFD) error { return nil } -func (p *pdr) parsePDR(ie1 *ie.IE, seid uint64, appPFDs map[string]appPFD) error { +func (p *pdr) parsePDR(ie1 *ie.IE, seid uint64, appPFDs map[string]appPFD, upf *upf) error { /* reset outerHeaderRemoval to begin with */ outerHeaderRemoval := uint8(0) @@ -239,7 +254,7 @@ func (p *pdr) parsePDR(ie1 *ie.IE, seid uint64, appPFDs map[string]appPFD) error outerHeaderRemoval = 1 } - err = p.parsePDI(pdi, appPFDs) + err = p.parsePDI(pdi, appPFDs, upf) if err != nil && err != errBadFilterDesc { return err } diff --git a/pfcpiface/session-pdr.go b/pfcpiface/session-pdr.go index a32d329ef..6e224ae9b 100644 --- a/pfcpiface/session-pdr.go +++ b/pfcpiface/session-pdr.go @@ -5,8 +5,43 @@ package main import ( "errors" + "github.com/wmnsk/go-pfcp/ie" + "github.com/wmnsk/go-pfcp/message" + "log" + "net" ) +// Release allocated IPs +func releaseAllocatedIPs(upf *upf, session *PFCPSession) { + log.Println("release allocated IPs") + for _, pdr := range session.pdrs { + if (pdr.allocIpFlag) && (pdr.srcIface == core) { + log.Println("pdrID : ", pdr.pdrID) + var ueIP net.IP = int2ip(pdr.dstIP) + log.Println("ueIP : ", ueIP.String()) + upf.ippool.deallocIPV4(ueIP) + } + } +} + +func addPdrInfo(msg *message.SessionEstablishmentResponse, + session *PFCPSession) { + log.Println("Add PDRs with UPF alloc IPs to Establishment response") + for _, pdr := range session.pdrs { + if (pdr.allocIpFlag) && (pdr.srcIface == core) { + log.Println("pdrID : ", pdr.pdrID) + var flags uint8 = 0x02 + var ueIP net.IP = int2ip(pdr.dstIP) + log.Println("ueIP : ", ueIP.String()) + msg.CreatedPDR = append(msg.CreatedPDR, + ie.NewCreatedPDR( + ie.NewPDRID(uint16(pdr.pdrID)), + ie.NewUEIPAddress(flags, ueIP.String(), "", 0, 0), + )) + } + } +} + // CreatePDR appends pdr to existing list of PDRs in the session func (s *PFCPSession) CreatePDR(p pdr) { s.pdrs = append(s.pdrs, p) diff --git a/pfcpiface/upf.go b/pfcpiface/upf.go index 190ba2236..beaef8e86 100644 --- a/pfcpiface/upf.go +++ b/pfcpiface/upf.go @@ -8,15 +8,18 @@ import ( ) type upf struct { - accessIface string - coreIface string - accessIP net.IP - coreIP net.IP - n4SrcIP net.IP - fqdnHost string - maxSessions uint32 - simInfo *SimModeInfo - intf fastPath + enableUeIPAlloc bool + accessIface string + coreIface string + ippool_cidr string + accessIP net.IP + coreIP net.IP + n4SrcIP net.IP + fqdnHost string + maxSessions uint32 + simInfo *SimModeInfo + intf fastPath + ippool ip_pool } // to be replaced with go-pfcp structs diff --git a/pfcpiface/utils.go b/pfcpiface/utils.go index ffe4b6a52..9aa3befad 100644 --- a/pfcpiface/utils.go +++ b/pfcpiface/utils.go @@ -23,6 +23,28 @@ func Set(b, flag Bits) Bits { return b | flag } //func Clear(b, flag Bits) Bits { return b &^ flag } //func Toggle(b, flag Bits) Bits { return b ^ flag } //func Has(b, flag Bits) bool { return b&flag != 0 } +func setUeipFeature(features ...uint8) { + if len(features) >= 3 { + features[2] = features[2] | 0x04 + } +} + +func has2ndBit(f uint8) bool { + return (f&0x02)>>1 == 1 +} + +func has5thBit(f uint8) bool { + return (f & 0x010) == 1 +} + +func inc(ip net.IP) { + for j := len(ip) - 1; j >= 0; j-- { + ip[j]++ + if ip[j] > 0 { + break + } + } +} func ip2int(ip net.IP) uint32 { if len(ip) == 16 { diff --git a/pfcpiface/vendor/github.com/wmnsk/go-pfcp/ie/ue-ip-address.go b/pfcpiface/vendor/github.com/wmnsk/go-pfcp/ie/ue-ip-address.go index 44abf3b03..b284f4492 100644 --- a/pfcpiface/vendor/github.com/wmnsk/go-pfcp/ie/ue-ip-address.go +++ b/pfcpiface/vendor/github.com/wmnsk/go-pfcp/ie/ue-ip-address.go @@ -192,7 +192,7 @@ func ParseUEIPAddressFields(b []byte) (*UEIPAddressFields, error) { // UnmarshalBinary parses b into IE. func (f *UEIPAddressFields) UnmarshalBinary(b []byte) error { l := len(b) - if l < 2 { + if l < 1 { return io.ErrUnexpectedEOF } diff --git a/pfcpiface/vendor/github.com/wmnsk/go-pfcp/message/misc.go b/pfcpiface/vendor/github.com/wmnsk/go-pfcp/message/misc.go index 3b1ad5390..1ea711018 100644 --- a/pfcpiface/vendor/github.com/wmnsk/go-pfcp/message/misc.go +++ b/pfcpiface/vendor/github.com/wmnsk/go-pfcp/message/misc.go @@ -4,8 +4,6 @@ package message -import "encoding/hex" - func uint24To32(b []byte) uint32 { if len(b) != 3 { return 0 @@ -17,34 +15,7 @@ func uint32To24(n uint32) []byte { return []byte{uint8(n >> 16), uint8(n >> 8), uint8(n)} } -func strToSwappedBytes(s, filler string) ([]byte, error) { - var raw []byte - var err error - if len(s)%2 == 0 { - raw, err = hex.DecodeString(s) - } else { - raw, err = hex.DecodeString(s + filler) - } - if err != nil { - return nil, err - } - - return swap(raw), nil -} - -func swappedBytesToStr(raw []byte, cutLastDigit bool) string { - if len(raw) == 0 { - return "" - } - - s := hex.EncodeToString(swap(raw)) - if cutLastDigit { - s = s[:len(s)-1] - } - - return s -} - +/* func swap(raw []byte) []byte { var swapped []byte for n := range raw { @@ -73,6 +44,7 @@ func has5thBit(f uint8) bool { func has4thBit(f uint8) bool { return (f&0x08)>>3 == 1 } +*/ func has3rdBit(f uint8) bool { return (f&0x04)>>2 == 1 diff --git a/pfcpiface/vendor/modules.txt b/pfcpiface/vendor/modules.txt index e8d0aa9a1..ec0a59170 100644 --- a/pfcpiface/vendor/modules.txt +++ b/pfcpiface/vendor/modules.txt @@ -33,7 +33,7 @@ github.com/prometheus/common/model github.com/prometheus/procfs github.com/prometheus/procfs/internal/fs github.com/prometheus/procfs/internal/util -# github.com/wmnsk/go-pfcp v0.0.8 +# github.com/wmnsk/go-pfcp v0.0.9-0.20210129064645-e1b1f34ebd9f github.com/wmnsk/go-pfcp/ie github.com/wmnsk/go-pfcp/internal/logger github.com/wmnsk/go-pfcp/internal/utils