diff --git a/dataplane/saiserver/routing.go b/dataplane/saiserver/routing.go index 51100f52..1b80aa68 100644 --- a/dataplane/saiserver/routing.go +++ b/dataplane/saiserver/routing.go @@ -680,8 +680,12 @@ func ifaceCounterID(oid uint64, input bool) string { // CreateRouterInterfaces creates a new router interface. func (ri *routerInterface) CreateRouterInterface(ctx context.Context, req *saipb.CreateRouterInterfaceRequest) (*saipb.CreateRouterInterfaceResponse, error) { id := ri.mgr.NextID() + + vlanID := uint16(0) switch req.GetType() { case saipb.RouterInterfaceType_ROUTER_INTERFACE_TYPE_PORT: + case saipb.RouterInterfaceType_ROUTER_INTERFACE_TYPE_SUB_PORT: + vlanID = uint16(req.GetOuterVlanId()) case saipb.RouterInterfaceType_ROUTER_INTERFACE_TYPE_LOOPBACK: // TODO: Support loopback interfaces slog.WarnContext(ctx, "loopback interfaces not supported") return &saipb.CreateRouterInterfaceResponse{Oid: id}, nil @@ -715,23 +719,48 @@ func (ri *routerInterface) CreateRouterInterface(ctx context.Context, req *saipb return nil, err } - // Link the port to the interface. - _, err = ri.dataplane.TableEntryAdd(ctx, fwdconfig.TableEntryAddRequest(ri.dataplane.ID(), inputIfaceTable). + inReq := fwdconfig.TableEntryAddRequest(ri.dataplane.ID(), inputIfaceTable). AppendEntry( - fwdconfig.EntryDesc(fwdconfig.ExactEntry(fwdconfig.PacketFieldBytes(fwdpb.PacketFieldNum_PACKET_FIELD_NUM_PACKET_PORT_INPUT).WithUint64(uint64(obj.NID())))), + fwdconfig.EntryDesc(fwdconfig.ExactEntry( + fwdconfig.PacketFieldBytes(fwdpb.PacketFieldNum_PACKET_FIELD_NUM_PACKET_PORT_INPUT).WithUint64(uint64(obj.NID())), + fwdconfig.PacketFieldBytes(fwdpb.PacketFieldNum_PACKET_FIELD_NUM_VLAN_TAG).WithBytes(binary.BigEndian.AppendUint16(nil, vlanID)), + )), fwdconfig.Action(fwdconfig.UpdateAction(fwdpb.UpdateType_UPDATE_TYPE_SET, fwdpb.PacketFieldNum_PACKET_FIELD_NUM_INPUT_IFACE).WithUint64Value(id)), fwdconfig.Action(fwdconfig.FlowCounterAction(inCounter)), - ).Build()) + ).Build() + + if vlanID != 0 { + inReq.Entries[0].Actions = append(inReq.Entries[0].Actions, &fwdpb.ActionDesc{ + ActionType: fwdpb.ActionType_ACTION_TYPE_DECAP, + Action: &fwdpb.ActionDesc_Decap{ + Decap: &fwdpb.DecapActionDesc{ + HeaderId: fwdpb.PacketHeaderId_PACKET_HEADER_ID_ETHERNET_VLAN, + }, + }, + }) + } + + // Link the port to the interface. + _, err = ri.dataplane.TableEntryAdd(ctx, inReq) if err != nil { return nil, err } - _, err = ri.dataplane.TableEntryAdd(ctx, fwdconfig.TableEntryAddRequest(ri.dataplane.ID(), outputIfaceTable). + outReq := fwdconfig.TableEntryAddRequest(ri.dataplane.ID(), outputIfaceTable). AppendEntry( fwdconfig.EntryDesc(fwdconfig.ExactEntry(fwdconfig.PacketFieldBytes(fwdpb.PacketFieldNum_PACKET_FIELD_NUM_OUTPUT_IFACE).WithUint64(id))), fwdconfig.Action(fwdconfig.TransmitAction(fmt.Sprint(req.GetPortId()))), fwdconfig.Action(fwdconfig.FlowCounterAction(outCounter)), - ).Build()) + ).Build() + + if vlanID != 0 { + outReq.Entries[0].Actions = append(inReq.Entries[0].Actions, + fwdconfig.Action(fwdconfig.EncapAction(fwdpb.PacketHeaderId_PACKET_HEADER_ID_ETHERNET_VLAN)).Build(), + fwdconfig.Action(fwdconfig.UpdateAction(fwdpb.UpdateType_UPDATE_TYPE_SET, fwdpb.PacketFieldNum_PACKET_FIELD_NUM_VLAN_TAG).WithValue(binary.BigEndian.AppendUint16(nil, vlanID))).Build(), + ) + } + + _, err = ri.dataplane.TableEntryAdd(ctx, outReq) if err != nil { return nil, err } diff --git a/dataplane/saiserver/routing_test.go b/dataplane/saiserver/routing_test.go index 1b170474..6f8ed5ab 100644 --- a/dataplane/saiserver/routing_test.go +++ b/dataplane/saiserver/routing_test.go @@ -16,6 +16,7 @@ package saiserver import ( "context" + "encoding/binary" "testing" "github.com/google/go-cmp/cmp" @@ -828,7 +829,7 @@ func TestCreateRouterInterface(t *testing.T) { req: &saipb.CreateRouterInterfaceRequest{}, wantErr: "InvalidArgument", }, { - desc: "success port", + desc: "success port - port", req: &saipb.CreateRouterInterfaceRequest{ PortId: proto.Uint64(10), Type: saipb.RouterInterfaceType_ROUTER_INTERFACE_TYPE_PORT.Enum(), @@ -844,6 +845,61 @@ func TestCreateRouterInterface(t *testing.T) { FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_PACKET_PORT_INPUT, }}, Bytes: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + }, { + FieldId: &fwdpb.PacketFieldId{Field: &fwdpb.PacketField{ + FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_VLAN_TAG, + }}, + Bytes: binary.BigEndian.AppendUint16(nil, 0), + }}, + }, + }}, + Actions: []*fwdpb.ActionDesc{{ + ActionType: fwdpb.ActionType_ACTION_TYPE_UPDATE, + Action: &fwdpb.ActionDesc_Update{ + Update: &fwdpb.UpdateActionDesc{ + Type: fwdpb.UpdateType_UPDATE_TYPE_SET, + FieldId: &fwdpb.PacketFieldId{Field: &fwdpb.PacketField{ + FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_INPUT_IFACE, + }}, + Field: &fwdpb.PacketFieldId{Field: &fwdpb.PacketField{}}, + Value: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + }, + }, + }, { + ActionType: fwdpb.ActionType_ACTION_TYPE_FLOW_COUNTER, + Action: &fwdpb.ActionDesc_Flow{ + Flow: &fwdpb.FlowCounterActionDesc{ + CounterId: &fwdpb.FlowCounterId{ + ObjectId: &fwdpb.ObjectId{Id: "1-in-counter"}, + }, + }, + }, + }}, + }}, + }, + }, { + desc: "success port - subport", + req: &saipb.CreateRouterInterfaceRequest{ + PortId: proto.Uint64(10), + Type: saipb.RouterInterfaceType_ROUTER_INTERFACE_TYPE_SUB_PORT.Enum(), + OuterVlanId: proto.Uint32(100), + }, + wantReq: &fwdpb.TableEntryAddRequest{ + ContextId: &fwdpb.ContextId{Id: "foo"}, + TableId: &fwdpb.TableId{ObjectId: &fwdpb.ObjectId{Id: inputIfaceTable}}, + Entries: []*fwdpb.TableEntryAddRequest_Entry{{ + EntryDesc: &fwdpb.EntryDesc{Entry: &fwdpb.EntryDesc_Exact{ + Exact: &fwdpb.ExactEntryDesc{ + Fields: []*fwdpb.PacketFieldBytes{{ + FieldId: &fwdpb.PacketFieldId{Field: &fwdpb.PacketField{ + FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_PACKET_PORT_INPUT, + }}, + Bytes: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + }, { + FieldId: &fwdpb.PacketFieldId{Field: &fwdpb.PacketField{ + FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_VLAN_TAG, + }}, + Bytes: binary.BigEndian.AppendUint16(nil, 100), }}, }, }}, @@ -868,6 +924,13 @@ func TestCreateRouterInterface(t *testing.T) { }, }, }, + }, { + ActionType: fwdpb.ActionType_ACTION_TYPE_DECAP, + Action: &fwdpb.ActionDesc_Decap{ + Decap: &fwdpb.DecapActionDesc{ + HeaderId: fwdpb.PacketHeaderId_PACKET_HEADER_ID_ETHERNET_VLAN, + }, + }, }}, }}, }, diff --git a/dataplane/saiserver/switch.go b/dataplane/saiserver/switch.go index 1fdc97fd..92ab17a9 100644 --- a/dataplane/saiserver/switch.go +++ b/dataplane/saiserver/switch.go @@ -601,6 +601,10 @@ func (sw *saiSwitch) CreateSwitch(ctx context.Context, _ *saipb.CreateSwitchRequ Field: &fwdpb.PacketField{ FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_PACKET_PORT_INPUT, // TODO: Figure out all the ways ports can be mapped to interfaces. }, + }, { + Field: &fwdpb.PacketField{ + FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_VLAN_TAG, // TODO: Figure out all the ways ports can be mapped to interfaces. + }, }}, }, },