diff --git a/feature/system/system_base_test/tests/system_g_protocol_test/README.md b/feature/system/system_base_test/tests/system_g_protocol_test/README.md index d4ad58947e1..88e1b72db3f 100644 --- a/feature/system/system_base_test/tests/system_g_protocol_test/README.md +++ b/feature/system/system_base_test/tests/system_g_protocol_test/README.md @@ -10,9 +10,14 @@ Each test will require the DUT configured with a basic service configuration tha should be provided as part of the basic configuration. This setup should also include any security setup for connecting to the services. -The default setup should expect a CA signed certifate and trust bundle which can be +The default setup should expect a CA signed certificate and trust bundle which can be used for mTLS. +The PQC client tests validate the ability of the system to negotiate a PQC based TLS +cipher suite for each of the g* APIs. These tests will need to utilize PQC-enabled +clients to validate the connection. Each of these tests should fail if the server +cannot negotiate a PQC cipher suite. + | Protocol | Port | | --------- | ----- | | gNMI | 9339 | @@ -30,6 +35,11 @@ used for mTLS. | service-1.3 | gNSI client | gNSI authz Get works | | service-1.4 | gRIBI client | gRIBI Get works | | service-1.5 | p4rt client | P4RT Capabilities works | +| service-1.6 | gNMI PQC client | gNMI Get works | +| service-1.7 | gNOI PQC client | gNOI system Time works | +| service-1.8 | gNSI PQC client | gNSI authz Get works | +| service-1.9 | gRIBI PQC client | gRIBI Get works | +| service-1.10 | p4rt PQC client | P4RT Capabilities works | 1. Configure DUT with service configurations for all required services 2. Each test will then create a client to those services and valid each service is properly @@ -55,3 +65,19 @@ rpcs: gribi: gRIBI.Get: ``` + +## Canonical OC + +```json +{ + "system": { + "state": { + "motd-banner": "", + "login-banner": "", + "hostname": "", + "current-datetime": "2022-01-01T00:00:00Z", + "boot-time": "0" + } + } +} +``` \ No newline at end of file diff --git a/feature/system/system_base_test/tests/system_g_protocol_test/system_pqc_protocol_test.go b/feature/system/system_base_test/tests/system_g_protocol_test/system_pqc_protocol_test.go new file mode 100644 index 00000000000..96405340449 --- /dev/null +++ b/feature/system/system_base_test/tests/system_g_protocol_test/system_pqc_protocol_test.go @@ -0,0 +1,156 @@ +/* + Copyright 2026 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + you may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package system_g_protocol_test + +import ( + "context" + "crypto/tls" + "fmt" + "testing" + + "github.com/openconfig/featureprofiles/internal/deviations" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/binding/introspect" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/status" + + gpb "github.com/openconfig/gnmi/proto/gnmi" + spb "github.com/openconfig/gnoi/system" + authzpb "github.com/openconfig/gnsi/authz" + gribipb "github.com/openconfig/gribi/v1/proto/service" + p4rtpb "github.com/p4lang/p4runtime/go/p4/v1" +) + +func dialPQCConn(t *testing.T, dut *ondatra.DUTDevice, svc introspect.Service, wantPort uint32, nonStandardPort bool) *grpc.ClientConn { + t.Helper() + if svc == introspect.GNOI || svc == introspect.GNSI { + // Renaming service name due to gnoi and gnsi always residing on same port as gnmi. + svc = introspect.GNMI + } + dialer := introspect.DUTDialer(t, dut, svc) + t.Logf("Dialing %s on %s (Port: %d) with PQC", svc, dialer.DialTarget, dialer.DevicePort) + if !nonStandardPort { + if dialer.DevicePort != int(wantPort) { + t.Fatalf("DUT is not listening on standard port for %q: got %d, want %d", svc, dialer.DevicePort, wantPort) + } + } + + // Custom TLS config for PQC + tlsConfig := &tls.Config{ + InsecureSkipVerify: true, + CurvePreferences: []tls.CurveID{tls.X25519MLKEM768, tls.X25519, tls.CurveP256}, + } + + target := fmt.Sprintf("%s:%d", dialer.DialTarget, dialer.DevicePort) + conn, err := grpc.NewClient(target, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))) + if err != nil { + t.Fatalf("grpc.NewClient failed to: %q, error: %v", target, err) + } + t.Logf("Successfully dialed %s on %s with PQC", svc, target) + return conn +} + +// TestPQCGNMIClient validates that the DUT listens on standard gNMI Port and supports PQC. +func TestPQCGNMIClient(t *testing.T) { + dut := ondatra.DUT(t, "dut") + conn := dialPQCConn(t, dut, introspect.GNMI, 9339, deviations.NonStandardGRPCPort(dut)) + defer conn.Close() + c := gpb.NewGNMIClient(conn) + + var req *gpb.GetRequest + if deviations.GNMIGetOnRootUnsupported(dut) { + req = &gpb.GetRequest{ + Path: []*gpb.Path{{ + Elem: []*gpb.PathElem{}}}, + Type: gpb.GetRequest_CONFIG, + Encoding: gpb.Encoding_JSON_IETF, + } + + } else { + req = &gpb.GetRequest{Encoding: gpb.Encoding_JSON_IETF, Path: []*gpb.Path{{Elem: []*gpb.PathElem{}}}} + } + + if _, err := c.Get(context.Background(), req); err != nil { + t.Fatalf("gnmi.Get failed: %v", err) + } +} + +// TestPQCGNOIClient validates that the DUT listens on standard gNOI Port and supports PQC. +func TestPQCGNOIClient(t *testing.T) { + dut := ondatra.DUT(t, "dut") + conn := dialPQCConn(t, dut, introspect.GNOI, 9339, deviations.NonStandardGRPCPort(dut)) + defer conn.Close() + c := spb.NewSystemClient(conn) + if _, err := c.Ping(context.Background(), &spb.PingRequest{}); err != nil { + t.Fatalf("gnoi.system.Ping failed: %v", err) + } +} + +// TestPQCGNSIClient validates that the DUT listens on standard gNSI Port and supports PQC. +func TestPQCGNSIClient(t *testing.T) { + dut := ondatra.DUT(t, "dut") + conn := dialPQCConn(t, dut, introspect.GNSI, 9339, deviations.NonStandardGRPCPort(dut)) + defer conn.Close() + c := authzpb.NewAuthzClient(conn) + rsp, err := c.Get(context.Background(), &authzpb.GetRequest{}) + if err != nil { + statusError, _ := status.FromError(err) + if statusError.Code() == codes.FailedPrecondition { + t.Logf("Expected error FAILED_PRECONDITION seen for authz Get Request.") + return + } + t.Fatalf("Unexpected error during authz Get Request: %v", err) + } + t.Logf("gNSI authz get response is %s", rsp) +} + +// TestPQCGRIBIClient validates that the DUT listens on standard gRIBI Port and supports PQC. +func TestPQCGRIBIClient(t *testing.T) { + dut := ondatra.DUT(t, "dut") + conn := dialPQCConn(t, dut, introspect.GRIBI, 9340, deviations.NonStandardGRPCPort(dut)) + defer conn.Close() + c := gribipb.NewGRIBIClient(conn) + if _, err := c.Get(context.Background(), &gribipb.GetRequest{}); err != nil { + t.Fatalf("gribi.Get failed: %v", err) + } +} + +// TestPQCP4RTClient validates that the DUT listens on standard P4RT Port and supports PQC. +func TestPQCP4RTClient(t *testing.T) { + dut := ondatra.DUT(t, "dut") + conn := dialPQCConn(t, dut, introspect.P4RT, 9559, deviations.NonStandardGRPCPort(dut)) + defer conn.Close() + c := p4rtpb.NewP4RuntimeClient(conn) + if deviations.P4RTCapabilitiesUnsupported(dut) { + if _, err := c.Read(context.Background(), &p4rtpb.ReadRequest{ + DeviceId: 1, + Entities: []*p4rtpb.Entity{ + { + Entity: &p4rtpb.Entity_TableEntry{}, + }, + }, + }); err != nil { + t.Fatalf("p4rt.Read failed: %v", err) + } + } else { + if _, err := c.Capabilities(context.Background(), &p4rtpb.CapabilitiesRequest{}); err != nil { + t.Fatalf("p4rt.Capabilites failed: %v", err) + } + } +} diff --git a/testregistry.textproto b/testregistry.textproto index 10b132cc9b6..686b5205070 100644 --- a/testregistry.textproto +++ b/testregistry.textproto @@ -2573,11 +2573,70 @@ test: { readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/macsec/otg_tests/macsec/README.md" exec: " " } - - test: { id: "PF-1.26" description: "Double GUE Decapsulation" readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/policy_forwarding/otg_tests/double_gue_decap/README.md" exec: " " } + +test: { + id: "service-1.1" + description: "gNMI client" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/system_base_test/tests/system_g_protocol_test/README.md" + exec: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/system_base_test/tests/system_g_protocol_test/system_g_protocol_test.go" +} +test: { + id: "service-1.2" + description: "gNOI client" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/system_base_test/tests/system_g_protocol_test/README.md" + exec: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/system_base_test/tests/system_g_protocol_test/system_g_protocol_test.go" +} +test: { + id: "service-1.3" + description: "gNSI client" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/system_base_test/tests/system_g_protocol_test/README.md" + exec: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/system_base_test/tests/system_g_protocol_test/system_g_protocol_test.go" +} +test: { + id: "service-1.4" + description: "gRIBI client" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/system_base_test/tests/system_g_protocol_test/README.md" + exec: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/system_base_test/tests/system_g_protocol_test/system_g_protocol_test.go" +} +test: { + id: "service-1.5" + description: "p4rt client" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/system_base_test/tests/system_g_protocol_test/README.md" + exec: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/system_base_test/tests/system_g_protocol_test/system_g_protocol_test.go" +} +test: { + id: "service-1.6" + description: "gNMI PQC client" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/system_base_test/tests/system_g_protocol_test/README.md" + exec: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/system_base_test/tests/system_g_protocol_test/system_pqc_protocol_test.go" +} +test: { + id: "service-1.7" + description: "gNOI PQC client" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/system_base_test/tests/system_g_protocol_test/README.md" + exec: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/system_base_test/tests/system_g_protocol_test/system_pqc_protocol_test.go" +} +test: { + id: "service-1.8" + description: "gNSI PQC client" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/system_base_test/tests/system_g_protocol_test/README.md" + exec: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/system_base_test/tests/system_g_protocol_test/system_pqc_protocol_test.go" +} +test: { + id: "service-1.9" + description: "gRIBI PQC client" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/system_base_test/tests/system_g_protocol_test/README.md" + exec: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/system_base_test/tests/system_g_protocol_test/system_pqc_protocol_test.go" +} +test: { + id: "service-1.10" + description: "p4rt PQC client" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/system_base_test/tests/system_g_protocol_test/README.md" + exec: "https://github.com/openconfig/featureprofiles/blob/main/feature/system/system_base_test/tests/system_g_protocol_test/system_pqc_protocol_test.go" +}