[VOL-4005] Add support for matches on MPLS fields

Change-Id: If74dbb747b22cb0ea66e65f4b256d76f6894695c
diff --git a/internal/pkg/openflow/flowMod.go b/internal/pkg/openflow/flowMod.go
index c0cbde1..4f8ab63 100644
--- a/internal/pkg/openflow/flowMod.go
+++ b/internal/pkg/openflow/flowMod.go
@@ -129,6 +129,10 @@
 			field.Value = &voltha.OfpOxmOfbField_EthDst{
 				EthDst: val.(net.HardwareAddr),
 			}
+		case voltha.OxmOfbFieldTypes_OFPXMT_OFB_ETH_SRC:
+			field.Value = &voltha.OfpOxmOfbField_EthSrc{
+				EthSrc: val.(net.HardwareAddr),
+			}
 		case voltha.OxmOfbFieldTypes_OFPXMT_OFB_UDP_SRC:
 			field.Value = &voltha.OfpOxmOfbField_UdpSrc{
 				UdpSrc: uint32(val.(uint16)),
@@ -145,6 +149,18 @@
 			field.Value = &voltha.OfpOxmOfbField_VlanPcp{
 				VlanPcp: uint32(val.(uint8)),
 			}
+		case voltha.OxmOfbFieldTypes_OFPXMT_OFB_MPLS_LABEL:
+			field.Value = &voltha.OfpOxmOfbField_MplsLabel{
+				MplsLabel: val.(uint32),
+			}
+		case voltha.OxmOfbFieldTypes_OFPXMT_OFB_MPLS_BOS:
+			field.Value = &voltha.OfpOxmOfbField_MplsBos{
+				MplsBos: uint32(val.(uint8)),
+			}
+		case voltha.OxmOfbFieldTypes_OFPXMT_OFB_MPLS_TC:
+			field.Value = &voltha.OfpOxmOfbField_MplsTc{
+				MplsTc: val.(uint32),
+			}
 		case 200: // voltha-protos doesn't actually have a type for vlan_mask
 			field = voltha.OfpOxmOfbField{Type: voltha.OxmOfbFieldTypes(oxmMap["vlan_vid"])}
 			field.HasMask = true
diff --git a/internal/pkg/openflow/flowMod_test.go b/internal/pkg/openflow/flowMod_test.go
new file mode 100644
index 0000000..8465cb9
--- /dev/null
+++ b/internal/pkg/openflow/flowMod_test.go
@@ -0,0 +1,436 @@
+/*
+   Copyright 2021 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"
+	"encoding/json"
+	"fmt"
+	"github.com/opencord/ofagent-go/internal/pkg/holder"
+	"github.com/opencord/ofagent-go/internal/pkg/mock"
+	"github.com/stretchr/testify/assert"
+	"net"
+	"testing"
+
+	"github.com/opencord/goloxi"
+	"github.com/opencord/goloxi/of13"
+	ofp "github.com/opencord/goloxi/of13"
+)
+
+func TestOFConnection_handleFlowAdd(t *testing.T) {
+	ofc := &OFConnection{
+		VolthaClient: &holder.VolthaServiceClientHolder{},
+	}
+	ofc.VolthaClient.Set(mock.MockVolthaClient{})
+
+	callables := []func(t *testing.T) *ofp.FlowAdd{getUpstreamOnuFlow, getUpstreamOLTFlow, getOnuDownstreamFlow,
+		getOLTDownstreamSingleMplsTagFlow, getOLTDownstreamDoubleMplsTagFlow}
+	for _, callable := range callables {
+		ofc.handleFlowAdd(context.Background(), callable(t))
+	}
+}
+
+func getUpstreamOnuFlow(t *testing.T) *ofp.FlowAdd {
+	flowAdd := &ofp.FlowAdd{
+		FlowMod: &ofp.FlowMod{
+			Header: &ofp.Header{
+				Version: 0,
+				Type:    ofp.OFPFCAdd,
+				Length:  0,
+				Xid:     10,
+			},
+			Cookie:   114560316889735549,
+			TableId:  0,
+			Priority: 1000,
+			Match: ofp.Match{
+				OxmList: []goloxi.IOxm{&of13.NxmInPort{
+					Oxm: &of13.Oxm{
+						TypeLen: 0,
+					},
+					Value: 16,
+				},
+					&of13.OxmVlanVid{
+						Oxm: &of13.Oxm{
+							TypeLen: 0,
+						},
+						Value: 4096,
+					}},
+			},
+			Instructions: []ofp.IInstruction{
+				&ofp.InstructionGotoTable{
+					Instruction: &ofp.Instruction{
+						Type: ofp.OFPITGotoTable,
+						Len:  8,
+					},
+					TableId: 1,
+				},
+				&ofp.InstructionMeter{
+					Instruction: &ofp.Instruction{
+						Type: ofp.OFPITMeter,
+					},
+					MeterId: 1,
+				},
+			},
+		},
+	}
+	flowAddJson, err := json.Marshal(flowAdd)
+	assert.NoError(t, err)
+
+	fmt.Printf("Onu Flow Add Json: %s\n", flowAddJson)
+	return flowAdd
+}
+
+func getUpstreamOLTFlow(t *testing.T) *ofp.FlowAdd {
+	flowAdd := &ofp.FlowAdd{
+		FlowMod: &ofp.FlowMod{
+			Header: &ofp.Header{
+				Version: 0,
+				Type:    ofp.OFPFCAdd,
+				Length:  0,
+				Xid:     10,
+			},
+			Cookie:   114560316889735549,
+			TableId:  1,
+			Priority: 1000,
+			Match: ofp.Match{
+				OxmList: []goloxi.IOxm{
+					&of13.OxmInPort{
+						Value: ofp.Port(16),
+					},
+					&of13.OxmVlanVid{
+						Oxm: &of13.Oxm{
+							TypeLen: 0,
+						},
+						Value: 4096,
+					}},
+			},
+			Instructions: []ofp.IInstruction{
+				&ofp.InstructionMeter{
+					Instruction: &ofp.Instruction{
+						Type: ofp.OFPITMeter,
+					},
+					MeterId: 1,
+				},
+			},
+		},
+	}
+
+	actions := &ofp.InstructionApplyActions{
+		Instruction: &ofp.Instruction{
+			Type: ofp.OFPITApplyActions,
+		},
+	}
+	actions.Actions = append(actions.Actions, &ofp.ActionPushVlan{
+		Action: &ofp.Action{
+			Type: ofp.OFPATPushVLAN,
+		},
+		Ethertype: 0x8100,
+	},
+		&ofp.ActionPushMpls{
+			Action: &ofp.Action{
+				Type: ofp.OFPATPushMpls,
+			},
+			Ethertype: 0x8847,
+		},
+		&ofp.ActionSetField{
+			Action: &ofp.Action{
+				Type: ofp.OFPATSetField,
+			},
+			Field: &ofp.OxmMplsLabel{
+				Value: 10,
+			},
+		},
+		&ofp.ActionSetField{
+			Action: &ofp.Action{
+				Type: ofp.OFPATSetField,
+			},
+			Field: &ofp.OxmMplsBos{
+				Value: 1,
+			},
+		},
+		&ofp.ActionSetField{
+			Action: &ofp.Action{
+				Type: ofp.OFPATSetField,
+			},
+			Field: &ofp.OxmEthSrc{
+				Value: net.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd, 0xee},
+			},
+		},
+		&ofp.ActionSetField{
+			Action: &ofp.Action{
+				Type: ofp.OFPATSetField,
+			},
+			Field: &ofp.OxmEthDst{
+				Value: net.HardwareAddr{0xee, 0xdd, 0xcc, 0xbb, 0xaa},
+			},
+		},
+		&ofp.ActionSetMplsTtl{
+			Action: &ofp.Action{
+				Type: ofp.OFPATSetMplsTtl,
+			},
+			MplsTtl: 64,
+		},
+	)
+
+	flowAdd.Instructions = append(flowAdd.Instructions, actions)
+	flowAddJson, err := json.Marshal(flowAdd)
+	assert.NoError(t, err)
+
+	fmt.Printf("OLT Flow Add Json: %s\n", flowAddJson)
+	return flowAdd
+}
+
+func getOnuDownstreamFlow(t *testing.T) *ofp.FlowAdd {
+	flowAdd := &ofp.FlowAdd{
+		FlowMod: &ofp.FlowMod{
+			Header: &ofp.Header{
+				Version: 3,
+				Type:    0,
+				Xid:     0,
+			},
+			Cookie:   114560316889735549,
+			TableId:  2,
+			Priority: 1000,
+			Flags:    0,
+			Match: ofp.Match{
+				Type:   0,
+				Length: 0,
+				OxmList: []goloxi.IOxm{
+					&of13.OxmInPort{
+						Value: ofp.Port(65536),
+					},
+					&of13.OxmVlanVid{
+						Value: 4096,
+					}},
+			},
+			Instructions: []ofp.IInstruction{
+				&ofp.InstructionMeter{
+					Instruction: &ofp.Instruction{
+						Type: ofp.OFPITMeter,
+					},
+					MeterId: 1,
+				},
+			},
+		},
+	}
+
+	actions := &ofp.InstructionApplyActions{
+		Instruction: &ofp.Instruction{
+			Type: ofp.OFPITApplyActions,
+		},
+	}
+	actions.Actions = append(actions.Actions,
+		&ofp.ActionOutput{
+			Action: &ofp.Action{
+				Type: ofp.OFPATOutput,
+			},
+			Port: 16,
+		})
+
+	flowAdd.Instructions = append(flowAdd.Instructions, actions)
+	flowAddJson, err := json.Marshal(flowAdd)
+	assert.NoError(t, err)
+
+	fmt.Printf("Onu Downstream Flow Add Json: %s\n", flowAddJson)
+	return flowAdd
+
+}
+
+func getOLTDownstreamSingleMplsTagFlow(t *testing.T) *ofp.FlowAdd {
+	flowAdd := &ofp.FlowAdd{
+		FlowMod: &ofp.FlowMod{
+			Header: &ofp.Header{
+				Version: 3,
+				Type:    0,
+				Xid:     0,
+			},
+			Cookie:   114560316889735549,
+			TableId:  0,
+			Priority: 1000,
+			Flags:    0,
+			Match: ofp.Match{
+				OxmList: []goloxi.IOxm{
+					&of13.OxmInPort{
+						Value: ofp.Port(65536),
+					},
+					&of13.OxmEthType{
+						Value: ofp.EthernetType(0x8847),
+					},
+					&of13.OxmMplsBos{
+						Value: 1,
+					},
+					&of13.OxmVlanVid{
+						Oxm: &of13.Oxm{
+							TypeLen: 0,
+						},
+						Value: 4096,
+					},
+					&of13.OxmEthSrc{
+						Value: net.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd},
+					},
+				},
+			},
+			Instructions: []ofp.IInstruction{
+				&ofp.InstructionGotoTable{
+					Instruction: &ofp.Instruction{
+						Type: ofp.OFPITGotoTable,
+					},
+					TableId: 1,
+				},
+				&ofp.InstructionMeter{
+					Instruction: &ofp.Instruction{
+						Type: ofp.OFPITMeter,
+					},
+					MeterId: 1,
+				},
+			},
+		},
+	}
+
+	actions := &ofp.InstructionApplyActions{
+		Instruction: &ofp.Instruction{
+			Type: ofp.OFPITApplyActions,
+		},
+	}
+	actions.Actions = append(actions.Actions,
+		&ofp.ActionOutput{
+			Action: &ofp.Action{
+				Type: ofp.OFPATOutput,
+			},
+			Port: 16,
+		},
+		&ofp.ActionDecMplsTtl{
+			Action: &ofp.Action{
+				Type: ofp.OFPATDecMplsTtl,
+			},
+		},
+		&ofp.ActionSetMplsTtl{
+			Action: &ofp.Action{
+				Type: ofp.OFPATSetMplsTtl,
+			},
+			MplsTtl: 64,
+		},
+		&ofp.ActionPopMpls{
+			Action: &ofp.Action{
+				Type: ofp.OFPATPopMpls,
+			},
+			Ethertype: 0x8847,
+		},
+	)
+
+	flowAdd.Instructions = append(flowAdd.Instructions, actions)
+	flowAddJson, err := json.Marshal(flowAdd)
+	assert.NoError(t, err)
+
+	fmt.Printf("Olt Downstream (Single MPLS tag) Flow Add Json: %s\n", flowAddJson)
+	return flowAdd
+}
+
+func getOLTDownstreamDoubleMplsTagFlow(t *testing.T) *ofp.FlowAdd {
+	flowAdd := &ofp.FlowAdd{
+		FlowMod: &ofp.FlowMod{
+			Header: &ofp.Header{
+				Version: 3,
+				Type:    0,
+				Xid:     0,
+			},
+			Cookie:   114560316889735549,
+			TableId:  0,
+			Priority: 1000,
+			Flags:    0,
+			Match: ofp.Match{
+				OxmList: []goloxi.IOxm{
+					&of13.OxmInPort{
+						Value: ofp.Port(65536),
+					},
+					&of13.OxmEthType{
+						Oxm: &of13.Oxm{
+							TypeLen: 0,
+						},
+						Value: ofp.EthernetType(0x8847),
+					},
+					&of13.OxmMplsBos{
+						Value: 1,
+					},
+					&of13.OxmVlanVid{
+						Value: 4096,
+					},
+					&of13.OxmEthSrc{
+						Value: net.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd},
+					},
+				},
+			},
+			Instructions: []ofp.IInstruction{
+				&ofp.InstructionGotoTable{
+					Instruction: &ofp.Instruction{
+						Type: ofp.OFPITGotoTable,
+					},
+					TableId: 1,
+				},
+				&ofp.InstructionMeter{
+					Instruction: &ofp.Instruction{
+						Type: ofp.OFPITMeter,
+					},
+					MeterId: 1,
+				},
+			},
+		},
+	}
+
+	actions := &ofp.InstructionApplyActions{
+		Instruction: &ofp.Instruction{
+			Type: ofp.OFPITApplyActions,
+		},
+	}
+	actions.Actions = append(actions.Actions,
+		&ofp.ActionOutput{
+			Action: &ofp.Action{
+				Type: ofp.OFPATOutput,
+			},
+			Port: 16,
+		},
+		&ofp.ActionDecMplsTtl{
+			Action: &ofp.Action{
+				Type: ofp.OFPATDecMplsTtl,
+			},
+		},
+		&ofp.ActionSetMplsTtl{
+			Action: &ofp.Action{
+				Type: ofp.OFPATSetMplsTtl,
+			},
+			MplsTtl: 64,
+		},
+		&ofp.ActionPopMpls{
+			Action: &ofp.Action{
+				Type: ofp.OFPATPopMpls,
+			},
+			Ethertype: 0x8847,
+		},
+		&ofp.ActionPopMpls{
+			Action: &ofp.Action{
+				Type: ofp.OFPATPopMpls,
+			},
+			Ethertype: 0x8847,
+		},
+	)
+
+	flowAdd.Instructions = append(flowAdd.Instructions, actions)
+	flowAddJson, err := json.Marshal(flowAdd)
+	assert.NoError(t, err)
+	fmt.Printf("Olt Downstream (Double MPLS tag) Flow Add Json: %s\n", flowAddJson)
+	return flowAdd
+}
diff --git a/internal/pkg/openflow/utils.go b/internal/pkg/openflow/utils.go
index 08de458..36642fa 100644
--- a/internal/pkg/openflow/utils.go
+++ b/internal/pkg/openflow/utils.go
@@ -20,6 +20,7 @@
 	"fmt"
 	ofp "github.com/opencord/goloxi/of13"
 	"github.com/opencord/voltha-protos/v4/go/openflow_13"
+	"net"
 	"strings"
 	"sync"
 )
@@ -62,6 +63,14 @@
 	case ofp.OFPATCopyTtlOut: //CopyTtltOut
 	case ofp.OFPATCopyTtlIn: //CopyTtlIn
 	case ofp.OFPATSetMplsTtl: //SetMplsTtl
+		mplsTtl := action.(*ofp.ActionSetMplsTtl)
+		setMplsTtl := openflow_13.OfpAction_MplsTtl{
+			MplsTtl: &openflow_13.OfpActionMplsTtl{
+				MplsTtl: uint32(mplsTtl.MplsTtl),
+			},
+		}
+		ofpAction.Type = openflow_13.OfpActionType_OFPAT_SET_MPLS_TTL
+		ofpAction.Action = &setMplsTtl
 	case ofp.OFPATDecMplsTtl: //DecMplsTtl
 	case ofp.OFPATPushVLAN: //PushVlan
 		var pushVlan openflow_13.OfpAction_Push
@@ -74,7 +83,15 @@
 	case ofp.OFPATPopVLAN: //PopVlan
 		ofpAction.Type = openflow_13.OfpActionType_OFPAT_POP_VLAN
 	case ofp.OFPATPushMpls: //PushMpls
+		var pushMpls openflow_13.OfpAction_Push
+		mplsPushAction := action.(*ofp.ActionPushMpls)
+		var push openflow_13.OfpActionPush
+		push.Ethertype = uint32(mplsPushAction.Ethertype)
+		pushMpls.Push = &push
+		ofpAction.Type = openflow_13.OfpActionType_OFPAT_PUSH_MPLS
+		ofpAction.Action = &pushMpls
 	case ofp.OFPATPopMpls: //PopMpls
+		ofpAction.Type = openflow_13.OfpActionType_OFPAT_POP_MPLS
 	case ofp.OFPATSetQueue: //SetQueue
 	case ofp.OFPATGroup: //ActionGroup
 		ofpAction.Type = openflow_13.OfpActionType_OFPAT_GROUP
@@ -110,6 +127,30 @@
 			var VlanPcp = loxiSetField.Field.GetOXMValue().(uint8)
 			vlanPcp.VlanPcp = uint32(VlanPcp)
 			ofpOxmOfbField.Value = &vlanPcp
+		case "mpls_label":
+			ofpOxmOfbField.Type = openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_MPLS_LABEL
+			var mplsLabel openflow_13.OfpOxmOfbField_MplsLabel
+			label := loxiSetField.Field.GetOXMValue().(uint32)
+			mplsLabel.MplsLabel = label
+			ofpOxmOfbField.Value = &mplsLabel
+		case "mpls_bos":
+			ofpOxmOfbField.Type = openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_MPLS_BOS
+			var mplsBos openflow_13.OfpOxmOfbField_MplsBos
+			bos := loxiSetField.Field.GetOXMValue().(uint8)
+			mplsBos.MplsBos = uint32(bos)
+			ofpOxmOfbField.Value = &mplsBos
+		case "eth_src":
+			ofpOxmOfbField.Type = openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_ETH_SRC
+			var ethSrc openflow_13.OfpOxmOfbField_EthSrc
+			src := loxiSetField.Field.GetOXMValue().(net.HardwareAddr)
+			ethSrc.EthSrc = src
+			ofpOxmOfbField.Value = &ethSrc
+		case "eth_dst":
+			ofpOxmOfbField.Type = openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_ETH_DST
+			var ethDst openflow_13.OfpOxmOfbField_EthDst
+			dst := loxiSetField.Field.GetOXMValue().(net.HardwareAddr)
+			ethDst.EthDst = dst
+			ofpOxmOfbField.Value = &ethDst
 		}
 		ofpOxmField_OfbField.OfbField = &ofpOxmOfbField
 		ofpOxmField.Field = &ofpOxmField_OfbField