Skip to content

Commit

Permalink
Implement L2MC via ACL redirect.
Browse files Browse the repository at this point in the history
  • Loading branch information
guoshiuan committed Jul 17, 2024
1 parent 2c43824 commit 3c96642
Show file tree
Hide file tree
Showing 8 changed files with 275 additions and 99 deletions.
3 changes: 3 additions & 0 deletions dataplane/forwarding/protocol/attr.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ var FieldAttr = map[fwdpb.PacketFieldNum]struct {
fwdpb.PacketFieldNum_PACKET_FIELD_NUM_HOST_PORT_ID: {
Sizes: []int{SizeUint64},
},
fwdpb.PacketFieldNum_PACKET_FIELD_NUM_L2MC_GROUP_ID: {
Sizes: []int{SizeUint64},
},
}

// GroupAttr contains attributes for each packet header group.
Expand Down
7 changes: 7 additions & 0 deletions dataplane/forwarding/protocol/metadata/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type Metadata struct {
outputIface []byte // L3 output interface id.
tunnelID []byte // Tunnel ID
hostPortID []byte // Host port id
l2mcgid []byte // L2MC Group ID
desc *protocol.Desc // Protocol descriptor.
}

Expand Down Expand Up @@ -126,6 +127,8 @@ func (m *Metadata) Field(id fwdpacket.FieldID) ([]byte, error) {
return m.tunnelID, nil
case fwdpb.PacketFieldNum_PACKET_FIELD_NUM_HOST_PORT_ID:
return m.hostPortID, nil
case fwdpb.PacketFieldNum_PACKET_FIELD_NUM_L2MC_GROUP_ID:
return m.l2mcgid, nil

default:
return nil, fmt.Errorf("metadata: Field %v failed, unsupported field", id)
Expand Down Expand Up @@ -232,6 +235,9 @@ func (m *Metadata) updateSet(id fwdpacket.FieldID, arg []byte) (bool, error) {
case fwdpb.PacketFieldNum_PACKET_FIELD_NUM_HOST_PORT_ID:
m.hostPortID = arg
return true, nil
case fwdpb.PacketFieldNum_PACKET_FIELD_NUM_L2MC_GROUP_ID:
m.l2mcgid = arg
return true, nil
default:
return false, fmt.Errorf("metadata: UpdateField failed, set unsupported for field %v", id)
}
Expand Down Expand Up @@ -287,6 +293,7 @@ func parse(frame *frame.Frame, desc *protocol.Desc) (protocol.Handler, fwdpb.Pac
outputIface: make([]byte, protocol.FieldAttr[fwdpb.PacketFieldNum_PACKET_FIELD_NUM_OUTPUT_IFACE].DefaultSize),
tunnelID: make([]byte, protocol.FieldAttr[fwdpb.PacketFieldNum_PACKET_FIELD_NUM_TUNNEL_ID].DefaultSize),
hostPortID: make([]byte, protocol.FieldAttr[fwdpb.PacketFieldNum_PACKET_FIELD_NUM_HOST_PORT_ID].DefaultSize),
l2mcgid: make([]byte, protocol.FieldAttr[fwdpb.PacketFieldNum_PACKET_FIELD_NUM_L2MC_GROUP_ID].DefaultSize),
attribute32: make(map[uint8][]byte),
attribute24: make(map[uint8][]byte),
attribute16: make(map[uint8][]byte),
Expand Down
5 changes: 5 additions & 0 deletions dataplane/saiserver/acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,11 @@ func (a *acl) CreateAclEntry(ctx context.Context, req *saipb.CreateAclEntryReque
},
})
}
if req.ActionRedirect != nil {
aReq.Actions = append(aReq.Actions,
fwdconfig.Action(fwdconfig.UpdateAction(fwdpb.UpdateType_UPDATE_TYPE_SET,
fwdpb.PacketFieldNum_PACKET_FIELD_NUM_L2MC_GROUP_ID).WithUint64Value(req.GetActionRedirect().GetOid())).Build())
}

cpuPortReq := &saipb.GetSwitchAttributeRequest{Oid: switchID, AttrType: []saipb.SwitchAttr{saipb.SwitchAttr_SWITCH_ATTR_CPU_PORT}}
resp := &saipb.GetSwitchAttributeResponse{}
Expand Down
149 changes: 141 additions & 8 deletions dataplane/saiserver/l2.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,29 @@ package saiserver

import (
"context"
"fmt"

"google.golang.org/grpc"
"google.golang.org/protobuf/proto"

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

saipb "github.com/openconfig/lemming/dataplane/proto/sai"
)

type l2mcGroupMember struct {
oid uint64
groupId uint64
outputId uint64
}

type l2mcGroup struct {
saipb.UnimplementedL2McGroupServer
mgr *attrmgr.AttrMgr
dataplane switchDataplaneAPI
groups map[uint64]map[uint64]*l2mcGroupMember // OID -> map of Port
}

func newL2mcGroup(mgr *attrmgr.AttrMgr, dataplane switchDataplaneAPI, s *grpc.Server) *l2mcGroup {
Expand All @@ -39,25 +50,147 @@ func newL2mcGroup(mgr *attrmgr.AttrMgr, dataplane switchDataplaneAPI, s *grpc.Se
return mg
}

// TODO: Implement this.
func (mg *l2mcGroup) CreateL2McGroup(context.Context, *saipb.CreateL2McGroupRequest) (*saipb.CreateL2McGroupResponse, error) {
func (mg *l2mcGroup) CreateL2McGroup(ctx context.Context, req *saipb.CreateL2McGroupRequest) (*saipb.CreateL2McGroupResponse, error) {
id := mg.mgr.NextID()
// Update internal data
mg.groups[id] = map[uint64]*l2mcGroupMember{}
// Update attributes
l2mcAttrs := &saipb.L2McGroupAttribute{
L2McOutputCount: proto.Uint32(0),
L2McMemberList: []uint64{},
}
mg.mgr.StoreAttributes(id, l2mcAttrs)
return &saipb.CreateL2McGroupResponse{Oid: id}, nil
}

// TODO: Implement this.
func (mg *l2mcGroup) CreateL2McGroupMember(context.Context, *saipb.CreateL2McGroupMemberRequest) (*saipb.CreateL2McGroupMemberResponse, error) {
func (mg *l2mcGroup) CreateL2McGroupMember(ctx context.Context, req *saipb.CreateL2McGroupMemberRequest) (*saipb.CreateL2McGroupMemberResponse, error) {
id := mg.mgr.NextID()
// Update table entry
r := fwdconfig.TableEntryAddRequest(mg.dataplane.ID(), L2MCGroupTable).AppendEntry(
fwdconfig.EntryDesc(fwdconfig.ExactEntry(fwdconfig.PacketFieldBytes(fwdpb.PacketFieldNum_PACKET_FIELD_NUM_L2MC_GROUP_ID).WithUint64(req.GetL2McGroupId())))).Build()
for _, p := range mg.groups {
r.Entries[0].Actions = append(r.Entries[0].Actions, &fwdpb.ActionDesc{
ActionType: fwdpb.ActionType_ACTION_TYPE_MIRROR,
Action: &fwdpb.ActionDesc_Mirror{
Mirror: &fwdpb.MirrorActionDesc{
PortId: &fwdpb.PortId{
ObjectId: &fwdpb.ObjectId{
Id: fmt.Sprint(p),
},
},
},
},
})
}
if _, err := mg.dataplane.TableEntryAdd(ctx, r); err != nil {
return nil, err
}
// Update internal data
if mg.groups[req.GetL2McGroupId()] == nil {
return nil, fmt.Errorf("L2MC group id %q not found", req.GetL2McGroupId())
}
if _, ok := mg.groups[req.GetL2McGroupId()][req.GetL2McOutputId()]; ok {
return nil, fmt.Errorf("found existing member %q", req.GetL2McOutputId())
}
mg.groups[req.GetL2McGroupId()][req.GetL2McOutputId()] = &l2mcGroupMember{
oid: id,
outputId: req.GetL2McOutputId(),
groupId: req.GetL2McGroupId(),
}
// Update L2MC Group member attributes
attr := &saipb.L2McGroupMemberAttribute{
L2McGroupId: req.L2McGroupId,
L2McOutputId: req.L2McOutputId,
}
mg.mgr.StoreAttributes(id, attr)
// Update L2MC Group Attributes.
gReq := &saipb.GetL2McGroupAttributeRequest{Oid: req.GetL2McGroupId(), AttrType: []saipb.L2McGroupAttr{saipb.L2McGroupAttr_L2MC_GROUP_ATTR_L2MC_MEMBER_LIST, saipb.L2McGroupAttr_L2MC_GROUP_ATTR_L2MC_OUTPUT_COUNT}}
gResp := &saipb.GetL2McGroupAttributeResponse{}
if err := mg.mgr.PopulateAttributes(gReq, gResp); err != nil {
return nil, err
}
gAttrs := gResp.GetAttr()
gAttrs.L2McMemberList = append(gAttrs.GetL2McMemberList(), id)
*gAttrs.L2McOutputCount += 1
mg.mgr.StoreAttributes(req.GetL2McGroupId(), gAttrs)
// Update Switch Attributes.
swReq := &saipb.GetSwitchAttributeRequest{Oid: req.GetSwitch(), AttrType: []saipb.SwitchAttr{saipb.SwitchAttr_SWITCH_ATTR_AVAILABLE_L2MC_ENTRY}}
swResp := &saipb.GetSwitchAttributeResponse{}
if err := mg.mgr.PopulateAttributes(swReq, swResp); err != nil {
return nil, err
}
attrs := swResp.GetAttr()
*attrs.AvailableL2McEntry = attrs.GetAvailableL2McEntry() - 1
mg.mgr.StoreAttributes(req.GetSwitch(), attrs)
return &saipb.CreateL2McGroupMemberResponse{Oid: id}, nil
}

// TODO: Implement this.
func (mg *l2mcGroup) RemoveL2McGroup(context.Context, *saipb.RemoveL2McGroupRequest) (*saipb.RemoveL2McGroupResponse, error) {
func (mg *l2mcGroup) RemoveL2McGroup(ctx context.Context, req *saipb.RemoveL2McGroupRequest) (*saipb.RemoveL2McGroupResponse, error) {
if mg.groups[req.GetOid()] == nil {
return nil, fmt.Errorf("cannot find group %q", req.GetOid())
}
// Remove all members in the group
for _, p := range mg.groups[req.GetOid()] {
_, err := attrmgr.InvokeAndSave(ctx, mg.mgr, mg.RemoveL2McGroupMember, &saipb.RemoveL2McGroupMemberRequest{
Oid: p.oid,
})
if err != nil {
return nil, err
}
}
// Update internal data
delete(mg.groups, req.GetOid())
return &saipb.RemoveL2McGroupResponse{}, nil
}

// TODO: Implement this.
func (mg *l2mcGroup) RemoveL2McGroupMember(context.Context, *saipb.RemoveL2McGroupMemberRequest) (*saipb.RemoveL2McGroupMemberResponse, error) {
func (mg *l2mcGroup) RemoveL2McGroupMember(ctx context.Context, req *saipb.RemoveL2McGroupMemberRequest) (*saipb.RemoveL2McGroupMemberResponse, error) {
// Remove table entry.
r := fwdconfig.TableEntryRemoveRequest(mg.dataplane.ID(), L2MCGroupTable).AppendEntry(
fwdconfig.EntryDesc(fwdconfig.ExactEntry(fwdconfig.PacketFieldBytes(fwdpb.PacketFieldNum_PACKET_FIELD_NUM_L2MC_GROUP_ID).WithUint64(req.GetOid())))).Build()
if _, err := mg.dataplane.TableEntryRemove(ctx, r); err != nil {
return nil, err
}
// Update L2MC group attributes
locateMember := func(oid uint64) *l2mcGroupMember {
for _, members := range mg.groups {
for k, v := range members {
if k == oid {
return v
}
}
}
return nil
}
m := locateMember(req.GetOid())
if m == nil {
return nil, fmt.Errorf("cannot find member with OID %d", req.GetOid())
}
gReq := &saipb.GetL2McGroupAttributeRequest{Oid: m.groupId, AttrType: []saipb.L2McGroupAttr{saipb.L2McGroupAttr_L2MC_GROUP_ATTR_L2MC_MEMBER_LIST, saipb.L2McGroupAttr_L2MC_GROUP_ATTR_L2MC_OUTPUT_COUNT}}
gResp := &saipb.GetL2McGroupAttributeResponse{}
if err := mg.mgr.PopulateAttributes(gReq, gResp); err != nil {
return nil, err
}
gAttrs := gResp.GetAttr()
newMemList := []uint64{}
for _, i := range gAttrs.GetL2McMemberList() {
if i != req.GetOid() {
newMemList = append(newMemList, i)
}
}
gAttrs.L2McMemberList = newMemList
*gAttrs.L2McOutputCount -= 1
mg.mgr.StoreAttributes(m.groupId, gAttrs)
// Update Switch Attributes.
swReq := &saipb.GetSwitchAttributeRequest{Oid: 1, AttrType: []saipb.SwitchAttr{saipb.SwitchAttr_SWITCH_ATTR_AVAILABLE_L2MC_ENTRY}}
swResp := &saipb.GetSwitchAttributeResponse{}
if err := mg.mgr.PopulateAttributes(swReq, swResp); err != nil {
return nil, err
}
attrs := swResp.GetAttr()
*attrs.AvailableL2McEntry = attrs.GetAvailableL2McEntry() + 1
mg.mgr.StoreAttributes(1, attrs)
// Update internal data
delete(mg.groups[m.groupId], req.GetOid())
return &saipb.RemoveL2McGroupMemberResponse{}, nil
}

Expand Down
1 change: 1 addition & 0 deletions dataplane/saiserver/ports.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ func getL3Pipeline() []*fwdpb.ActionDesc {
func getL2Pipeline() []*fwdpb.ActionDesc {
return []*fwdpb.ActionDesc{
fwdconfig.Action(fwdconfig.LookupAction(IngressActionTable)).Build(), // Run ingress action.
fwdconfig.Action(fwdconfig.LookupAction(L2MCGroupTable)).Build(), // Check L2MC group.
fwdconfig.Action(fwdconfig.DropAction()).Build(), // DROP
}
}
Expand Down
21 changes: 21 additions & 0 deletions dataplane/saiserver/switch.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ const (
portToHostifTable = "cpu-output"
tunTermTable = "tun-term"
VlanTable = "vlan"
L2MCGroupTable = "l2mcg"
DefaultVlanId = 1
)

Expand Down Expand Up @@ -269,6 +270,26 @@ func (sw *saiSwitch) CreateSwitch(ctx context.Context, _ *saipb.CreateSwitchRequ
if _, err := sw.dataplane.TableCreate(ctx, vlanReq); err != nil {
return nil, err
}
l2mcGroupReq := &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: L2MCGroupTable}},
Actions: []*fwdpb.ActionDesc{{ActionType: fwdpb.ActionType_ACTION_TYPE_CONTINUE}},
Table: &fwdpb.TableDesc_Exact{
Exact: &fwdpb.ExactTableDesc{
FieldIds: []*fwdpb.PacketFieldId{{
Field: &fwdpb.PacketField{
FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_L2MC_GROUP_ID,
},
}},
},
},
},
}
if _, err := sw.dataplane.TableCreate(ctx, l2mcGroupReq); err != nil {
return nil, err
}
action := &fwdpb.TableCreateRequest{
ContextId: &fwdpb.ContextId{Id: sw.dataplane.ID()},
Desc: &fwdpb.TableDesc{
Expand Down
Loading

0 comments on commit 3c96642

Please sign in to comment.