Support group mods and group stats requests.
Added support for match IPv4 destination address.
Change-Id: I8693299f29ba3c45f61b936877d443218e5410c4
diff --git a/internal/pkg/openflow/connection.go b/internal/pkg/openflow/connection.go
index 691644c..af388b2 100644
--- a/internal/pkg/openflow/connection.go
+++ b/internal/pkg/openflow/connection.go
@@ -407,7 +407,7 @@
ofc.sendRoleSlaveError(header)
return
}
- // TODO handle group mods
+ ofc.handleGroupMod(header.(ofp.IGroupMod))
}
}
diff --git a/internal/pkg/openflow/flowMod.go b/internal/pkg/openflow/flowMod.go
index 5424c12..08c0b98 100644
--- a/internal/pkg/openflow/flowMod.go
+++ b/internal/pkg/openflow/flowMod.go
@@ -26,6 +26,7 @@
"github.com/opencord/voltha-lib-go/v3/pkg/log"
"github.com/opencord/voltha-protos/v3/go/openflow_13"
"github.com/opencord/voltha-protos/v3/go/voltha"
+ "net"
)
var oxmMap = map[string]int32{
@@ -119,6 +120,10 @@
field.Value = &voltha.OfpOxmOfbField_IpProto{
IpProto: uint32(val.(ofp.IpPrototype)),
}
+ case voltha.OxmOfbFieldTypes_OFPXMT_OFB_IPV4_DST:
+ field.Value = &voltha.OfpOxmOfbField_Ipv4Dst{
+ Ipv4Dst: binary.BigEndian.Uint32(val.(net.IP)),
+ }
case voltha.OxmOfbFieldTypes_OFPXMT_OFB_UDP_SRC:
field.Value = &voltha.OfpOxmOfbField_UdpSrc{
UdpSrc: uint32(val.(uint16)),
@@ -355,6 +360,10 @@
field.Value = &voltha.OfpOxmOfbField_IpProto{
IpProto: uint32(val.(ofp.IpPrototype)),
}
+ case voltha.OxmOfbFieldTypes_OFPXMT_OFB_IPV4_DST:
+ field.Value = &voltha.OfpOxmOfbField_Ipv4Dst{
+ Ipv4Dst: binary.BigEndian.Uint32(val.(net.IP)),
+ }
case voltha.OxmOfbFieldTypes_OFPXMT_OFB_UDP_SRC:
field.Value = &voltha.OfpOxmOfbField_UdpSrc{
UdpSrc: uint32(val.(uint16)),
diff --git a/internal/pkg/openflow/group.go b/internal/pkg/openflow/group.go
new file mode 100644
index 0000000..a1e09c7
--- /dev/null
+++ b/internal/pkg/openflow/group.go
@@ -0,0 +1,156 @@
+/*
+ Copyright 2020 the original author or authors.
+
+ 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
+
+ http://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 openflow
+
+import (
+ "context"
+ "github.com/donNewtonAlpha/goloxi"
+ ofp "github.com/donNewtonAlpha/goloxi/of13"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ "github.com/opencord/voltha-protos/v3/go/openflow_13"
+ "github.com/opencord/voltha-protos/v3/go/voltha"
+)
+
+func (ofc *OFConnection) handleGroupMod(groupMod ofp.IGroupMod) {
+
+ volthaClient := ofc.VolthaClient.Get()
+ if volthaClient == nil {
+ logger.Errorw("no-voltha-connection",
+ log.Fields{"device-id": ofc.DeviceID})
+ return
+ }
+
+ groupUpdate := &openflow_13.FlowGroupTableUpdate{
+ Id: ofc.DeviceID,
+ GroupMod: &voltha.OfpGroupMod{
+ Command: openflowGroupModCommandToVoltha(groupMod.GetCommand()),
+ Type: openflowGroupTypeToVoltha(groupMod.GetGroupType()),
+ GroupId: groupMod.GetGroupId(),
+ Buckets: openflowBucketsToVoltha(groupMod.GetBuckets()),
+ },
+ }
+
+ _, err := volthaClient.UpdateLogicalDeviceFlowGroupTable(context.Background(), groupUpdate)
+ if err != nil {
+ logger.Errorw("Error updating group table",
+ log.Fields{"device-id": ofc.DeviceID, "error": err})
+ }
+
+}
+
+func openflowGroupModCommandToVoltha(command ofp.GroupModCommand) openflow_13.OfpGroupModCommand {
+ switch command {
+ case ofp.OFPGCAdd:
+ return openflow_13.OfpGroupModCommand_OFPGC_ADD
+ case ofp.OFPGCModify:
+ return openflow_13.OfpGroupModCommand_OFPGC_MODIFY
+ case ofp.OFPGCDelete:
+ return openflow_13.OfpGroupModCommand_OFPGC_DELETE
+ }
+ logger.Errorw("Unknown group mod command", log.Fields{"command": command})
+ return 0
+}
+
+func openflowGroupTypeToVoltha(t ofp.GroupType) openflow_13.OfpGroupType {
+ switch t {
+ case ofp.OFPGTAll:
+ return openflow_13.OfpGroupType_OFPGT_ALL
+ case ofp.OFPGTSelect:
+ return openflow_13.OfpGroupType_OFPGT_SELECT
+ case ofp.OFPGTIndirect:
+ return openflow_13.OfpGroupType_OFPGT_INDIRECT
+ case ofp.OFPGTFf:
+ return openflow_13.OfpGroupType_OFPGT_FF
+ }
+ logger.Errorw("Unknown openflow group type", log.Fields{"type": t})
+ return 0
+}
+
+func volthaGroupTypeToOpenflow(t openflow_13.OfpGroupType) ofp.GroupType {
+ switch t {
+ case openflow_13.OfpGroupType_OFPGT_ALL:
+ return ofp.OFPGTAll
+ case openflow_13.OfpGroupType_OFPGT_SELECT:
+ return ofp.OFPGTSelect
+ case openflow_13.OfpGroupType_OFPGT_INDIRECT:
+ return ofp.OFPGTIndirect
+ case openflow_13.OfpGroupType_OFPGT_FF:
+ return ofp.OFPGTFf
+ }
+ logger.Errorw("Unknown voltha group type", log.Fields{"type": t})
+ return 0
+}
+
+func openflowBucketsToVoltha(buckets []*ofp.Bucket) []*openflow_13.OfpBucket {
+ outBuckets := make([]*openflow_13.OfpBucket, len(buckets))
+
+ for i, bucket := range buckets {
+ b := &openflow_13.OfpBucket{
+ Weight: uint32(bucket.Weight),
+ WatchPort: uint32(bucket.WatchPort),
+ WatchGroup: bucket.WatchGroup,
+ Actions: openflowActionsToVoltha(bucket.Actions),
+ }
+ outBuckets[i] = b
+ }
+
+ return outBuckets
+}
+
+func openflowActionsToVoltha(actions []goloxi.IAction) []*openflow_13.OfpAction {
+ outActions := make([]*openflow_13.OfpAction, len(actions))
+
+ for i, action := range actions {
+ outActions[i] = extractAction(action)
+ }
+
+ return outActions
+}
+
+func volthaBucketsToOpenflow(buckets []*openflow_13.OfpBucket) []*ofp.Bucket {
+ outBuckets := make([]*ofp.Bucket, len(buckets))
+
+ for i, bucket := range buckets {
+ actions, length := volthaActionsToOpenflow(bucket.Actions)
+ b := &ofp.Bucket{
+ // TODO loxi should set lengths automatically
+ // last 4 is padding
+ Len: 2 + 2 + 4 + 4 + length + 4, // length field + weight + watchPort + watchGroup + actions
+ Weight: uint16(bucket.Weight),
+ WatchPort: ofp.Port(bucket.WatchPort),
+ WatchGroup: bucket.WatchGroup,
+ Actions: actions,
+ }
+ outBuckets[i] = b
+ }
+
+ return outBuckets
+}
+
+func volthaActionsToOpenflow(actions []*openflow_13.OfpAction) ([]goloxi.IAction, uint16) {
+ outActions := make([]goloxi.IAction, len(actions))
+
+ var length uint16
+
+ for i, action := range actions {
+ a, l := parseAction(action)
+ length += l
+ outActions[i] = a
+ }
+
+ return outActions, length
+}
diff --git a/internal/pkg/openflow/parseGrpcReturn.go b/internal/pkg/openflow/parseGrpcReturn.go
index a5bd1d1..beff0be 100644
--- a/internal/pkg/openflow/parseGrpcReturn.go
+++ b/internal/pkg/openflow/parseGrpcReturn.go
@@ -16,6 +16,8 @@
package openflow
import (
+ "bytes"
+ "encoding/binary"
"encoding/json"
"github.com/donNewtonAlpha/goloxi"
ofp "github.com/donNewtonAlpha/goloxi/of13"
@@ -24,13 +26,11 @@
"github.com/opencord/voltha-protos/v3/go/voltha"
)
-func parseOxm(ofbField *openflow_13.OfpOxmOfbField, DeviceID string) (goloxi.IOxm, uint16) {
+func parseOxm(ofbField *openflow_13.OfpOxmOfbField) (goloxi.IOxm, uint16) {
if logger.V(log.DebugLevel) {
js, _ := json.Marshal(ofbField)
logger.Debugw("parseOxm called",
- log.Fields{
- "device-id": DeviceID,
- "ofbField": js})
+ log.Fields{"ofbField": js})
}
switch ofbField.Type {
@@ -54,6 +54,17 @@
val := ofbField.GetValue().(*openflow_13.OfpOxmOfbField_IpProto)
ofpIpProto.Value = ofp.IpPrototype(val.IpProto)
return ofpIpProto, 1
+ case voltha.OxmOfbFieldTypes_OFPXMT_OFB_IPV4_DST:
+ ofpIpv4Dst := ofp.NewOxmIpv4Dst()
+ val := ofbField.GetValue().(*openflow_13.OfpOxmOfbField_Ipv4Dst)
+ buf := new(bytes.Buffer)
+ err := binary.Write(buf, binary.BigEndian, val.Ipv4Dst)
+ if err != nil {
+ logger.Errorw("error writing ipv4 address %v",
+ log.Fields{"error": err})
+ }
+ ofpIpv4Dst.Value = buf.Bytes()
+ return ofpIpv4Dst, 4
case voltha.OxmOfbFieldTypes_OFPXMT_OFB_UDP_SRC:
ofpUdpSrc := ofp.NewOxmUdpSrc()
val := ofbField.GetValue().(*openflow_13.OfpOxmOfbField_UdpSrc)
@@ -97,21 +108,17 @@
if logger.V(log.WarnLevel) {
js, _ := json.Marshal(ofbField)
logger.Warnw("ParseOXM Unhandled OxmField",
- log.Fields{
- "device-id": DeviceID,
- "OfbField": js})
+ log.Fields{"OfbField": js})
}
}
return nil, 0
}
-func parseInstructions(ofpInstruction *openflow_13.OfpInstruction, DeviceID string) (ofp.IInstruction, uint16) {
+func parseInstructions(ofpInstruction *openflow_13.OfpInstruction) (ofp.IInstruction, uint16) {
if logger.V(log.DebugLevel) {
js, _ := json.Marshal(ofpInstruction)
logger.Debugw("parseInstructions called",
- log.Fields{
- "device-id": DeviceID,
- "Instruction": js})
+ log.Fields{"Instruction": js})
}
instType := ofpInstruction.Type
data := ofpInstruction.GetData()
@@ -141,7 +148,7 @@
//ofpActions := ofpInstruction.GetActions().Actions
var actions []goloxi.IAction
for _, ofpAction := range ofpInstruction.GetActions().Actions {
- action, actionSize := parseAction(ofpAction, DeviceID)
+ action, actionSize := parseAction(ofpAction)
actions = append(actions, action)
instructionSize += actionSize
@@ -152,7 +159,6 @@
js, _ := json.Marshal(instruction)
logger.Debugw("parseInstructions returning",
log.Fields{
- "device-id": DeviceID,
"size": instructionSize,
"parsed-instruction": js})
}
@@ -162,13 +168,11 @@
return nil, 0
}
-func parseAction(ofpAction *openflow_13.OfpAction, DeviceID string) (goloxi.IAction, uint16) {
+func parseAction(ofpAction *openflow_13.OfpAction) (goloxi.IAction, uint16) {
if logger.V(log.DebugLevel) {
js, _ := json.Marshal(ofpAction)
logger.Debugw("parseAction called",
- log.Fields{
- "device-id": DeviceID,
- "action": js})
+ log.Fields{"action": js})
}
switch ofpAction.Type {
case openflow_13.OfpActionType_OFPAT_OUTPUT:
@@ -191,17 +195,21 @@
ofpActionSetField := ofpAction.GetSetField()
setFieldAction := ofp.NewActionSetField()
- iOxm, _ := parseOxm(ofpActionSetField.GetField().GetOfbField(), DeviceID)
+ iOxm, _ := parseOxm(ofpActionSetField.GetField().GetOfbField())
setFieldAction.Field = iOxm
setFieldAction.Len = 16
return setFieldAction, 16
+ case openflow_13.OfpActionType_OFPAT_GROUP:
+ ofpGroupAction := ofpAction.GetGroup()
+ groupAction := ofp.NewActionGroup()
+ groupAction.GroupId = ofpGroupAction.GroupId
+ groupAction.Len = 8
+ return groupAction, 8
default:
if logger.V(log.WarnLevel) {
js, _ := json.Marshal(ofpAction)
logger.Warnw("parseAction unknow action",
- log.Fields{
- "device-id": DeviceID,
- "action": js})
+ log.Fields{"action": js})
}
}
return nil, 0
diff --git a/internal/pkg/openflow/stats.go b/internal/pkg/openflow/stats.go
index 246bc80..9b642ce 100644
--- a/internal/pkg/openflow/stats.go
+++ b/internal/pkg/openflow/stats.go
@@ -350,7 +350,7 @@
for _, oxmField := range pbMatch.GetOxmFields() {
field := oxmField.GetField()
ofbField := field.(*openflow_13.OfpOxmField_OfbField).OfbField
- iOxm, oxmSize := parseOxm(ofbField, ofc.DeviceID)
+ iOxm, oxmSize := parseOxm(ofbField)
fields = append(fields, iOxm)
if oxmSize > 0 {
size += 4 //header for oxm
@@ -368,7 +368,7 @@
entry.SetMatch(*match)
var instructions []ofp.IInstruction
for _, ofpInstruction := range item.Instructions {
- instruction, size := parseInstructions(ofpInstruction, ofc.DeviceID)
+ instruction, size := parseInstructions(ofpInstruction)
instructions = append(instructions, instruction)
entrySize += size
}
@@ -416,13 +416,20 @@
entry.SetRefCount(stats.GetRefCount())
entry.SetGroupId(stats.GetGroupId())
var bucketStatsList []*ofp.BucketCounter
- for _, bucketStat := range stats.GetBucketStats() {
+ // TODO fix this when API handler is fixed in the core
+ // Core doesn't return any buckets in the Stats object, so just
+ // fill out an empty BucketCounter for each bucket in the group Desc for now.
+ for range item.GetDesc().GetBuckets() {
bucketCounter := ofp.BucketCounter{}
- bucketCounter.SetPacketCount(bucketStat.GetPacketCount())
- bucketCounter.SetByteCount(bucketStat.GetByteCount())
+ bucketCounter.SetPacketCount(0)
+ bucketCounter.SetByteCount(0)
bucketStatsList = append(bucketStatsList, &bucketCounter)
}
entry.SetBucketStats(bucketStatsList)
+ // TODO loxi should set lengths automatically
+ // last 2 + 4 are padding
+ length := 2 + 4 + 4 + 8 + 8 + 4 + 4 + len(bucketStatsList)*16 + 2 + 4
+ entry.SetLength(uint16(length))
groupStatsEntries = append(groupStatsEntries, &entry)
}
response.SetEntries(groupStatsEntries)
@@ -445,19 +452,23 @@
}
var groupDescStatsEntries []*ofp.GroupDescStatsEntry
for _, item := range reply.GetItems() {
- stats := item.GetStats()
- var groupDesc ofp.GroupDescStatsEntry
- groupDesc.SetGroupId(stats.GetGroupId())
- /*
- buckets := item.g
- var bucketList []*ofp.Bucket
- for j:=0;j<len(buckets);j++{
+ desc := item.GetDesc()
- }
+ buckets := volthaBucketsToOpenflow(desc.Buckets)
+ var bucketsLength uint16
+ for _, b := range buckets {
+ bucketsLength += b.Len
+ }
- groupDesc.SetBuckets(bucketList)
- */
- groupDescStatsEntries = append(groupDescStatsEntries, &groupDesc)
+ groupDesc := &ofp.GroupDescStatsEntry{
+ // TODO loxi should set lengths automatically
+ // last 1 is padding
+ Length: 2 + 1 + 4 + bucketsLength + 1, // length field + groupType + groupId + buckets
+ GroupType: volthaGroupTypeToOpenflow(desc.Type),
+ GroupId: desc.GroupId,
+ Buckets: buckets,
+ }
+ groupDescStatsEntries = append(groupDescStatsEntries, groupDesc)
}
response.SetEntries(groupDescStatsEntries)
return response, nil
diff --git a/internal/pkg/openflow/utils.go b/internal/pkg/openflow/utils.go
index 7a29e66..dac2f22 100644
--- a/internal/pkg/openflow/utils.go
+++ b/internal/pkg/openflow/utils.go
@@ -77,6 +77,13 @@
case ofp.OFPATPopMpls: //PopMpls
case ofp.OFPATSetQueue: //SetQueue
case ofp.OFPATGroup: //ActionGroup
+ ofpAction.Type = openflow_13.OfpActionType_OFPAT_GROUP
+ group := action.(*ofp.ActionGroup)
+ ofpAction.Action = &openflow_13.OfpAction_Group{
+ Group: &openflow_13.OfpActionGroup{
+ GroupId: group.GroupId,
+ },
+ }
case ofp.OFPATSetNwTtl: //SetNwTtl
case ofp.OFPATDecNwTtl: //DecNwTtl
case ofp.OFPATSetField: //SetField