@@ -41,6 +41,12 @@ type RuleAction struct {
4141 Action * n4tosrv6.Action
4242 GtpDstPrefix netip.Prefix
4343}
44+
45+ type HandoverInfos struct {
46+ UlTargetSrgw jsonapi.Fteid
47+ DlTargetGnb * jsonapi.Fteid
48+ }
49+
4450type ueInfos struct {
4551 sync.Mutex
4652
@@ -49,8 +55,9 @@ type ueInfos struct {
4955 Gnb string
5056 Pushed bool
5157
52- AnchorsRules []* RuleAction
53- AnchorsLock sync.RWMutex
58+ HandoverInfos * HandoverInfos
59+ AnchorsRules []* RuleAction
60+ AnchorsLock sync.RWMutex
5461
5562 SRGWRules []* RuleAction
5663 SRGWLock sync.RWMutex
@@ -127,6 +134,193 @@ func gnbInArea(gnb netip.Addr, area []netip.Prefix) bool {
127134 return false
128135}
129136
137+ func (pusher * RulesPusher ) pushHandoverAcrossAreas (ctx context.Context , ue_ip string ) error {
138+ i , ok := pusher .ues .Load (ue_ip )
139+ if ! ok {
140+ return fmt .Errorf ("UE not in ue list" )
141+ }
142+ infos := i .(* ueInfos )
143+ infos .Lock ()
144+ defer infos .Unlock ()
145+ if ! infos .Pushed {
146+ return nil // not pushed, nothing to do
147+ }
148+ if infos .HandoverInfos == nil {
149+ return nil // no handover
150+ }
151+ if infos .HandoverInfos .DlTargetGnb == nil {
152+ return nil // not enough info, yet
153+ }
154+ gnb_addr := infos .HandoverInfos .DlTargetGnb .Addr
155+ ue_addr , err := netip .ParseAddr (ue_ip )
156+ if err != nil {
157+ return err
158+ }
159+ client := http.Client {}
160+ logrus .WithFields (logrus.Fields {
161+ "dl-teid" : infos .HandoverInfos .DlTargetGnb .Teid ,
162+ "dl-addr" : infos .HandoverInfos .DlTargetGnb .Addr ,
163+ "ul-teid" : infos .HandoverInfos .UlTargetSrgw .Teid ,
164+ "ul-addr" : infos .HandoverInfos .UlTargetSrgw .Addr ,
165+ "ue-ip" : ue_ip ,
166+ }).Info ("Pushing Handover Rules" )
167+ var wg sync.WaitGroup
168+
169+ for _ , r := range pusher .uplink {
170+ if r .Service == nil {
171+ return fmt .Errorf ("service configurated is nil for uplink rule" )
172+ }
173+ //TODO: add ArgMobSession
174+ srh , err := n4tosrv6 .NewSRH (r .SegmentsList )
175+ if err != nil {
176+ logrus .WithFields (logrus.Fields {
177+ "segments-list" : r .SegmentsList ,
178+ }).WithError (err ).Error ("Creation of SRH uplink failed" )
179+ return err
180+ }
181+ var area []netip.Prefix
182+ if r .Area != nil {
183+ area = * r .Area
184+ } else {
185+ // if no area is defined, create a new-one with only this gnb
186+ area = []netip.Prefix {netip .PrefixFrom (gnb_addr , 32 )}
187+ }
188+ // check infos.Gnb in area
189+ if ! gnbInArea (gnb_addr , area ) {
190+ continue
191+ }
192+
193+ action := n4tosrv6.Action {
194+ SRH : * srh ,
195+ }
196+ rule := n4tosrv6.Rule {
197+ Enabled : r .Enabled ,
198+ Type : "uplink" ,
199+ Match : n4tosrv6.Match {
200+ Header : & n4tosrv6.GtpHeader {
201+ OuterIpSrc : area ,
202+ FTeid : infos .HandoverInfos .UlTargetSrgw ,
203+ InnerIpSrc : & ue_addr ,
204+ },
205+ Payload : & n4tosrv6.Payload {
206+ // TODO: allow multiple services
207+ Dst : * r .Service ,
208+ },
209+ },
210+ Action : action ,
211+ }
212+ rule_json , err := json .Marshal (rule )
213+ if err != nil {
214+ logrus .WithError (err ).Error ("Could not marshal json" )
215+ return err
216+ }
217+ wg .Add (1 )
218+ // TODO: remove old rules
219+ go func () error {
220+ defer wg .Done ()
221+ //infos.SRGWLock.Lock()
222+ //defer infos.SRGWLock.Unlock()
223+ _ , _ = pusher .pushSingleRule (ctx , client , r .ControlURI , rule_json )
224+ // TODO: store this (after old rules are removed)
225+ //if err == nil {
226+ // infos.SRGWRules = append(infos.SRGWRules, &RuleAction{
227+ // Url: url,
228+ // Action: &action,
229+ // })
230+ //}
231+ return err
232+ }()
233+
234+ }
235+
236+ for _ , r := range pusher .downlink {
237+ var area []netip.Prefix
238+ if r .Area != nil {
239+ area = * r .Area
240+ } else {
241+ // if no area is defined, create a new-one with only this gnb
242+ area = []netip.Prefix {netip .PrefixFrom (gnb_addr , 32 )}
243+ }
244+ // check infos.Gnb in area
245+ if ! gnbInArea (gnb_addr , area ) {
246+ continue
247+ }
248+ if len (r .SegmentsList ) == 0 {
249+ logrus .Error ("Empty segments list for downlink" )
250+ return fmt .Errorf ("Empty segments list for downlink" )
251+ }
252+ segList := make ([]string , len (r .SegmentsList ))
253+ copy (segList , r .SegmentsList )
254+ prefix , err := netip .ParsePrefix (r .SegmentsList [0 ])
255+ if err != nil {
256+ return err
257+ }
258+ dst := encoding .NewMGTP4IPv6Dst (prefix , gnb_addr .As4 (), encoding .NewArgsMobSession (0 , false , false , infos .HandoverInfos .DlTargetGnb .Teid ))
259+ dstB , err := dst .Marshal ()
260+ if err != nil {
261+ return err
262+ }
263+ dstIp , ok := netip .AddrFromSlice (dstB )
264+ if ! ok {
265+ return fmt .Errorf ("could not convert MGTP4IPv6Dst to netip.Addr" )
266+ }
267+ // note: in srv6, segment[n] is the first segment of the path, and segment[0] is the last segment in the path
268+ // because Segment-Left is a pointer to the current segment and is decremented each SR-hop
269+ segList [0 ] = dstIp .String ()
270+
271+ srh , err := n4tosrv6 .NewSRH (segList )
272+ if err != nil {
273+ logrus .WithFields (logrus.Fields {
274+ "segments-list" : r .SegmentsList ,
275+ }).WithError (err ).Error ("Creation of SRH downlink failed" )
276+ return err
277+ }
278+ action := n4tosrv6.Action {
279+ SRH : * srh ,
280+ SourceGtp4 : r .SrgwGtp4 ,
281+ }
282+ rule := n4tosrv6.Rule {
283+ Enabled : true ,
284+ Type : "downlink" ,
285+ Match : n4tosrv6.Match {
286+ Payload : & n4tosrv6.Payload {
287+ Dst : ue_addr ,
288+ },
289+ },
290+ Action : action ,
291+ }
292+ rule_json , err := json .Marshal (rule )
293+ if err != nil {
294+ logrus .WithError (err ).Error ("Could not marshal json" )
295+ return err
296+ }
297+ wg .Add (1 )
298+ // TODO: remove old rules
299+ go func () error {
300+ defer wg .Done ()
301+ //infos.AnchorsLock.Lock()
302+ //defer infos.AnchorsLock.Unlock()
303+ _ , _ = pusher .pushSingleRule (ctx , client , r .ControlURI , rule_json )
304+ // TODO: store this (after old rules are removed)
305+ //if err == nil {
306+ // infos.AnchorsRules = append(infos.AnchorsRules, &RuleAction{
307+ // Url: url,
308+ // Action: &action,
309+ // GtpDstPrefix: prefix,
310+ // })
311+ //}
312+ return err
313+ }()
314+
315+ }
316+ wg .Wait ()
317+ infos .HandoverInfos = nil // reset handover state
318+ pusher .ues .Store (ue_ip , infos )
319+
320+ return nil
321+
322+ }
323+
130324func (pusher * RulesPusher ) pushRTRRule (ctx context.Context , ue_ip string ) error {
131325 i , ok := pusher .ues .Load (ue_ip )
132326 if ! ok {
@@ -376,33 +570,6 @@ func (pusher *RulesPusher) pushHandover(ctx context.Context, ue_ip string, hando
376570
377571func (pusher * RulesPusher ) updateRoutersRules (ctx context.Context , msgType pfcputil.MessageType , msg pfcp_networking.ReceivedMessage , e * pfcp_networking.PFCPEntityUP ) {
378572 logrus .Debug ("Into updateRoutersRules" )
379- // detect handover with indirect forwarding
380-
381- // 1. Establishment request:
382- // 1.1. new PDR UL (srgw1+teid)
383- // 1.2 FAR to edge
384- // -> we have UE ip and UL fteid, but we don't have gnb DL fteid yet
385- // => we don't know the area of the UE
386- // * store ul_fteids[ue_ip] = this ul fteid
387-
388- // 2. Modification request:
389- // 2.1 PDR new fteid srgw1+teid (forw)
390- // 2.2 FAR gnbl3
391- // -> we don't have UE ip, we have a forw fteid
392-
393- // 3. Modification request:
394- // 3.1 PDR new fteid srgw0+teid (forw)
395- // 3.2 FAR srgw1+teid
396- // -> we don't have UE ip, we have the forw fteid from step 2, we have the gnb fteid
397-
398- // 4. Modification request:
399- // 4.1. PDR match UE
400- // 4.2. FAR to gnbl3
401- // -> we have the UE ip, we have the gnb DL fteid
402- // => we know the area of the UE
403- // * establish UL using fteid from step 1
404- // * establish DL using fteid from step 4
405-
406573 if msgType == message .MsgTypeSessionModificationRequest {
407574 logrus .Debug ("session modification request" )
408575 // check if handover
@@ -542,14 +709,31 @@ func (pusher *RulesPusher) updateRoutersRules(ctx context.Context, msgType pfcpu
542709 AnchorsRules : make ([]* RuleAction , 0 ),
543710 SRGWRules : make ([]* RuleAction , 0 ),
544711 }); loaded {
545- logrus .WithFields (logrus.Fields {
546- "teid-uplink" : fteid .TEID ,
547- "ue-ipv4" : ue_ipv4 ,
548- }).Debug ("Updating UeInfos" )
712+ if ue .(* ueInfos ).Pushed {
713+ if addr == ue .(* ueInfos ).UplinkFTeid .Addr && fteid .TEID == ue .(* ueInfos ).UplinkFTeid .Teid {
714+ return nil // old data
715+ }
716+ logrus .WithFields (logrus.Fields {
717+ "teid-uplink" : fteid .TEID ,
718+ "addr-uplink" : addr ,
719+ "ue-ipv4" : ue_ipv4 ,
720+ }).Debug ("Updating UeInfos with handoverInfos for uplink" )
721+ ue .(* ueInfos ).Lock ()
722+ ue .(* ueInfos ).HandoverInfos = & HandoverInfos {
723+ UlTargetSrgw : jsonapi.Fteid {Teid : fteid .TEID , Addr : addr },
724+ }
725+ ue .(* ueInfos ).Unlock ()
549726
550- ue .(* ueInfos ).Lock ()
551- ue .(* ueInfos ).UplinkFTeid = jsonapi.Fteid {Teid : fteid .TEID , Addr : addr }
552- ue .(* ueInfos ).Unlock ()
727+ } else {
728+ logrus .WithFields (logrus.Fields {
729+ "teid-uplink" : fteid .TEID ,
730+ "ue-ipv4" : ue_ipv4 ,
731+ }).Debug ("Updating UeInfos" )
732+
733+ ue .(* ueInfos ).Lock ()
734+ ue .(* ueInfos ).UplinkFTeid = jsonapi.Fteid {Teid : fteid .TEID , Addr : addr }
735+ ue .(* ueInfos ).Unlock ()
736+ }
553737 } else if logrus .IsLevelEnabled (logrus .DebugLevel ) {
554738 logrus .WithFields (logrus.Fields {
555739 "teid-uplink" : fteid .TEID ,
@@ -578,15 +762,33 @@ func (pusher *RulesPusher) updateRoutersRules(ctx context.Context, msgType pfcpu
578762 AnchorsRules : make ([]* RuleAction , 0 ),
579763 SRGWRules : make ([]* RuleAction , 0 ),
580764 }); loaded {
581- logrus .WithFields (logrus.Fields {
582- "gnb-ipv4" : gnb_ipv4 ,
583- "teid-downlink" : teid_downlink ,
584- "ue-ipv4" : ue_ipv4 ,
585- }).Debug ("Updating UeInfos" )
586- ue .(* ueInfos ).Lock ()
587- ue .(* ueInfos ).Gnb = gnb_ipv4
588- ue .(* ueInfos ).DownlinkTeid = teid_downlink
589- ue .(* ueInfos ).Unlock ()
765+ if ue .(* ueInfos ).Pushed {
766+ if gnb_ipv4 == ue .(* ueInfos ).Gnb && teid_downlink == ue .(* ueInfos ).DownlinkTeid {
767+ return nil // old info
768+ }
769+ addr , err := netip .ParseAddr (gnb_ipv4 )
770+ if err != nil {
771+ return nil
772+ }
773+ logrus .WithFields (logrus.Fields {
774+ "teid-downlink" : teid_downlink ,
775+ "addr-downlink" : gnb_ipv4 ,
776+ "ue-ipv4" : ue_ipv4 ,
777+ }).Debug ("Updating UeInfos with handoverInfos for downlink" )
778+ ue .(* ueInfos ).Lock ()
779+ ue .(* ueInfos ).HandoverInfos .DlTargetGnb = & jsonapi.Fteid {Teid : teid_downlink , Addr : addr }
780+ ue .(* ueInfos ).Unlock ()
781+ } else {
782+ logrus .WithFields (logrus.Fields {
783+ "gnb-ipv4" : gnb_ipv4 ,
784+ "teid-downlink" : teid_downlink ,
785+ "ue-ipv4" : ue_ipv4 ,
786+ }).Debug ("Updating UeInfos" )
787+ ue .(* ueInfos ).Lock ()
788+ ue .(* ueInfos ).Gnb = gnb_ipv4
789+ ue .(* ueInfos ).DownlinkTeid = teid_downlink
790+ ue .(* ueInfos ).Unlock ()
791+ }
590792 } else if logrus .IsLevelEnabled (logrus .DebugLevel ) {
591793 logrus .WithFields (logrus.Fields {
592794 "gnb-ipv4" : gnb_ipv4 ,
@@ -628,6 +830,7 @@ func (pusher *RulesPusher) updateRoutersRules(ctx context.Context, msgType pfcpu
628830 go func () {
629831 defer wg .Done ()
630832 pusher .pushRTRRule (ctx , ip .(string ))
833+ pusher .pushHandoverAcrossAreas (ctx , ip .(string ))
631834 // TODO: check pushRTRRule return code and send pfcp error on failure
632835 }()
633836 return true
0 commit comments