Skip to content

Commit 74ff307

Browse files
committed
Update downlink
1 parent b3cdeea commit 74ff307

File tree

1 file changed

+130
-22
lines changed

1 file changed

+130
-22
lines changed

internal/app/rules-pusher.go

Lines changed: 130 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ type RulesPusher struct {
3636
ues sync.Map
3737
}
3838

39+
type RuleAction struct {
40+
Url *url.URL
41+
Action *n4tosrv6.Action
42+
GtpDstPrefix netip.Prefix
43+
}
3944
type ueInfos struct {
4045
sync.Mutex
4146

@@ -44,11 +49,11 @@ type ueInfos struct {
4449
Gnb string
4550
Pushed bool
4651

47-
AnchorsRules []*url.URL
48-
AnchorsLock sync.Mutex
52+
AnchorsRules []*RuleAction
53+
AnchorsLock sync.RWMutex
4954

50-
SRGWRules []*url.URL
51-
SRGWLock sync.Mutex
55+
SRGWRules []*RuleAction
56+
SRGWLock sync.RWMutex
5257
}
5358

5459
func NewRulesPusher(config *config.CtrlConfig) *RulesPusher {
@@ -59,6 +64,29 @@ func NewRulesPusher(config *config.CtrlConfig) *RulesPusher {
5964
}
6065
}
6166

67+
func (pusher *RulesPusher) pushUpdateAction(ctx context.Context, client http.Client, url *url.URL, data []byte) error {
68+
req, err := http.NewRequestWithContext(ctx, http.MethodPatch, url.String(), bytes.NewBuffer(data))
69+
if err != nil {
70+
logrus.WithError(err).Error("could not create http request")
71+
return err
72+
}
73+
req.Header.Add("User-Agent", UserAgent)
74+
req.Header.Set("Content-Type", "application/json; charset=UTF-8")
75+
resp, err := client.Do(req)
76+
if err != nil {
77+
logrus.WithError(err).Error("Could not push update action: server not responding")
78+
return fmt.Errorf("Could not push update action: server not responding")
79+
}
80+
defer resp.Body.Close()
81+
if resp.StatusCode == 400 {
82+
logrus.WithError(err).Error("HTTP Bad Request")
83+
return fmt.Errorf("HTTP Bad request")
84+
} else if resp.StatusCode >= 500 {
85+
logrus.WithError(err).Error("HTTP internal error")
86+
return fmt.Errorf("HTTP internal error")
87+
}
88+
return nil
89+
}
6290
func (pusher *RulesPusher) pushSingleRule(ctx context.Context, client http.Client, uri jsonapi.ControlURI, data []byte) (*url.URL, error) {
6391
req, err := http.NewRequestWithContext(ctx, http.MethodPost, uri.JoinPath("rules").String(), bytes.NewBuffer(data))
6492
if err != nil {
@@ -92,16 +120,16 @@ func (pusher *RulesPusher) pushSingleRule(ctx context.Context, client http.Clien
92120

93121
func (pusher *RulesPusher) pushRTRRule(ctx context.Context, ue_ip string) error {
94122
i, ok := pusher.ues.Load(ue_ip)
123+
if !ok {
124+
return fmt.Errorf("UE not in ue list")
125+
}
95126
infos := i.(*ueInfos)
96127
infos.Lock()
97128
defer infos.Unlock()
98129
if infos.Pushed {
99130
return nil // already pushed, nothing to do
100131
}
101132
infos.Pushed = true
102-
if !ok {
103-
return fmt.Errorf("UE not in ue list")
104-
}
105133
service_ip := "10.4.0.1"
106134
logrus.WithFields(logrus.Fields{
107135
"ue-ip": ue_ip,
@@ -134,6 +162,9 @@ func (pusher *RulesPusher) pushRTRRule(ctx context.Context, ue_ip string) error
134162
// if no area is defined, create a new-one with only this gnb
135163
area = []netip.Prefix{netip.PrefixFrom(gnb_addr, 32)}
136164
}
165+
action := n4tosrv6.Action{
166+
SRH: *srh,
167+
}
137168
rule := n4tosrv6.Rule{
138169
Enabled: r.Enabled,
139170
Type: "uplink",
@@ -147,9 +178,7 @@ func (pusher *RulesPusher) pushRTRRule(ctx context.Context, ue_ip string) error
147178
Dst: service_addr,
148179
},
149180
},
150-
Action: n4tosrv6.Action{
151-
SRH: *srh,
152-
},
181+
Action: action,
153182
}
154183
rule_json, err := json.Marshal(rule)
155184
if err != nil {
@@ -163,7 +192,10 @@ func (pusher *RulesPusher) pushRTRRule(ctx context.Context, ue_ip string) error
163192
defer infos.SRGWLock.Unlock()
164193
url, err := pusher.pushSingleRule(ctx, client, r.ControlURI, rule_json)
165194
if err == nil {
166-
infos.SRGWRules = append(infos.SRGWRules, url)
195+
infos.SRGWRules = append(infos.SRGWRules, &RuleAction{
196+
Url: url,
197+
Action: &action,
198+
})
167199
}
168200
return err
169201
}()
@@ -190,6 +222,8 @@ func (pusher *RulesPusher) pushRTRRule(ctx context.Context, ue_ip string) error
190222
if !ok {
191223
return fmt.Errorf("could not convert MGTP4IPv6Dst to netip.Addr")
192224
}
225+
// note: in srv6, segment[n] is the first segment of the path, and segment[0] is the last segment in the path
226+
// because Segment-Left is a pointer to the current segment and is decremented each SR-hop
193227
segList[0] = dstIp.String()
194228

195229
srh, err := n4tosrv6.NewSRH(segList)
@@ -199,6 +233,9 @@ func (pusher *RulesPusher) pushRTRRule(ctx context.Context, ue_ip string) error
199233
}).WithError(err).Error("Creation of SRH downlink failed")
200234
return err
201235
}
236+
action := n4tosrv6.Action{
237+
SRH: *srh,
238+
}
202239
rule := n4tosrv6.Rule{
203240
Enabled: true,
204241
Type: "downlink",
@@ -207,9 +244,7 @@ func (pusher *RulesPusher) pushRTRRule(ctx context.Context, ue_ip string) error
207244
Dst: ue_addr,
208245
},
209246
},
210-
Action: n4tosrv6.Action{
211-
SRH: *srh,
212-
},
247+
Action: action,
213248
}
214249
rule_json, err := json.Marshal(rule)
215250
if err != nil {
@@ -223,18 +258,86 @@ func (pusher *RulesPusher) pushRTRRule(ctx context.Context, ue_ip string) error
223258
defer infos.AnchorsLock.Unlock()
224259
url, err := pusher.pushSingleRule(ctx, client, r.ControlURI, rule_json)
225260
if err == nil {
226-
infos.AnchorsRules = append(infos.AnchorsRules, url)
261+
infos.AnchorsRules = append(infos.AnchorsRules, &RuleAction{
262+
Url: url,
263+
Action: &action,
264+
GtpDstPrefix: prefix,
265+
})
227266
}
228267
return err
229268
}()
230269

231270
}
232271
wg.Wait()
272+
pusher.ues.Store(ue_ip, infos)
273+
233274
return nil
234275
}
235276

236-
func (pusher *RulesPusher) pushHandover(ctx context.Context, ue string, handoverTo jsonapi.Fteid) {
237-
//TODO Handover
277+
func (pusher *RulesPusher) pushHandover(ctx context.Context, ue_ip string, handoverTo jsonapi.Fteid) error {
278+
i, ok := pusher.ues.Load(ue_ip)
279+
if !ok {
280+
return fmt.Errorf("UE not in ue list")
281+
}
282+
infos := i.(*ueInfos)
283+
infos.Lock()
284+
defer infos.Unlock()
285+
286+
client := http.Client{}
287+
var wg sync.WaitGroup
288+
289+
infos.AnchorsLock.RLock()
290+
defer infos.AnchorsLock.RUnlock()
291+
292+
logrus.WithFields(logrus.Fields{
293+
"nb-uplink": len(infos.AnchorsRules),
294+
"ue-ip": ue_ip,
295+
"handover-to-addr": handoverTo.Addr,
296+
"handover-to-teid": handoverTo.Teid,
297+
}).Debug("Pushing new downlink rules for handover")
298+
299+
for _, r := range infos.AnchorsRules {
300+
dst := encoding.NewMGTP4IPv6Dst(r.GtpDstPrefix, handoverTo.Addr.As4(), encoding.NewArgsMobSession(0, false, false, handoverTo.Teid))
301+
dstB, err := dst.Marshal()
302+
if err != nil {
303+
return err
304+
}
305+
dstIp, ok := netip.AddrFromSlice(dstB)
306+
if !ok {
307+
return fmt.Errorf("could not convert MGTP4IPv6Dst to netip.Addr")
308+
}
309+
seg0, err := n4tosrv6.NewSegment(dstIp.String())
310+
if err != nil {
311+
return err
312+
}
313+
// note: in srv6, segment[n] is the first segment of the path, and segment[0] is the last segment in the path
314+
// because Segment-Left is a pointer to the current segment and is decremented each SR-hop
315+
r.Action.SRH[0] = seg0
316+
317+
action_json, err := json.Marshal(r.Action)
318+
if err != nil {
319+
logrus.WithError(err).Error("Could not marshal json")
320+
return err
321+
}
322+
wg.Add(1)
323+
go func() error {
324+
defer wg.Done()
325+
err := pusher.pushUpdateAction(ctx, client, r.Url.JoinPath("update-action"), action_json)
326+
if err != nil {
327+
logrus.WithError(err).Error("Could not push update action")
328+
} else {
329+
logrus.WithFields(logrus.Fields{
330+
"path": r.Url.JoinPath("update-action").String(),
331+
}).Debug("pushed update action")
332+
}
333+
return err
334+
}()
335+
}
336+
wg.Wait()
337+
infos.Gnb = handoverTo.Addr.String()
338+
infos.DownlinkTeid = handoverTo.Teid
339+
pusher.ues.Store(ue_ip, infos)
340+
return nil
238341

239342
}
240343

@@ -320,7 +423,12 @@ func (pusher *RulesPusher) updateRoutersRules(ctx context.Context, msgType pfcpu
320423
"pdrid": pdrid,
321424
"ue": ue.IPv4Address.String(),
322425
}).Debug("UE identified for handover")
323-
pusher.pushHandover(ctx, ue.IPv4Address.String(), handoverTo)
426+
go func() {
427+
err := pusher.pushHandover(ctx, ue.IPv4Address.String(), handoverTo)
428+
if err != nil {
429+
logrus.WithError(err).Error("Could not push handover rule")
430+
}
431+
}()
324432
handoverDone = true
325433
return nil
326434
})
@@ -369,8 +477,8 @@ func (pusher *RulesPusher) updateRoutersRules(ctx context.Context, msgType pfcpu
369477
}
370478
if ue, loaded := pusher.ues.LoadOrStore(ue_ipv4, &ueInfos{
371479
UplinkFTeid: jsonapi.Fteid{Teid: fteid.TEID, Addr: addr},
372-
AnchorsRules: make([]*url.URL, 0),
373-
SRGWRules: make([]*url.URL, 0),
480+
AnchorsRules: make([]*RuleAction, 0),
481+
SRGWRules: make([]*RuleAction, 0),
374482
}); loaded {
375483
logrus.WithFields(logrus.Fields{
376484
"teid-uplink": fteid.TEID,
@@ -405,8 +513,8 @@ func (pusher *RulesPusher) updateRoutersRules(ctx context.Context, msgType pfcpu
405513
if ue, loaded := pusher.ues.LoadOrStore(ue_ipv4, &ueInfos{
406514
DownlinkTeid: teid_downlink,
407515
Gnb: gnb_ipv4,
408-
AnchorsRules: make([]*url.URL, 0),
409-
SRGWRules: make([]*url.URL, 0),
516+
AnchorsRules: make([]*RuleAction, 0),
517+
SRGWRules: make([]*RuleAction, 0),
410518
}); loaded {
411519
logrus.WithFields(logrus.Fields{
412520
"gnb-ipv4": gnb_ipv4,

0 commit comments

Comments
 (0)