Skip to content

Commit

Permalink
Hash API (#395)
Browse files Browse the repository at this point in the history
* Hash API

* fix
  • Loading branch information
DanG100 authored Apr 4, 2024
1 parent c946d8c commit b678d6b
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 40 deletions.
110 changes: 94 additions & 16 deletions dataplane/saiserver/routing.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,15 @@ type nextHopGroup struct {
mgr *attrmgr.AttrMgr
dataplane switchDataplaneAPI
groups map[uint64]map[uint64]*groupMember // groups is map of next hop groups to a map of next hops
groupIsV4 map[uint64]bool // map from group id to IP protocol version
}

func newNextHopGroup(mgr *attrmgr.AttrMgr, dataplane switchDataplaneAPI, s *grpc.Server) *nextHopGroup {
n := &nextHopGroup{
mgr: mgr,
dataplane: dataplane,
groups: map[uint64]map[uint64]*groupMember{},
groupIsV4: map[uint64]bool{},
}
saipb.RegisterNextHopGroupServer(s, n)
return n
Expand All @@ -132,12 +134,23 @@ func (nhg *nextHopGroup) CreateNextHopGroup(_ context.Context, req *saipb.Create

// updateNextHopGroupMember updates the next hop group.
// If m is nil, remove mid from the group(key: nhgid), otherwise add m to group with mid as the key.
func updateNextHopGroupMember(ctx context.Context, nhg *nextHopGroup, nhgid, mid uint64, m *groupMember) error {
func (nhg *nextHopGroup) updateNextHopGroupMember(ctx context.Context, nhgid, mid uint64, m *groupMember) error {
group := nhg.groups[nhgid]
if group == nil {
return status.Errorf(codes.FailedPrecondition, "group %d does not exist", nhgid)
}
if m != nil {
if _, ok := nhg.groupIsV4[nhgid]; !ok { // Use the first member added to group to determine if the group is ipv4.
nhAttr := &saipb.GetNextHopAttributeResponse{}
err := nhg.mgr.PopulateAttributes(&saipb.GetNextHopAttributeRequest{
Oid: m.nextHop,
AttrType: []saipb.NextHopAttr{saipb.NextHopAttr_NEXT_HOP_ATTR_IP},
}, nhAttr)
if err != nil {
return fmt.Errorf("failed to retrieve next hop attr: %v", err)
}
nhg.groupIsV4[nhgid] = len(nhAttr.GetAttr().GetIp()) == 4
}
group[mid] = m
} else {
delete(group, mid)
Expand All @@ -150,23 +163,40 @@ func updateNextHopGroupMember(ctx context.Context, nhg *nextHopGroup, nhgid, mid
Actions: []*fwdpb.ActionDesc{action.Build()},
})
}

swAttr := &saipb.GetSwitchAttributeResponse{}
err := nhg.mgr.PopulateAttributes(&saipb.GetSwitchAttributeRequest{
Oid: switchID,
AttrType: []saipb.SwitchAttr{saipb.SwitchAttr_SWITCH_ATTR_ECMP_HASH_IPV4, saipb.SwitchAttr_SWITCH_ATTR_ECMP_HASH_IPV6},
}, swAttr)
if err != nil {
return fmt.Errorf("failed to retrieve hash id: %v", err)
}
hashID := swAttr.GetAttr().GetEcmpHashIpv6()
if nhg.groupIsV4[nhgid] {
hashID = swAttr.GetAttr().GetEcmpHashIpv4()
}
hashAttr := &saipb.GetHashAttributeResponse{}
err = nhg.mgr.PopulateAttributes(&saipb.GetHashAttributeRequest{
Oid: hashID,
AttrType: []saipb.HashAttr{saipb.HashAttr_HASH_ATTR_NATIVE_HASH_FIELD_LIST},
}, hashAttr)
if err != nil {
return fmt.Errorf("failed to retrieve hash field: %v", err)
}

fieldsID, err := convertHashFields(hashAttr.GetAttr().GetNativeHashFieldList())
if err != nil {
return fmt.Errorf("failed to compute hash fields: %v", err)
}

actions := []*fwdpb.ActionDesc{{
ActionType: fwdpb.ActionType_ACTION_TYPE_SELECT_ACTION_LIST,
Action: &fwdpb.ActionDesc_Select{
Select: &fwdpb.SelectActionListActionDesc{
SelectAlgorithm: fwdpb.SelectActionListActionDesc_SELECT_ALGORITHM_CRC32, // TODO: should algo + hash be configurable?
FieldIds: []*fwdpb.PacketFieldId{{Field: &fwdpb.PacketField{ // Hash the traffic flow, identified, IP protocol, L3 SRC, DST address, and L4 ports (if present).
FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_IP_PROTO,
}}, {Field: &fwdpb.PacketField{
FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_IP_ADDR_SRC,
}}, {Field: &fwdpb.PacketField{
FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_IP_ADDR_DST,
}}, {Field: &fwdpb.PacketField{
FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_L4_PORT_SRC,
}}, {Field: &fwdpb.PacketField{
FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_L4_PORT_DST,
}}},
ActionLists: actLists,
FieldIds: fieldsID,
ActionLists: actLists,
},
},
}, {
Expand Down Expand Up @@ -205,7 +235,7 @@ func updateNextHopGroupMember(ctx context.Context, nhg *nextHopGroup, nhgid, mid
Actions: actions,
}},
}
_, err := nhg.dataplane.TableEntryAdd(ctx, entries)
_, err = nhg.dataplane.TableEntryAdd(ctx, entries)
return err
}

Expand Down Expand Up @@ -239,7 +269,7 @@ func (nhg *nextHopGroup) CreateNextHopGroupMember(ctx context.Context, req *saip
nextHop: req.GetNextHopId(),
weight: req.GetWeight(),
}
if err := updateNextHopGroupMember(ctx, nhg, nhgid, mid, m); err != nil {
if err := nhg.updateNextHopGroupMember(ctx, nhgid, mid, m); err != nil {
return nil, err
}
return &saipb.CreateNextHopGroupMemberResponse{Oid: mid}, nil
Expand All @@ -263,7 +293,7 @@ func (nhg *nextHopGroup) RemoveNextHopGroupMember(ctx context.Context, req *saip
return nil, err
}

if err := updateNextHopGroupMember(ctx, nhg, nhgid, mid, nil); err != nil {
if err := nhg.updateNextHopGroupMember(ctx, nhgid, mid, nil); err != nil {
return nil, err
}
return &saipb.RemoveNextHopGroupMemberResponse{}, nil
Expand Down Expand Up @@ -678,3 +708,51 @@ func (br *bridge) CreateBridge(context.Context, *saipb.CreateBridgeRequest) (*sa
Oid: id,
}, nil
}

type hash struct {
saipb.UnimplementedHashServer
mgr *attrmgr.AttrMgr
dataplane switchDataplaneAPI
}

func newHash(mgr *attrmgr.AttrMgr, dataplane switchDataplaneAPI, s *grpc.Server) *hash {
m := &hash{
mgr: mgr,
dataplane: dataplane,
}
saipb.RegisterHashServer(s, m)
return m
}

func convertHashFields(list []saipb.NativeHashField) ([]*fwdpb.PacketFieldId, error) {
fields := []*fwdpb.PacketFieldId{}
for _, field := range list {
switch field {
case saipb.NativeHashField_NATIVE_HASH_FIELD_SRC_IP, saipb.NativeHashField_NATIVE_HASH_FIELD_SRC_IPV4, saipb.NativeHashField_NATIVE_HASH_FIELD_SRC_IPV6:
fields = append(fields, &fwdpb.PacketFieldId{Field: &fwdpb.PacketField{FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_IP_ADDR_SRC}})
case saipb.NativeHashField_NATIVE_HASH_FIELD_DST_IP, saipb.NativeHashField_NATIVE_HASH_FIELD_DST_IPV4, saipb.NativeHashField_NATIVE_HASH_FIELD_DST_IPV6:
fields = append(fields, &fwdpb.PacketFieldId{Field: &fwdpb.PacketField{FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_IP_ADDR_DST}})
case saipb.NativeHashField_NATIVE_HASH_FIELD_L4_SRC_PORT:
fields = append(fields, &fwdpb.PacketFieldId{Field: &fwdpb.PacketField{FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_L4_PORT_SRC}})
case saipb.NativeHashField_NATIVE_HASH_FIELD_L4_DST_PORT:
fields = append(fields, &fwdpb.PacketFieldId{Field: &fwdpb.PacketField{FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_L4_PORT_DST}})
case saipb.NativeHashField_NATIVE_HASH_FIELD_IPV6_FLOW_LABEL:
fields = append(fields, &fwdpb.PacketFieldId{Field: &fwdpb.PacketField{FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_IP6_FLOW}})
default:
return nil, fmt.Errorf("unsupported hash field: %v", field)
}
}
return fields, nil
}

func (h *hash) CreateHash(_ context.Context, req *saipb.CreateHashRequest) (*saipb.CreateHashResponse, error) {
id := h.mgr.NextID()

// Creating a hash doesn't affect the forwarding pipeline, just validate the arguments.
_, err := convertHashFields(req.GetNativeHashFieldList())
if err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}

return &saipb.CreateHashResponse{Oid: id}, nil
}
42 changes: 24 additions & 18 deletions dataplane/saiserver/routing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,8 @@ func TestCreateNextHopGroupMember(t *testing.T) {
}{{
desc: "success",
req: &saipb.CreateNextHopGroupMemberRequest{
NextHopGroupId: proto.Uint64(1),
NextHopId: proto.Uint64(2),
NextHopGroupId: proto.Uint64(2),
NextHopId: proto.Uint64(3),
Weight: proto.Uint32(3),
},
wantReq: &fwdpb.TableEntryAddRequest{
Expand All @@ -182,7 +182,7 @@ func TestCreateNextHopGroupMember(t *testing.T) {
FieldId: &fwdpb.PacketFieldId{Field: &fwdpb.PacketField{
FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_NEXT_HOP_GROUP_ID,
}},
Bytes: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
Bytes: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
}},
},
},
Expand All @@ -193,11 +193,7 @@ func TestCreateNextHopGroupMember(t *testing.T) {
Select: &fwdpb.SelectActionListActionDesc{
SelectAlgorithm: fwdpb.SelectActionListActionDesc_SELECT_ALGORITHM_CRC32,
FieldIds: []*fwdpb.PacketFieldId{
{Field: &fwdpb.PacketField{FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_IP_PROTO}},
{Field: &fwdpb.PacketField{FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_IP_ADDR_SRC}},
{Field: &fwdpb.PacketField{FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_IP_ADDR_DST}},
{Field: &fwdpb.PacketField{FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_L4_PORT_SRC}},
{Field: &fwdpb.PacketField{FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_L4_PORT_DST}},
},
ActionLists: []*fwdpb.ActionList{{
Weight: 3,
Expand All @@ -208,7 +204,7 @@ func TestCreateNextHopGroupMember(t *testing.T) {
FieldId: &fwdpb.PacketFieldId{Field: &fwdpb.PacketField{FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_NEXT_HOP_ID}},
Type: fwdpb.UpdateType_UPDATE_TYPE_SET,
Field: &fwdpb.PacketFieldId{Field: &fwdpb.PacketField{}},
Value: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
Value: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
},
},
}},
Expand All @@ -226,15 +222,22 @@ func TestCreateNextHopGroupMember(t *testing.T) {
}},
},
wantAttr: &saipb.NextHopGroupMemberAttribute{
NextHopGroupId: proto.Uint64(1),
NextHopId: proto.Uint64(2),
NextHopGroupId: proto.Uint64(2),
NextHopId: proto.Uint64(3),
Weight: proto.Uint32(3),
},
}}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
dplane := &fakeSwitchDataplane{}
c, mgr, stopFn := newTestNextHopGroup(t, dplane)

mgr.StoreAttributes(mgr.NextID(), &saipb.SwitchAttribute{EcmpHashIpv4: proto.Uint64(10), EcmpHashIpv6: proto.Uint64(10)})
mgr.StoreAttributes(3, &saipb.CreateNextHopRequest{Ip: []byte{127, 0, 0, 1}})
mgr.StoreAttributes(10, &saipb.CreateHashRequest{
NativeHashFieldList: []saipb.NativeHashField{saipb.NativeHashField_NATIVE_HASH_FIELD_DST_IP},
})

_, err := c.CreateNextHopGroup(context.Background(), &saipb.CreateNextHopGroupRequest{Type: saipb.NextHopGroupType_NEXT_HOP_GROUP_TYPE_DYNAMIC_UNORDERED_ECMP.Enum()})
if err != nil {
t.Fatal(err)
Expand All @@ -251,7 +254,7 @@ func TestCreateNextHopGroupMember(t *testing.T) {
t.Errorf("CreateNextHopGroupMember() failed: diff(-got,+want)\n:%s", d)
}
attr := &saipb.NextHopGroupMemberAttribute{}
if err := mgr.PopulateAllAttributes("2", attr); err != nil {
if err := mgr.PopulateAllAttributes("3", attr); err != nil {
t.Fatal(err)
}
if d := cmp.Diff(attr, tt.wantAttr, protocmp.Transform()); d != "" {
Expand Down Expand Up @@ -282,7 +285,7 @@ func TestRemoveNextHopGroupMember(t *testing.T) {
FieldId: &fwdpb.PacketFieldId{Field: &fwdpb.PacketField{
FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_NEXT_HOP_GROUP_ID,
}},
Bytes: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
Bytes: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
}},
},
},
Expand All @@ -293,11 +296,7 @@ func TestRemoveNextHopGroupMember(t *testing.T) {
Select: &fwdpb.SelectActionListActionDesc{
SelectAlgorithm: fwdpb.SelectActionListActionDesc_SELECT_ALGORITHM_CRC32,
FieldIds: []*fwdpb.PacketFieldId{
{Field: &fwdpb.PacketField{FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_IP_PROTO}},
{Field: &fwdpb.PacketField{FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_IP_ADDR_SRC}},
{Field: &fwdpb.PacketField{FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_IP_ADDR_DST}},
{Field: &fwdpb.PacketField{FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_L4_PORT_SRC}},
{Field: &fwdpb.PacketField{FieldNum: fwdpb.PacketFieldNum_PACKET_FIELD_NUM_L4_PORT_DST}},
},
ActionLists: []*fwdpb.ActionList{{
Weight: 66,
Expand Down Expand Up @@ -326,7 +325,7 @@ func TestRemoveNextHopGroupMember(t *testing.T) {
}},
},
wantAttr: &saipb.NextHopGroupMemberAttribute{
NextHopGroupId: proto.Uint64(1),
NextHopGroupId: proto.Uint64(2),
NextHopId: proto.Uint64(12),
Weight: proto.Uint32(66),
},
Expand All @@ -347,6 +346,13 @@ func TestRemoveNextHopGroupMember(t *testing.T) {
t.Run(tt.desc, func(t *testing.T) {
dplane := &fakeSwitchDataplane{}
c, mgr, stopFn := newTestNextHopGroup(t, dplane)
mgr.StoreAttributes(mgr.NextID(), &saipb.SwitchAttribute{EcmpHashIpv4: proto.Uint64(10), EcmpHashIpv6: proto.Uint64(10)})
mgr.StoreAttributes(10, &saipb.CreateNextHopRequest{Ip: []byte{127, 0, 0, 1}})
mgr.StoreAttributes(11, &saipb.CreateNextHopRequest{Ip: []byte{127, 0, 0, 2}})
mgr.StoreAttributes(10, &saipb.CreateHashRequest{
NativeHashFieldList: []saipb.NativeHashField{saipb.NativeHashField_NATIVE_HASH_FIELD_DST_IP},
})

ctx := context.Background()
r, err := c.CreateNextHopGroup(ctx, &saipb.CreateNextHopGroupRequest{Type: saipb.NextHopGroupType_NEXT_HOP_GROUP_TYPE_DYNAMIC_UNORDERED_ECMP.Enum()})
if err != nil {
Expand Down Expand Up @@ -380,7 +386,7 @@ func TestRemoveNextHopGroupMember(t *testing.T) {
t.Errorf("RemoveNextHopGroupMember() failed: diff(-got,+want)\n:%s", d)
}
attr := &saipb.NextHopGroupMemberAttribute{}
if err := mgr.PopulateAllAttributes("3", attr); err != nil {
if err := mgr.PopulateAllAttributes("4", attr); err != nil {
t.Fatal(err)
}
if d := cmp.Diff(attr, tt.wantAttr, protocmp.Transform()); d != "" {
Expand Down
4 changes: 0 additions & 4 deletions dataplane/saiserver/saiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,6 @@ type fdb struct {
saipb.UnimplementedFdbServer
}

type hash struct {
saipb.UnimplementedHashServer
}

type ipmcGroup struct {
saipb.UnimplementedIpmcGroupServer
}
Expand Down
3 changes: 1 addition & 2 deletions dataplane/saiserver/switch.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func newSwitch(mgr *attrmgr.AttrMgr, engine switchDataplaneAPI, s *grpc.Server,
vr: &virtualRouter{},
bridge: newBridge(mgr, engine, s),
hostif: newHostif(mgr, engine, s, opts),
hash: &hash{},
hash: newHash(mgr, engine, s),
myMac: newMyMac(mgr, engine, s),
neighbor: newNeighbor(mgr, engine, s),
nextHopGroup: newNextHopGroup(mgr, engine, s),
Expand All @@ -126,7 +126,6 @@ func newSwitch(mgr *attrmgr.AttrMgr, engine switchDataplaneAPI, s *grpc.Server,
saipb.RegisterSwitchServer(s, sw)
saipb.RegisterStpServer(s, sw.stp)
saipb.RegisterVirtualRouterServer(s, sw.vr)
saipb.RegisterHashServer(s, sw.hash)
return sw, nil
}

Expand Down
23 changes: 23 additions & 0 deletions dataplane/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,29 @@ func (d *Dataplane) Start(ctx context.Context, c gpb.GNMIClient, target string)
return err
}

// Allow all traffic to L3 processing.
hash := saipb.NewHashClient(conn)
hashResp, err := hash.CreateHash(context.Background(), &saipb.CreateHashRequest{
Switch: swResp.Oid,
NativeHashFieldList: []saipb.NativeHashField{
saipb.NativeHashField_NATIVE_HASH_FIELD_SRC_IP,
saipb.NativeHashField_NATIVE_HASH_FIELD_DST_IP,
saipb.NativeHashField_NATIVE_HASH_FIELD_L4_SRC_PORT,
saipb.NativeHashField_NATIVE_HASH_FIELD_L4_DST_PORT,
},
})
if err != nil {
return err
}
_, err = sw.SetSwitchAttribute(ctx, &saipb.SetSwitchAttributeRequest{
Oid: swResp.Oid,
EcmpHashIpv4: proto.Uint64(hashResp.Oid),
EcmpHashIpv6: proto.Uint64(hashResp.Oid),
})
if err != nil {
return err
}

// Allow all traffic to L3 processing.
mmc := saipb.NewMyMacClient(conn)
_, err = mmc.CreateMyMac(context.Background(), &saipb.CreateMyMacRequest{
Expand Down

0 comments on commit b678d6b

Please sign in to comment.