@@ -41,6 +41,12 @@ type RuleAction struct {
41
41
Action * n4tosrv6.Action
42
42
GtpDstPrefix netip.Prefix
43
43
}
44
+
45
+ type HandoverInfos struct {
46
+ UlTargetSrgw jsonapi.Fteid
47
+ DlTargetGnb * jsonapi.Fteid
48
+ }
49
+
44
50
type ueInfos struct {
45
51
sync.Mutex
46
52
@@ -49,8 +55,9 @@ type ueInfos struct {
49
55
Gnb string
50
56
Pushed bool
51
57
52
- AnchorsRules []* RuleAction
53
- AnchorsLock sync.RWMutex
58
+ HandoverInfos * HandoverInfos
59
+ AnchorsRules []* RuleAction
60
+ AnchorsLock sync.RWMutex
54
61
55
62
SRGWRules []* RuleAction
56
63
SRGWLock sync.RWMutex
@@ -127,6 +134,193 @@ func gnbInArea(gnb netip.Addr, area []netip.Prefix) bool {
127
134
return false
128
135
}
129
136
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
+
130
324
func (pusher * RulesPusher ) pushRTRRule (ctx context.Context , ue_ip string ) error {
131
325
i , ok := pusher .ues .Load (ue_ip )
132
326
if ! ok {
@@ -376,33 +570,6 @@ func (pusher *RulesPusher) pushHandover(ctx context.Context, ue_ip string, hando
376
570
377
571
func (pusher * RulesPusher ) updateRoutersRules (ctx context.Context , msgType pfcputil.MessageType , msg pfcp_networking.ReceivedMessage , e * pfcp_networking.PFCPEntityUP ) {
378
572
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
-
406
573
if msgType == message .MsgTypeSessionModificationRequest {
407
574
logrus .Debug ("session modification request" )
408
575
// check if handover
@@ -542,14 +709,31 @@ func (pusher *RulesPusher) updateRoutersRules(ctx context.Context, msgType pfcpu
542
709
AnchorsRules : make ([]* RuleAction , 0 ),
543
710
SRGWRules : make ([]* RuleAction , 0 ),
544
711
}); 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 ()
549
726
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
+ }
553
737
} else if logrus .IsLevelEnabled (logrus .DebugLevel ) {
554
738
logrus .WithFields (logrus.Fields {
555
739
"teid-uplink" : fteid .TEID ,
@@ -578,15 +762,33 @@ func (pusher *RulesPusher) updateRoutersRules(ctx context.Context, msgType pfcpu
578
762
AnchorsRules : make ([]* RuleAction , 0 ),
579
763
SRGWRules : make ([]* RuleAction , 0 ),
580
764
}); 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
+ }
590
792
} else if logrus .IsLevelEnabled (logrus .DebugLevel ) {
591
793
logrus .WithFields (logrus.Fields {
592
794
"gnb-ipv4" : gnb_ipv4 ,
@@ -628,6 +830,7 @@ func (pusher *RulesPusher) updateRoutersRules(ctx context.Context, msgType pfcpu
628
830
go func () {
629
831
defer wg .Done ()
630
832
pusher .pushRTRRule (ctx , ip .(string ))
833
+ pusher .pushHandoverAcrossAreas (ctx , ip .(string ))
631
834
// TODO: check pushRTRRule return code and send pfcp error on failure
632
835
}()
633
836
return true
0 commit comments