diff --git a/go.mod b/go.mod index f0bf674..fe65aee 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.16 + github.com/nextmn/json-api v0.0.17 github.com/nextmn/logrus-formatter v0.0.1 github.com/nextmn/rfc9433 v0.0.2 github.com/sirupsen/logrus v1.9.3 @@ -21,14 +21,14 @@ require ( require ( github.com/bytedance/sonic v1.12.7 // indirect - github.com/bytedance/sonic/loader v0.2.2 // indirect + github.com/bytedance/sonic/loader v0.2.3 // indirect github.com/cloudwego/base64x v0.1.4 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/gabriel-vasile/mimetype v1.4.8 // indirect github.com/gin-contrib/sse v1.0.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.23.0 // indirect + github.com/go-playground/validator/v10 v10.24.0 // indirect github.com/goccy/go-json v0.10.4 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.9 // indirect @@ -46,5 +46,5 @@ require ( golang.org/x/net v0.34.0 // indirect golang.org/x/sys v0.29.0 // indirect golang.org/x/text v0.21.0 // indirect - google.golang.org/protobuf v1.36.2 // indirect + google.golang.org/protobuf v1.36.3 // indirect ) diff --git a/go.sum b/go.sum index cdc83f5..227880c 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ github.com/bytedance/sonic v1.12.7 h1:CQU8pxOy9HToxhndH0Kx/S1qU/CuS9GnKYrGioDcU1Q= github.com/bytedance/sonic v1.12.7/go.mod h1:tnbal4mxOMju17EGfknm2XyYcpyCnIROYOEYuemj13I= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= -github.com/bytedance/sonic/loader v0.2.2 h1:jxAJuN9fOot/cyz5Q6dUuMJF5OqQ6+5GfA8FjjQ0R4o= -github.com/bytedance/sonic/loader v0.2.2/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= +github.com/bytedance/sonic/loader v0.2.3 h1:yctD0Q3v2NOGfSWPLPvG2ggA2kV6TS6s4wioyEqssH0= +github.com/bytedance/sonic/loader v0.2.3/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= @@ -25,8 +25,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o= -github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg= +github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus= github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= @@ -57,8 +57,8 @@ github.com/nextmn/gopacket-gtp v0.0.8 h1:NF3GRPSdDjmoTeyN584NwbJJJ3tHwPZysZE2ma/ github.com/nextmn/gopacket-gtp v0.0.8/go.mod h1:94jLjLU04IOVTKBXUP09MXZCgmlizqmflU2ion1ht6E= github.com/nextmn/gopacket-srv6 v0.0.8 h1:oP4wuJ7dOiV/gWmX3zoFcdp2dKdSWLUaxH2fJ3TYAwA= github.com/nextmn/gopacket-srv6 v0.0.8/go.mod h1:2Tyuo9zsG0bP2IhC4tVRgPRuyUqOgrvEEH9seJSZTlU= -github.com/nextmn/json-api v0.0.16 h1:RU5eTVvnwrYK0Zmh/EhdO7Q3A+hUWjavg9ytJknaYTU= -github.com/nextmn/json-api v0.0.16/go.mod h1:CQXeNPj9MDGsEExtnqJFIGjLgZAKsmOoO2fy+mep7Ak= +github.com/nextmn/json-api v0.0.17 h1:u8Q32fxy7xh1XCGG/QrtZIN/8NVDU6Q9W8CnaMZdZq0= +github.com/nextmn/json-api v0.0.17/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= @@ -117,8 +117,8 @@ golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU= -google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU= +google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/database/database.go b/internal/database/database.go index 5c7c3e2..7e7cc55 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -125,7 +125,14 @@ func (db *Database) InsertRule(ctx context.Context, r n4tosrv6.Rule) (*uuid.UUID } else { dst = r.Match.Payload.Dst.String() + "/32" } - err := stmt.QueryRowContext(ctx, r.Enabled, dst, pq.Array(srh)).Scan(&id) + src_ipv6 := "::" + if r.Action.SourceGtp4 != nil { + src_ipv6 = r.Action.SourceGtp4.String() + } else { + return nil, fmt.Errorf("Empty SourceGtp4 for downlink Action") + } + + err := stmt.QueryRowContext(ctx, r.Enabled, dst, pq.Array(srh), src_ipv6).Scan(&id) return &id, err } else { return nil, fmt.Errorf("Procedure not registered") @@ -139,13 +146,14 @@ func (db *Database) GetRule(ctx context.Context, uuid uuid.UUID) (n4tosrv6.Rule, var type_uplink bool var enabled bool var action_srh []string + var action_source_gtp4 *string var match_ue_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, pq.Array(&match_gnb_ip), &match_uplink_teid, &match_uplink_upf, &match_service_ip) + err := stmt.QueryRowContext(ctx, uuid.String()).Scan(&type_uplink, &enabled, pq.Array(&action_srh), &action_source_gtp4, &match_ue_ip, pq.Array(&match_gnb_ip), &match_uplink_teid, &match_uplink_upf, &match_service_ip) if err != nil { return n4tosrv6.Rule{}, err } @@ -202,8 +210,18 @@ func (db *Database) GetRule(ctx context.Context, uuid uuid.UUID) (n4tosrv6.Rule, return n4tosrv6.Rule{}, err } + if action_source_gtp4 == nil { + return n4tosrv6.Rule{}, fmt.Errorf("Empty SourceGtp4 for downlink rule") + } + + source_gtp4, err := netip.ParseAddr(*action_source_gtp4) + if err != nil { + return n4tosrv6.Rule{}, err + } + rule.Action = n4tosrv6.Action{ - SRH: *srh, + SRH: *srh, + SourceGtp4: &source_gtp4, } return rule, err @@ -217,6 +235,7 @@ func (db *Database) GetRules(ctx context.Context) (n4tosrv6.RuleMap, error) { var type_uplink bool var enabled bool var action_srh []string + var action_source_gtp4 *string var match_ue_ip string var match_gnb_ip []string var match_uplink_teid *uint32 @@ -234,7 +253,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, pq.Array(&match_gnb_ip), &match_uplink_teid, &match_uplink_upf, &match_service_ip) + err := rows.Scan(&uuid, &type_uplink, &enabled, pq.Array(&action_srh), &action_source_gtp4, &match_ue_ip, pq.Array(&match_gnb_ip), &match_uplink_teid, &match_uplink_upf, &match_service_ip) if err != nil { return m, err } @@ -285,6 +304,14 @@ func (db *Database) GetRules(ctx context.Context) (n4tosrv6.RuleMap, error) { } } } + if action_source_gtp4 == nil { + return n4tosrv6.RuleMap{}, fmt.Errorf("Empty SourceGtp4 for downlink rule") + } + + source_gtp4, err := netip.ParseAddr(*action_source_gtp4) + if err != nil { + return n4tosrv6.RuleMap{}, err + } srh, err := n4tosrv6.NewSRH(action_srh) if err != nil { @@ -292,7 +319,8 @@ func (db *Database) GetRules(ctx context.Context) (n4tosrv6.RuleMap, error) { } rule.Action = n4tosrv6.Action{ - SRH: *srh, + SRH: *srh, + SourceGtp4: &source_gtp4, } m[uuid] = rule } @@ -351,7 +379,9 @@ func (db *Database) GetUplinkAction(ctx context.Context, uplinkFTeid jsonapi.Fte if err != nil { return n4tosrv6.Action{}, err } - return n4tosrv6.Action{SRH: *srh}, err + return n4tosrv6.Action{ + SRH: *srh, + }, err } else { return n4tosrv6.Action{}, fmt.Errorf("Procedure not registered") } @@ -359,8 +389,9 @@ func (db *Database) GetUplinkAction(ctx context.Context, uplinkFTeid jsonapi.Fte func (db *Database) GetDownlinkAction(ctx context.Context, ueIp netip.Addr) (n4tosrv6.Action, error) { var action_srh []string + var action_source_gtp4 *string if stmt, ok := db.stmt["get_downlink_action"]; ok { - err := stmt.QueryRowContext(ctx, ueIp.String()).Scan(pq.Array(&action_srh)) + err := stmt.QueryRowContext(ctx, ueIp.String()).Scan(pq.Array(&action_srh), &action_source_gtp4) if err != nil { return n4tosrv6.Action{}, err } @@ -368,7 +399,17 @@ func (db *Database) GetDownlinkAction(ctx context.Context, ueIp netip.Addr) (n4t if err != nil { return n4tosrv6.Action{}, err } - return n4tosrv6.Action{SRH: *srh}, err + if action_source_gtp4 == nil { + return n4tosrv6.Action{}, fmt.Errorf("Empty SourceGtp4 for downlink rule") + } + source_gtp4, err := netip.ParseAddr(*action_source_gtp4) + if err != nil { + return n4tosrv6.Action{}, err + } + return n4tosrv6.Action{ + SRH: *srh, + SourceGtp4: &source_gtp4, + }, err } else { return n4tosrv6.Action{}, fmt.Errorf("Procedure not registered") } @@ -376,11 +417,17 @@ func (db *Database) GetDownlinkAction(ctx context.Context, ueIp netip.Addr) (n4t func (db *Database) UpdateAction(ctx context.Context, uuidRule uuid.UUID, action n4tosrv6.Action) error { srh := []string{} + source_gtp4 := "::" + if action.SourceGtp4 != nil { + source_gtp4 = action.SourceGtp4.String() + } else { + return fmt.Errorf("Empty SourceGtp4 for downlink rule") + } for _, ip := range action.SRH { srh = append(srh, ip.String()) } if stmt, ok := db.stmt["update_action"]; ok { - _, err := stmt.ExecContext(ctx, uuidRule.String(), pq.Array(srh)) + _, err := stmt.ExecContext(ctx, uuidRule.String(), pq.Array(srh), source_gtp4) return err } else { return fmt.Errorf("Procedure not registered") diff --git a/internal/database/database.sql b/internal/database/database.sql index 29efab3..1417a36 100644 --- a/internal/database/database.sql +++ b/internal/database/database.sql @@ -8,6 +8,7 @@ CREATE TABLE IF NOT EXISTS rule ( type_uplink BOOL NOT NULL, enabled BOOL NOT NULL, action_srh INET ARRAY NOT NULL, + action_source_gtp4 INET, match_ue_ip CIDR NOT NULL, match_gnb_ip CIDR ARRAY, match_service_ip CIDR, @@ -33,12 +34,13 @@ END;$$; CREATE OR REPLACE PROCEDURE insert_downlink_rule( IN in_enabled BOOL, IN in_ue_ip CIDR, IN in_srh INET ARRAY, + IN in_source_gtp4 INET, OUT out_uuid UUID ) LANGUAGE plpgsql AS $$ BEGIN - INSERT INTO rule(type_uplink, enabled, match_ue_ip, action_srh) - VALUES(FALSE, in_enabled, in_ue_ip, in_srh) RETURNING rule.uuid INTO out_uuid; + INSERT INTO rule(type_uplink, enabled, match_ue_ip, action_srh, action_source_gtp4) + VALUES(FALSE, in_enabled, in_ue_ip, in_srh, in_source_gtp4) RETURNING rule.uuid INTO out_uuid; END;$$; @@ -78,11 +80,13 @@ END;$$; CREATE OR REPLACE PROCEDURE update_action( IN in_uuid UUID, - IN in_srh INET ARRAY + IN in_srh INET ARRAY, + IN in_source_gtp4 INET ) LANGUAGE plpgsql AS $$ BEGIN UPDATE rule SET action_srh = in_srh WHERE rule.uuid = in_uuid; + UPDATE rule SET action_source_gtp4 = in_source_gtp4 WHERE rule.uuid = in_uuid; END;$$; CREATE OR REPLACE FUNCTION get_uplink_action( @@ -111,11 +115,12 @@ CREATE OR REPLACE FUNCTION get_downlink_action( IN in_ue_ip_address INET ) RETURNS TABLE ( - t_action_srh INET ARRAY + t_action_srh INET ARRAY, + t_action_source_gtp4 INET ) AS $$ BEGIN - RETURN QUERY SELECT rule.action_srh AS "t_action_srh" + RETURN QUERY SELECT rule.action_srh AS "t_action_srh", rule.action_source_gtp4 AS "t_action_source_gtp4" FROM rule WHERE (rule.type_uplink = FALSE AND rule.enabled = TRUE AND match_ue_ip && in_ue_ip_address); @@ -128,6 +133,7 @@ RETURNS TABLE ( t_type_uplink BOOL, t_enabled BOOL, t_action_srh INET ARRAY, + t_action_source_gtp4 INET, t_match_ue_ip CIDR, t_match_gnb_ip CIDR ARRAY, t_match_uplink_teid BIGINT, @@ -137,7 +143,8 @@ RETURNS TABLE ( AS $$ BEGIN RETURN QUERY SELECT type_uplink AS "t_type_uplink", enabled AS "t_enabled", - action_srh AS "t_action_srh", match_ue_ip AS "t_match_ue_ip", match_gnb_ip AS "t_match_gnb_ip", + action_srh AS "t_action_srh", action_source_gtp4 AS "t_action_source_gtp4", + match_ue_ip AS "t_match_ue_ip", match_gnb_ip AS "t_match_gnb_ip", match_uplink_teid AS "t_match_uplink_teid", match_uplink_upf AS "t_match_uplink_upf", match_service_ip AS "t_match_service_ip" FROM rule @@ -150,6 +157,7 @@ RETURNS TABLE ( t_type_uplink BOOL, t_enabled BOOL, t_action_srh INET ARRAY, + t_action_source_gtp4 INET, t_match_ue_ip CIDR, t_match_gnb_ip CIDR ARRAY, t_match_uplink_teid BIGINT, @@ -160,7 +168,8 @@ AS $$ BEGIN RETURN QUERY SELECT uuid AS "t_uuid", type_uplink AS "t_type_uplink", enabled AS "t_enabled", - action_srh AS "t_action_srh", match_ue_ip AS "t_match_ue_ip", match_gnb_ip AS "t_match_gnb_ip", + action_srh AS "t_action_srh", action_source_gtp4 AS "t_action_source_gtp4", + match_ue_ip AS "t_match_ue_ip", match_gnb_ip AS "t_match_gnb_ip", match_uplink_teid AS "t_match_uplink_teid", match_uplink_upf AS "t_match_uplink_upf", match_service_ip AS "t_match_service_ip" FROM rule; diff --git a/internal/database/database_gen.go b/internal/database/database_gen.go index 4bf7ebe..1faf4f2 100644 --- a/internal/database/database_gen.go +++ b/internal/database/database_gen.go @@ -14,12 +14,12 @@ type procedureOrFunction struct { var procedures = map[string]procedureOrFunction{ "insert_uplink_rule": {is_procedure: true, num_in: 7, num_out: 1}, - "insert_downlink_rule": {is_procedure: true, num_in: 3, num_out: 1}, + "insert_downlink_rule": {is_procedure: true, num_in: 4, 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}, - "update_action": {is_procedure: true, num_in: 2, num_out: 0}, + "update_action": {is_procedure: true, num_in: 3, 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}, diff --git a/internal/netfunc/headend-encaps-ctrl.go b/internal/netfunc/headend-encaps-ctrl.go index 71b2f8f..1c89793 100644 --- a/internal/netfunc/headend-encaps-ctrl.go +++ b/internal/netfunc/headend-encaps-ctrl.go @@ -45,7 +45,10 @@ func (h HeadendEncapsWithCtrl) Handle(ctx context.Context, packet []byte) ([]byt if err != nil { return nil, err } - srgw_gtp_ip := netip.MustParseAddr("10.3.0.1") // FIXME: dont hardcode + if action.SourceGtp4 == nil { + return nil, fmt.Errorf("Empty SourceGtp4 for downlink Action") + } + srgw_gtp_ip := *action.SourceGtp4 ipv6Src := encoding.NewMGTP4IPv6Src(h.srcPrefix, srgw_gtp_ip.As4(), 2152) // FIXME:dont hardcode udp port number to 2152 src, err := ipv6Src.Marshal() if err != nil {