Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions dataplane/forwarding/fwdconfig/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,3 +409,25 @@ func (m *MirrorActionBuilder) set(a *fwdpb.ActionDesc) {
func (m *MirrorActionBuilder) actionType() fwdpb.ActionType {
return fwdpb.ActionType_ACTION_TYPE_MIRROR
}

// BridgeLearnActionBuilder is a builder for a bridge learn action.
type BridgeLearnActionBuilder struct {
tableID string
}

// BridgeLearnAction returns a new bridge learn action builder.
func BridgeLearnAction(tableID string) *BridgeLearnActionBuilder {
return &BridgeLearnActionBuilder{tableID: tableID}
}

func (b *BridgeLearnActionBuilder) set(a *fwdpb.ActionDesc) {
a.Action = &fwdpb.ActionDesc_Bridge{
Bridge: &fwdpb.BridgeLearnActionDesc{
TableId: &fwdpb.TableId{ObjectId: &fwdpb.ObjectId{Id: b.tableID}},
},
}
}

func (b *BridgeLearnActionBuilder) actionType() fwdpb.ActionType {
return fwdpb.ActionType_ACTION_TYPE_BRIDGE_LEARN
}
73 changes: 73 additions & 0 deletions dataplane/saiserver/fdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,16 @@ package saiserver

import (
"context"
"fmt"

"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

"github.com/openconfig/lemming/dataplane/forwarding/fwdconfig"
saipb "github.com/openconfig/lemming/dataplane/proto/sai"
"github.com/openconfig/lemming/dataplane/saiserver/attrmgr"
fwdpb "github.com/openconfig/lemming/proto/forwarding"
)

type fdb struct {
Expand All @@ -41,3 +46,71 @@ func newFdb(mgr *attrmgr.AttrMgr, dataplane switchDataplaneAPI, s *grpc.Server)
func (f *fdb) FlushFdbEntries(ctx context.Context, req *saipb.FlushFdbEntriesRequest) (*saipb.FlushFdbEntriesResponse, error) {
return &saipb.FlushFdbEntriesResponse{}, nil
}

func (f *fdb) CreateFdbEntry(ctx context.Context, req *saipb.CreateFdbEntryRequest) (*saipb.CreateFdbEntryResponse, error) {
entry := req.GetEntry()
if entry == nil {
return nil, status.Errorf(codes.InvalidArgument, "FDB entry is required")
}

mac := entry.GetMacAddress()
if len(mac) == 0 {
return nil, status.Errorf(codes.InvalidArgument, "MAC address is required")
}

portOID := req.GetBridgePortId()
if portOID == 0 {
return nil, status.Errorf(codes.InvalidArgument, "Bridge port ID is required")
}

bpReq := &saipb.GetBridgePortAttributeRequest{
Oid: portOID,
AttrType: []saipb.BridgePortAttr{saipb.BridgePortAttr_BRIDGE_PORT_ATTR_PORT_ID},
}
bpResp := &saipb.GetBridgePortAttributeResponse{}
if err := f.mgr.PopulateAttributes(bpReq, bpResp); err != nil {
return nil, fmt.Errorf("failed to populate bridge port %d: %v", portOID, err)
}

portID := bpResp.GetAttr().GetPortId()
if portID == 0 {
return nil, fmt.Errorf("cannot find port ID for bridge port %d", portOID)
}

addReq := fwdconfig.TableEntryAddRequest(f.dataplane.ID(), FDBTable).AppendEntry(
fwdconfig.EntryDesc(fwdconfig.ExactEntry(
fwdconfig.PacketFieldBytes(fwdpb.PacketFieldNum_PACKET_FIELD_NUM_ETHER_MAC_DST).WithBytes(mac),
)),
fwdconfig.TransmitAction(fmt.Sprint(portID)),
).Build()

if _, err := f.dataplane.TableEntryAdd(ctx, addReq); err != nil {
return nil, fmt.Errorf("failed to add FDB entry to dataplane: %v", err)
}

return &saipb.CreateFdbEntryResponse{}, nil
}

func (f *fdb) RemoveFdbEntry(ctx context.Context, req *saipb.RemoveFdbEntryRequest) (*saipb.RemoveFdbEntryResponse, error) {
entry := req.GetEntry()
if entry == nil {
return nil, status.Errorf(codes.InvalidArgument, "FDB entry is required")
}

mac := entry.GetMacAddress()
if len(mac) == 0 {
return nil, status.Errorf(codes.InvalidArgument, "MAC address is required")
}

delReq := fwdconfig.TableEntryRemoveRequest(f.dataplane.ID(), FDBTable).AppendEntry(
fwdconfig.EntryDesc(fwdconfig.ExactEntry(
fwdconfig.PacketFieldBytes(fwdpb.PacketFieldNum_PACKET_FIELD_NUM_ETHER_MAC_DST).WithBytes(mac),
)),
).Build()

if _, err := f.dataplane.TableEntryRemove(ctx, delReq); err != nil {
return nil, fmt.Errorf("failed to remove FDB entry from dataplane: %v", err)
}

return &saipb.RemoveFdbEntryResponse{}, nil
}
14 changes: 10 additions & 4 deletions dataplane/saiserver/ports.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package saiserver

import (
"context"
"encoding/binary"
"fmt"
"log/slog"
"net"
Expand Down Expand Up @@ -64,6 +65,7 @@ var getInterface = net.InterfaceByName
func getPreIngressPipeline() []*fwdpb.ActionDesc {
return []*fwdpb.ActionDesc{
fwdconfig.Action(fwdconfig.LookupAction(tunTermTable)).Build(), // Decap the packet if we have a tunnel.
fwdconfig.Action(fwdconfig.LookupAction(VlanTable)).Build(), // Classify incoming packets to VLAN.
fwdconfig.Action(fwdconfig.LookupAction(inputIfaceTable)).Build(), // Match packet to interface.
fwdconfig.Action(fwdconfig.LookupAction(IngressVRFTable)).Build(), // Match interface to VRF.
fwdconfig.Action(fwdconfig.LookupAction(PreIngressActionTable)).Build(), // Run pre-ingress actions.
Expand Down Expand Up @@ -101,7 +103,9 @@ func getL3Pipeline(skipIPValidation bool) []*fwdpb.ActionDesc {
func getL2Pipeline() []*fwdpb.ActionDesc {
return []*fwdpb.ActionDesc{
fwdconfig.Action(fwdconfig.LookupAction(IngressActionTable)).Build(), // Run ingress action.
fwdconfig.Action(fwdconfig.LookupAction(outputTable)).Build(), // Take final decision on forward, drop, or trap.
fwdconfig.Action(fwdconfig.BridgeLearnAction(FDBTable)).Build(), // Learn source MAC into FDB.
fwdconfig.Action(fwdconfig.LookupAction(FDBTable)).Build(), // Match dest MAC in FDB.
fwdconfig.Action(fwdconfig.LookupAction(FloodTable)).Build(), // Match VLAN tag if FDB misses.
{
ActionType: fwdpb.ActionType_ACTION_TYPE_OUTPUT,
},
Expand Down Expand Up @@ -301,8 +305,10 @@ func (port *port) CreatePort(ctx context.Context, req *saipb.CreatePortRequest)
Update: &fwdpb.PortUpdateDesc{
Port: &fwdpb.PortUpdateDesc_Kernel{
Kernel: &fwdpb.KernelPortUpdateDesc{
Inputs: getPreIngressPipeline(),
Outputs: nil,
Inputs: getPreIngressPipeline(),
Outputs: []*fwdpb.ActionDesc{
fwdconfig.Action(fwdconfig.DecapAction(fwdpb.PacketHeaderId_PACKET_HEADER_ID_ETHERNET_VLAN)).Build(),
},
},
},
},
Expand Down Expand Up @@ -361,7 +367,7 @@ func (port *port) CreatePort(ctx context.Context, req *saipb.CreatePortRequest)
fwdconfig.EntryDesc(fwdconfig.ExactEntry(fwdconfig.PacketFieldBytes(fwdpb.PacketFieldNum_PACKET_FIELD_NUM_PACKET_PORT_INPUT).WithUint64(nid.GetNid())))).Build()
vlanReq.Entries[0].Actions = []*fwdpb.ActionDesc{
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).WithUint64Value(uint64(attrs.GetPortVlanId()))).Build(),
fwdconfig.Action(fwdconfig.UpdateAction(fwdpb.UpdateType_UPDATE_TYPE_SET, fwdpb.PacketFieldNum_PACKET_FIELD_NUM_VLAN_TAG).WithValue(binary.BigEndian.AppendUint16(nil, uint16(attrs.GetPortVlanId())))).Build(),
}
if _, err := port.dataplane.TableEntryAdd(ctx, vlanReq); err != nil {
return nil, err
Expand Down
57 changes: 56 additions & 1 deletion dataplane/saiserver/routing.go
Original file line number Diff line number Diff line change
Expand Up @@ -1290,6 +1290,36 @@ func (vlan *vlan) CreateVlan(ctx context.Context, r *saipb.CreateVlanRequest) (*
vlan.mgr.StoreAttributes(id, attrs)
vlan.vlans[id] = map[uint64]*vlanMember{}
vlan.oidByVId[r.GetVlanId()] = id

floodID := fmt.Sprintf("vlan_flood_%d", r.GetVlanId())
_, _ = vlan.dataplane.PortCreate(ctx, &fwdpb.PortCreateRequest{
ContextId: &fwdpb.ContextId{Id: vlan.dataplane.ID()},
Port: &fwdpb.PortDesc{
PortType: fwdpb.PortType_PORT_TYPE_AGGREGATE_PORT,
PortId: &fwdpb.PortId{ObjectId: &fwdpb.ObjectId{Id: floodID}},
},
})
_, _ = vlan.dataplane.PortUpdate(ctx, &fwdpb.PortUpdateRequest{
ContextId: &fwdpb.ContextId{Id: vlan.dataplane.ID()},
PortId: &fwdpb.PortId{ObjectId: &fwdpb.ObjectId{Id: floodID}},
Update: &fwdpb.PortUpdateDesc{
Port: &fwdpb.PortUpdateDesc_AggregateAlgo{
AggregateAlgo: &fwdpb.AggregatePortAlgorithmUpdateDesc{
Hash: fwdpb.AggregateHashAlgorithm_AGGREGATE_HASH_ALGORITHM_FLOOD,
},
},
},
})
floodReq := fwdconfig.TableEntryAddRequest(vlan.dataplane.ID(), FloodTable).AppendEntry(
fwdconfig.EntryDesc(fwdconfig.ExactEntry(
fwdconfig.PacketFieldBytes(fwdpb.PacketFieldNum_PACKET_FIELD_NUM_VLAN_TAG).WithBytes(binary.BigEndian.AppendUint16(nil, uint16(r.GetVlanId()))),
)),
fwdconfig.TransmitAction(floodID),
).Build()
if _, err := vlan.dataplane.TableEntryAdd(ctx, floodReq); err != nil {
return nil, err
}

return &saipb.CreateVlanResponse{
Oid: id,
}, nil
Expand Down Expand Up @@ -1371,11 +1401,24 @@ func (vlan *vlan) CreateVlanMember(ctx context.Context, r *saipb.CreateVlanMembe
fwdconfig.EntryDesc(fwdconfig.ExactEntry(fwdconfig.PacketFieldBytes(fwdpb.PacketFieldNum_PACKET_FIELD_NUM_PACKET_PORT_INPUT).WithUint64(nid.GetNid())))).Build()
vlanReq.Entries[0].Actions = []*fwdpb.ActionDesc{
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).WithUint64Value(uint64(vId))).Build(),
fwdconfig.Action(fwdconfig.UpdateAction(fwdpb.UpdateType_UPDATE_TYPE_SET, fwdpb.PacketFieldNum_PACKET_FIELD_NUM_VLAN_TAG).WithValue(binary.BigEndian.AppendUint16(nil, uint16(vId)))).Build(),
}
if _, err := vlan.dataplane.TableEntryAdd(ctx, vlanReq); err != nil {
return nil, err
}
floodID := fmt.Sprintf("vlan_flood_%d", vId)
_, _ = vlan.dataplane.PortUpdate(ctx, &fwdpb.PortUpdateRequest{
ContextId: &fwdpb.ContextId{Id: vlan.dataplane.ID()},
PortId: &fwdpb.PortId{ObjectId: &fwdpb.ObjectId{Id: floodID}},
Update: &fwdpb.PortUpdateDesc{
Port: &fwdpb.PortUpdateDesc_AggregateAdd{
AggregateAdd: &fwdpb.AggregatePortAddMemberUpdateDesc{
PortId: &fwdpb.PortId{ObjectId: &fwdpb.ObjectId{Id: fmt.Sprint(portID)}},
InstanceCount: 1,
},
},
},
})
// Update the attributes and intenal data.
vlanAttrReq := &saipb.GetVlanAttributeRequest{Oid: vOid, AttrType: []saipb.VlanAttr{saipb.VlanAttr_VLAN_ATTR_MEMBER_LIST}}
vlanAttrResp := &saipb.GetVlanAttributeResponse{}
Expand Down Expand Up @@ -1444,6 +1487,18 @@ func (vlan *vlan) RemoveVlanMember(ctx context.Context, r *saipb.RemoveVlanMembe
fwdconfig.EntryDesc(fwdconfig.ExactEntry(fwdconfig.PacketFieldBytes(fwdpb.PacketFieldNum_PACKET_FIELD_NUM_PACKET_PORT_INPUT).WithUint64(nid.GetNid())))).Build()); err != nil {
return nil, err
}
floodID := fmt.Sprintf("vlan_flood_%d", member.Vid)
_, _ = vlan.dataplane.PortUpdate(ctx, &fwdpb.PortUpdateRequest{
ContextId: &fwdpb.ContextId{Id: vlan.dataplane.ID()},
PortId: &fwdpb.PortId{ObjectId: &fwdpb.ObjectId{Id: floodID}},
Update: &fwdpb.PortUpdateDesc{
Port: &fwdpb.PortUpdateDesc_AggregateDel{
AggregateDel: &fwdpb.AggregatePortRemoveMemberUpdateDesc{
PortId: &fwdpb.PortId{ObjectId: &fwdpb.ObjectId{Id: fmt.Sprint(member.PortID)}},
},
},
},
})

delete(vlan.vlans[targetVlanOid], r.GetOid())

Expand Down
35 changes: 35 additions & 0 deletions dataplane/saiserver/switch.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ const (
tunTermTable = "tun-term"
VlanTable = "vlan"
L2MCGroupTable = "l2mcg"
FDBTable = "fdb"
FloodTable = "flood"
policerTabler = "policerTable"
invalidIngress = "invalid-ingress"
invalidIngressV4Table = "invalid-ingress-v4"
Expand Down Expand Up @@ -445,6 +447,39 @@ func (sw *saiSwitch) CreateSwitch(ctx context.Context, _ *saipb.CreateSwitchRequ
if _, err := sw.dataplane.TableCreate(ctx, l2mcGroupReq); err != nil {
return nil, err
}
fdbReq := &fwdpb.TableCreateRequest{
ContextId: &fwdpb.ContextId{Id: sw.dataplane.ID()},
Desc: &fwdpb.TableDesc{
TableType: fwdpb.TableType_TABLE_TYPE_BRIDGE,
TableId: &fwdpb.TableId{ObjectId: &fwdpb.ObjectId{Id: FDBTable}},
Table: &fwdpb.TableDesc_Bridge{
Bridge: &fwdpb.BridgeTableDesc{},
},
},
}
if _, err := sw.dataplane.TableCreate(ctx, fdbReq); err != nil {
return nil, err
}
floodReq := &fwdpb.TableCreateRequest{
ContextId: &fwdpb.ContextId{Id: sw.dataplane.ID()},
Desc: &fwdpb.TableDesc{
TableType: fwdpb.TableType_TABLE_TYPE_EXACT,
TableId: &fwdpb.TableId{ObjectId: &fwdpb.ObjectId{Id: FloodTable}},
Actions: []*fwdpb.ActionDesc{{ActionType: fwdpb.ActionType_ACTION_TYPE_DROP}},
Table: &fwdpb.TableDesc_Exact{
Exact: &fwdpb.ExactTableDesc{
FieldIds: []*fwdpb.PacketFieldId{{
Field: &fwdpb.PacketField{
FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_VLAN_TAG,
},
}},
},
},
},
}
if _, err := sw.dataplane.TableCreate(ctx, floodReq); err != nil {
return nil, err
}
action := &fwdpb.TableCreateRequest{
ContextId: &fwdpb.ContextId{Id: sw.dataplane.ID()},
Desc: &fwdpb.TableDesc{
Expand Down