diff --git a/go.mod b/go.mod index 100b2aa..d2eb6e7 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/lib/pq v1.10.9 github.com/nextmn/gopacket-gtp v0.0.8 github.com/nextmn/gopacket-srv6 v0.0.8 - github.com/nextmn/json-api v0.0.15 + github.com/nextmn/json-api v0.0.16-0.20250114105630-01e517e8726a github.com/nextmn/logrus-formatter v0.0.1 github.com/nextmn/rfc9433 v0.0.2 github.com/sirupsen/logrus v1.9.3 diff --git a/go.sum b/go.sum index 0106bec..51e7580 100644 --- a/go.sum +++ b/go.sum @@ -59,6 +59,8 @@ github.com/nextmn/gopacket-srv6 v0.0.8 h1:oP4wuJ7dOiV/gWmX3zoFcdp2dKdSWLUaxH2fJ3 github.com/nextmn/gopacket-srv6 v0.0.8/go.mod h1:2Tyuo9zsG0bP2IhC4tVRgPRuyUqOgrvEEH9seJSZTlU= github.com/nextmn/json-api v0.0.15 h1:ZSFr06omGsXtTHT6SWCy7Sc9csNQgwpOibkkdUm0pEA= github.com/nextmn/json-api v0.0.15/go.mod h1:CQXeNPj9MDGsEExtnqJFIGjLgZAKsmOoO2fy+mep7Ak= +github.com/nextmn/json-api v0.0.16-0.20250114105630-01e517e8726a h1:sOxYlcsNpMXkKew6oR3pTFHjh0DfKDmlPgj0jQgLnh4= +github.com/nextmn/json-api v0.0.16-0.20250114105630-01e517e8726a/go.mod h1:CQXeNPj9MDGsEExtnqJFIGjLgZAKsmOoO2fy+mep7Ak= github.com/nextmn/logrus-formatter v0.0.1 h1:Bsf78jjiEESc+rV8xE6IyKj4frDPGMwXFNrLQzm6A1E= github.com/nextmn/logrus-formatter v0.0.1/go.mod h1:vdSZ+sIcSna8vjbXkSFxsnsKHqRwaUEed4JCPcXoGyM= github.com/nextmn/rfc9433 v0.0.2 h1:6FjMY+Qy8MNXQ0PPxezUsyXDxJiCbTp5j3OcXQgIQh8= diff --git a/internal/database/api/uplink.go b/internal/database/api/uplink.go index 72aec6c..bdee69b 100644 --- a/internal/database/api/uplink.go +++ b/internal/database/api/uplink.go @@ -9,9 +9,10 @@ import ( "context" "net/netip" + "github.com/nextmn/json-api/jsonapi" "github.com/nextmn/json-api/jsonapi/n4tosrv6" ) type Uplink interface { - GetUplinkAction(ctx context.Context, UplinkTeid uint32, GnbIp netip.Addr, UeIp netip.Addr, ServiceIp netip.Addr) (n4tosrv6.Action, error) + GetUplinkAction(ctx context.Context, UplinkFTeid jsonapi.Fteid, GnbIp netip.Addr, UeIp netip.Addr, ServiceIp netip.Addr) (n4tosrv6.Action, error) } diff --git a/internal/database/database.go b/internal/database/database.go index c03c094..23fb54a 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -13,6 +13,7 @@ import ( "net/netip" "strings" + "github.com/nextmn/json-api/jsonapi" "github.com/nextmn/json-api/jsonapi/n4tosrv6" "github.com/gofrs/uuid" @@ -93,7 +94,7 @@ func (db *Database) InsertRule(ctx context.Context, r n4tosrv6.Rule) (*uuid.UUID case "uplink": var inneripsrc string var inneripdst string - var outeripsrc string + var outeripsrc []string if r.Match.Header.InnerIpSrc == nil { inneripsrc = "0.0.0.0/0" } else { @@ -104,11 +105,13 @@ func (db *Database) InsertRule(ctx context.Context, r n4tosrv6.Rule) (*uuid.UUID } else { inneripdst = r.Match.Payload.Dst.String() + "/32" } - outeripsrc = r.Match.Header.OuterIpSrc.String() + "/32" + for _, i := range r.Match.Header.OuterIpSrc { + outeripsrc = append(outeripsrc, i.String()) + } if stmt, ok := db.stmt["insert_uplink_rule"]; ok { var id uuid.UUID - err := stmt.QueryRowContext(ctx, r.Enabled, inneripsrc, outeripsrc, r.Match.Header.Teid, inneripdst, pq.Array(srh)).Scan(&id) + err := stmt.QueryRowContext(ctx, r.Enabled, inneripsrc, pq.Array(outeripsrc), r.Match.Header.FTeid.Teid, r.Match.Header.FTeid.Addr, inneripdst, pq.Array(srh)).Scan(&id) return &id, err } else { return nil, fmt.Errorf("Procedure not registered") @@ -137,11 +140,12 @@ func (db *Database) GetRule(ctx context.Context, uuid uuid.UUID) (n4tosrv6.Rule, var enabled bool var action_srh []string var match_ue_ip string - var match_gnb_ip *string + var match_gnb_ip []string var match_service_ip *string var match_uplink_teid *uint32 + var match_uplink_upf *string if stmt, ok := db.stmt["get_rule"]; ok { - err := stmt.QueryRowContext(ctx, uuid.String()).Scan(&type_uplink, &enabled, pq.Array(&action_srh), &match_ue_ip, &match_gnb_ip, &match_uplink_teid, &match_service_ip) + err := stmt.QueryRowContext(ctx, uuid.String()).Scan(&type_uplink, &enabled, pq.Array(&action_srh), &match_ue_ip, pq.Array(&match_gnb_ip), &match_uplink_teid, &match_uplink_upf, &match_service_ip) if err != nil { return n4tosrv6.Rule{}, err } @@ -152,14 +156,24 @@ func (db *Database) GetRule(ctx context.Context, uuid uuid.UUID) (n4tosrv6.Rule, if type_uplink { rule.Type = "uplink" rule.Match.Header = &n4tosrv6.GtpHeader{} - if match_gnb_ip != nil { - p, err := netip.ParsePrefix(*match_gnb_ip) - if err == nil && p.Bits() == 32 { - rule.Match.Header.OuterIpSrc = p.Addr() + + rule.Match.Header.OuterIpSrc = make([]netip.Prefix, 0) + for _, i := range match_gnb_ip { + p, err := netip.ParsePrefix(i) + if err != nil { + return n4tosrv6.Rule{}, err } + rule.Match.Header.OuterIpSrc = append(rule.Match.Header.OuterIpSrc, p) } - if match_uplink_teid != nil { - rule.Match.Header.Teid = *match_uplink_teid + if match_uplink_upf != nil && match_uplink_teid != nil { + addr, err := netip.ParseAddr(*match_uplink_upf) + if err != nil { + return n4tosrv6.Rule{}, err + } + rule.Match.Header.FTeid = jsonapi.Fteid{ + Teid: *match_uplink_teid, + Addr: addr, + } } if match_service_ip != nil { p, err := netip.ParsePrefix(*match_service_ip) @@ -204,8 +218,9 @@ func (db *Database) GetRules(ctx context.Context) (n4tosrv6.RuleMap, error) { var enabled bool var action_srh []string var match_ue_ip string - var match_gnb_ip *string + var match_gnb_ip []string var match_uplink_teid *uint32 + var match_uplink_upf *string var match_service_ip *string m := n4tosrv6.RuleMap{} if stmt, ok := db.stmt["get_all_rules"]; ok { @@ -219,7 +234,7 @@ func (db *Database) GetRules(ctx context.Context) (n4tosrv6.RuleMap, error) { // avoid looping if no longer necessary return n4tosrv6.RuleMap{}, ctx.Err() default: - err := rows.Scan(&uuid, &type_uplink, &enabled, pq.Array(&action_srh), &match_ue_ip, &match_gnb_ip, &match_uplink_teid, &match_service_ip) + err := rows.Scan(&uuid, &type_uplink, &enabled, pq.Array(&action_srh), &match_ue_ip, pq.Array(&match_gnb_ip), &match_uplink_teid, &match_uplink_upf, &match_service_ip) if err != nil { return m, err } @@ -230,14 +245,23 @@ func (db *Database) GetRules(ctx context.Context) (n4tosrv6.RuleMap, error) { if type_uplink { rule.Type = "uplink" rule.Match.Header = &n4tosrv6.GtpHeader{} - if match_gnb_ip != nil { - p, err := netip.ParsePrefix(*match_gnb_ip) - if err == nil && p.Bits() == 32 { - rule.Match.Header.OuterIpSrc = p.Addr() + rule.Match.Header.OuterIpSrc = make([]netip.Prefix, 0) + for _, i := range match_gnb_ip { + p, err := netip.ParsePrefix(i) + if err != nil { + return n4tosrv6.RuleMap{}, err } + rule.Match.Header.OuterIpSrc = append(rule.Match.Header.OuterIpSrc, p) } - if match_uplink_teid != nil { - rule.Match.Header.Teid = *match_uplink_teid + if match_uplink_upf != nil && match_uplink_teid != nil { + addr, err := netip.ParseAddr(*match_uplink_upf) + if err != nil { + return n4tosrv6.RuleMap{}, err + } + rule.Match.Header.FTeid = jsonapi.Fteid{ + Teid: *match_uplink_teid, + Addr: addr, + } } if match_service_ip != nil { p, err := netip.ParsePrefix(*match_service_ip) @@ -316,10 +340,10 @@ func (db *Database) DeleteRule(ctx context.Context, uuid uuid.UUID) error { } } -func (db *Database) GetUplinkAction(ctx context.Context, uplinkTeid uint32, gnbIp netip.Addr, ueIp netip.Addr, serviceIp netip.Addr) (n4tosrv6.Action, error) { +func (db *Database) GetUplinkAction(ctx context.Context, uplinkFTeid jsonapi.Fteid, gnbIp netip.Addr, ueIp netip.Addr, serviceIp netip.Addr) (n4tosrv6.Action, error) { var action_srh []string if stmt, ok := db.stmt["get_uplink_action"]; ok { - err := stmt.QueryRowContext(ctx, uplinkTeid, gnbIp.String(), ueIp.String(), serviceIp.String()).Scan(pq.Array(&action_srh)) + err := stmt.QueryRowContext(ctx, uplinkFTeid.Teid, uplinkFTeid.Addr.String(), gnbIp.String(), ueIp.String(), serviceIp.String()).Scan(pq.Array(&action_srh)) if err != nil { return n4tosrv6.Action{}, err } diff --git a/internal/database/database.sql b/internal/database/database.sql index 626c08c..f11c39c 100644 --- a/internal/database/database.sql +++ b/internal/database/database.sql @@ -9,23 +9,25 @@ CREATE TABLE IF NOT EXISTS rule ( enabled BOOL NOT NULL, action_srh INET ARRAY NOT NULL, match_ue_ip CIDR NOT NULL, - match_gnb_ip CIDR, + match_gnb_ip CIDR ARRAY, match_service_ip CIDR, - match_uplink_teid BIGINT + match_uplink_teid BIGINT, + match_uplink_upf INET, ); CREATE OR REPLACE PROCEDURE insert_uplink_rule( IN in_enabled BOOL, IN in_ue_ip CIDR, - IN in_gnb_ip CIDR, IN in_uplink_teid BIGINT, + IN in_gnb_ip CIDR ARRAY, + IN in_uplink_teid BIGINT, IN in_uplink_upf INET, IN in_service_ip CIDR, IN in_srh INET ARRAY, OUT out_uuid UUID ) LANGUAGE plpgsql AS $$ BEGIN - INSERT INTO rule(type_uplink, enabled, match_ue_ip, match_gnb_ip, match_uplink_teid, match_service_ip, action_srh) - VALUES(TRUE, in_enabled, in_ue_ip, in_gnb_ip, in_uplink_teid, in_service_ip, in_srh) RETURNING rule.uuid INTO out_uuid; + INSERT INTO rule(type_uplink, enabled, match_ue_ip, match_gnb_ip, match_uplink_teid, match_uplink_upf, match_service_ip, action_srh) + VALUES(TRUE, in_enabled, in_ue_ip, in_gnb_ip, in_uplink_teid, in_match_uplink_upf, in_service_ip, in_srh) RETURNING rule.uuid INTO out_uuid; END;$$; CREATE OR REPLACE PROCEDURE insert_downlink_rule( @@ -75,7 +77,8 @@ BEGIN END;$$; CREATE OR REPLACE FUNCTION get_uplink_action( - IN in_uplink_teid BIGINT, IN in_gnb_ip INET, + IN in_uplink_teid BIGINT, IN in_uplink_upf INET, + IN in_gnb_ip INET, IN in_ue_ip INET, IN in_service_ip INET ) RETURNS TABLE ( @@ -86,7 +89,8 @@ BEGIN RETURN QUERY SELECT rule.action_srh AS "t_action_srh" FROM rule WHERE (rule.match_uplink_teid = in_uplink_teid - AND rule.match_gnb_ip && in_gnb_ip + AND rule.match_uplink_upf && in_uplink_upf + AND in_gnb_ip <<= any (rule.match_gnb_ip) AND rule.match_ue_ip && in_ue_ip AND rule.match_service_ip && in_service_ip AND rule.enabled = TRUE @@ -116,8 +120,9 @@ RETURNS TABLE ( t_enabled BOOL, t_action_srh INET ARRAY, t_match_ue_ip CIDR, - t_match_gnb_ip CIDR, + t_match_gnb_ip CIDR ARRAY, t_match_uplink_teid BIGINT, + t_match_uplink_upf INET, t_match_service_ip CIDR ) AS $$ diff --git a/internal/database/database_gen.go b/internal/database/database_gen.go index d24c44f..421b7f4 100644 --- a/internal/database/database_gen.go +++ b/internal/database/database_gen.go @@ -13,13 +13,13 @@ type procedureOrFunction struct { } var procedures = map[string]procedureOrFunction{ - "insert_uplink_rule": {is_procedure: true, num_in: 6, num_out: 1}, + "insert_uplink_rule": {is_procedure: true, num_in: 7, num_out: 1}, "insert_downlink_rule": {is_procedure: true, num_in: 3, num_out: 1}, "enable_rule": {is_procedure: true, num_in: 1, num_out: 0}, "disable_rule": {is_procedure: true, num_in: 1, num_out: 0}, "switch_rule": {is_procedure: true, num_in: 2, num_out: 0}, "delete_rule": {is_procedure: true, num_in: 1, num_out: 0}, - "get_uplink_action": {is_procedure: false, num_in: 4, num_out: 0}, + "get_uplink_action": {is_procedure: false, num_in: 5, num_out: 0}, "get_downlink_action": {is_procedure: false, num_in: 1, num_out: 0}, "get_rule": {is_procedure: false, num_in: 1, num_out: 0}, "get_all_rules": {is_procedure: false, num_in: 0, num_out: 0}, diff --git a/internal/netfunc/headend-gtp4-ctrl.go b/internal/netfunc/headend-gtp4-ctrl.go index 68dd322..1b3279d 100644 --- a/internal/netfunc/headend-gtp4-ctrl.go +++ b/internal/netfunc/headend-gtp4-ctrl.go @@ -16,6 +16,7 @@ import ( gopacket_gtp "github.com/nextmn/gopacket-gtp" gopacket_srv6 "github.com/nextmn/gopacket-srv6" + "github.com/nextmn/json-api/jsonapi" "github.com/nextmn/rfc9433/encoding" "github.com/google/gopacket" @@ -141,7 +142,7 @@ func (h HeadendGTP4WithCtrl) Handle(ctx context.Context, packet []byte) ([]byte, innerHeaderSrcIPv4 := netip.AddrFrom4([4]byte{inner.SrcIP[0], inner.SrcIP[1], inner.SrcIP[2], inner.SrcIP[3]}) innerHeaderDstIPv4 := netip.AddrFrom4([4]byte{inner.DstIP[0], inner.DstIP[1], inner.DstIP[2], inner.DstIP[3]}) - action, err := h.db.GetUplinkAction(ctx, teid, gnb_ip, innerHeaderSrcIPv4, innerHeaderDstIPv4) + action, err := h.db.GetUplinkAction(ctx, jsonapi.Fteid{Teid: teid, Addr: dest_addr}, gnb_ip, innerHeaderSrcIPv4, innerHeaderDstIPv4) if err != nil { return nil, err }