Support group mods and group stats requests.

Added support for match IPv4 destination address.

Change-Id: I8693299f29ba3c45f61b936877d443218e5410c4
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
+}