Update odltools
[netvirt.git] / resources / tools / odltools / odltools / netvirt / flow_parser.py
index 8431f986747ceaee8c9bb578a3d40fe9fc7f5efa..4fa811ef69d76bb1fe9c871a5cf6b6b8cea7edcd 100644 (file)
@@ -1,5 +1,19 @@
+# Copyright 2018 Red Hat, Inc. and others. All Rights Reserved.
+#
+# 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.
+
 from odltools.mdsal.models.model import Model
-import utils
+from odltools.netvirt import utils
 
 
 MAC_LEN = 17
@@ -70,6 +84,8 @@ ELAN_HEX_LEN = 4
 LPORT_REG6_MASK = 0xfffff00
 LPORT_REG6_MASK_ZLEN = 2
 VRFID_MASK = 0x0000000000fffffe
+SERVICE_ID_MASK = 0xf000000000000000
+SERVICE_ID_MASK_ZLEN = 15
 
 
 def parse_flow(flow):
@@ -104,7 +120,6 @@ def get_instruction_writemeta(flow):
     for instruction in flow['instructions'].get('instruction', []):
         if 'write-metadata' in instruction:
             return instruction['write-metadata']
-    return None
 
 
 def get_act_reg6load(flow):
@@ -113,7 +128,6 @@ def get_act_reg6load(flow):
             for action in instruction['apply-actions'].get('action', []):
                 if 'openflowplugin-extension-nicira-action:nx-reg-load' in action:
                     return action['openflowplugin-extension-nicira-action:nx-reg-load']
-    return None
 
 
 def get_act_conntrack(flow):
@@ -165,6 +179,22 @@ def get_act_output(flow):
                     return action.get('output-action')
 
 
+def get_act_set_ipv4_dest(flow):
+    for instruction in flow['instructions'].get('instruction', []):
+        if 'apply-actions' in instruction:
+            for action in instruction['apply-actions'].get('action', []):
+                if 'set-field' in action and 'ipv4-destination' in action.get('set-field'):
+                    return action.get('set-field').get('ipv4-destination')
+
+
+def get_act_set_ipv4_src(flow):
+    for instruction in flow['instructions'].get('instruction', []):
+        if 'apply-actions' in instruction:
+            for action in instruction['apply-actions'].get('action', []):
+                if 'set-field' in action and 'ipv4-source' in action.get('set-field'):
+                    return action.get('set-field').get('ipv4-source')
+
+
 def get_match_metadata(flow):
     return flow['match'].get('metadata')
 
@@ -178,73 +208,121 @@ def get_match_reg6(flow):
             return (
                 ofex['extension']
                 ['openflowplugin-extension-nicira-match:nxm-nx-reg'])
-    return None
 
 
 def get_match_mpls(flow):
     if flow['match'].get('protocol-match-fields'):
         return flow['match'].get('protocol-match-fields').get('mpls-label')
-    return None
 
 
 def get_match_tunnelid(flow):
     if flow['match'].get('tunnel'):
         return flow['match'].get('tunnel').get('tunnel-id')
-    return None
 
 
 def get_match_ether_dest(flow):
     if flow.get('match').get('ethernet-match') and flow['match'].get('ethernet-match').get('ethernet-destination'):
-        return flow['match'].get('ethernet-match').get('ethernet-destination')
-    return None
+        return flow['match'].get('ethernet-match').get('ethernet-destination').get('address')
 
 
 def get_match_ether_src(flow):
     if flow.get('match').get('ethernet-match') and flow['match'].get('ethernet-match').get('ethernet-source'):
-        return flow['match'].get('ethernet-match').get('ethernet-source')
-    return None
+        return flow['match'].get('ethernet-match').get('ethernet-source').get('address')
+
+
+def get_match_ipv4_dest(flow):
+    return utils.parse_ipv4(flow['match'].get('ipv4-destination'))
+
+
+def get_match_ipv4_src(flow):
+    return utils.parse_ipv4(flow['match'].get('ipv4-source'))
 
 
 def get_match_vlanid(flow):
     if flow.get('match').get('vlan-match') and flow['match'].get('vlan-match').get('vlan-id'):
         return flow['match'].get('vlan-match').get('vlan-id')
-    return None
 
 
 def get_match_inport(flow):
     if flow.get('match').get('in-port'):
         return flow['match'].get('in-port')
-    return None
+
+
+def get_lport_from_mreg6(m_reg6):
+    if m_reg6 and m_reg6.get('value'):
+        return (('%x' % (m_reg6.get('value') & LPORT_REG6_MASK))[:-LPORT_REG6_MASK_ZLEN])
+
+
+def get_lport_from_metadata(metadata, mask):
+    if mask & LPORT_MASK:
+        return ('%x' % (metadata & LPORT_MASK))[:-LPORT_MASK_ZLEN]
+
+
+def get_elan_from_metadata(metadata, mask):
+    if mask & ELAN_TAG_MASK:
+        return ('%x' % (metadata & ELAN_TAG_MASK))[:ELAN_HEX_LEN]
+
+
+def get_service_id_from_metadata(metadata, mask):
+    if mask & SERVICE_ID_MASK:
+        return ('%x' % (metadata & SERVICE_ID_MASK))[:-SERVICE_ID_MASK_ZLEN]
+
+
+def get_vpnid_from_metadata(metadata, mask):
+    if mask & VRFID_MASK:
+        return (metadata & VRFID_MASK) / 2
 
 
 def get_flow_info_from_any(flow_info, flow):
     w_mdata = get_instruction_writemeta(flow)
-    lport = None
+    lport = flow_info.get('lport') if flow_info else None
+    serviceid = flow_info.get('serviceid') if flow_info else None
     if w_mdata:
         metadata = w_mdata['metadata']
         mask = w_mdata['metadata-mask']
-        if mask & LPORT_MASK:
-            lport = ('%x' % (metadata & LPORT_MASK))[:-LPORT_MASK_ZLEN]
-            if lport:
-                flow_info['lport'] = int(lport, 16)
+        lport = get_lport_from_metadata(metadata, mask) if not lport else lport
+        if lport:
+            flow_info['lport'] = int(lport, 16)
+        serviceid = get_service_id_from_metadata(metadata, mask) if not serviceid else serviceid
+        if serviceid:
+            flow_info['serviceid'] = int(serviceid, 16)
     m_metadata = get_match_metadata(flow)
     if m_metadata:
+        elan = flow_info.get('elan-tag')
+        vpnid = flow_info.get('vpnid')
         metadata = m_metadata['metadata']
         mask = m_metadata['metadata-mask']
-        if mask & ELAN_TAG_MASK:
-            elan = ('%x' % (metadata & ELAN_TAG_MASK))[:ELAN_HEX_LEN]
-            if elan:
-                flow_info['elan-tag'] = int(elan, 16)
-        if not lport and (mask & LPORT_MASK):
-            lport = ('%x' % (metadata & LPORT_MASK))[:-LPORT_MASK_ZLEN]
+        if not lport:
+            lport = get_lport_from_metadata(metadata, mask)
             if lport:
                 flow_info['lport'] = int(lport, 16)
-    m_ether_dest = get_match_ether_dest(flow)
-    if m_ether_dest and m_ether_dest.get('address'):
-        flow_info['dst-mac'] = m_ether_dest.get('address').lower()
-    m_ether_src = get_match_ether_src(flow)
-    if m_ether_src and m_ether_src.get('address'):
-        flow_info['src-mac'] = m_ether_src.get('address').lower()
+        if not serviceid:
+            serviceid = get_service_id_from_metadata(metadata, mask)
+            flow_info['serviceid'] = int(serviceid, 16)
+        if not elan:
+            elan = get_elan_from_metadata(metadata, mask)
+            if elan:
+                flow_info['elan-tag'] = int(elan, 16)
+        if not vpnid:
+            vpnid = get_vpnid_from_metadata(metadata, mask)
+            if vpnid:
+                flow_info['vpnid'] = vpnid
+    if not flow_info.get('dst-mac'):
+        m_ether_dest = get_match_ether_dest(flow)
+        if m_ether_dest:
+            flow_info['dst-mac'] = m_ether_dest.lower()
+    if not flow_info.get('src-mac'):
+        m_ether_src = get_match_ether_src(flow)
+        if m_ether_src:
+            flow_info['src-mac'] = m_ether_src.lower()
+    if not flow_info.get('dst-ip4'):
+        m_ipv4_dest = get_match_ipv4_dest(flow)
+        if m_ipv4_dest:
+            flow_info['dst-ip4'] = m_ipv4_dest
+    if not flow_info.get('src-ip4'):
+        m_ipv4_src = get_match_ipv4_src(flow)
+        if m_ipv4_src:
+            flow_info['src-ip4'] = m_ipv4_src
     return flow_info
 
 # Table specific parsing
@@ -270,14 +348,17 @@ def get_flow_info_from_ifm_table(flow_info, flow):
     if w_mdata:
         metadata = w_mdata['metadata']
         mask = w_mdata['metadata-mask']
-        if mask & LPORT_MASK:
-            lport = ('%x' % (metadata & LPORT_MASK))[:-LPORT_MASK_ZLEN]
+        lport = get_lport_from_metadata(metadata, mask)
+        if lport:
             flow_info['lport'] = int(lport, 16)
+        service_id = get_service_id_from_metadata(metadata, mask)
+        if service_id:
+            flow_info['serviceid'] = int(service_id, 16)
     m_reg6 = get_match_reg6(flow)
-    if not flow.get('lport') and m_reg6 and m_reg6.get('value'):
-        lport = (('%x' % (m_reg6.get('value') & LPORT_REG6_MASK))
-                 [:-LPORT_REG6_MASK_ZLEN])
-        flow_info['lport'] = int(lport, 16)
+    if not flow.get('lport'):
+        lport = get_lport_from_mreg6(m_reg6)
+        if lport:
+            flow_info['lport'] = int(lport, 16)
     if flow['table_id'] == 0:
         m_inport = get_match_inport(flow)
         if m_inport:
@@ -308,10 +389,11 @@ def get_flow_info_from_l3vpn_table(flow_info, flow):
     if m_metadata:
         metadata = m_metadata['metadata']
         mask = m_metadata['metadata-mask']
-        if mask & LPORT_MASK:
-            lport = ('%x' % (metadata & LPORT_MASK))[:-LPORT_MASK_ZLEN]
+        lport = get_lport_from_metadata(metadata, mask)
+        if lport:
             flow_info['lport'] = int(lport, 16)
-        if mask & VRFID_MASK:
+        vpnid = get_vpnid_from_metadata(metadata, mask)
+        if vpnid:
             flow_info['vpnid'] = (metadata & VRFID_MASK) / 2
     return flow_info
 
@@ -328,18 +410,19 @@ def get_flow_info_from_elan_table(flow_info, flow):
     if m_metadata:
         metadata = m_metadata['metadata']
         mask = m_metadata['metadata-mask']
-        if mask & ELAN_TAG_MASK:
-            elan = ('%x' % (metadata & ELAN_TAG_MASK))[:ELAN_HEX_LEN]
+        elan = get_elan_from_metadata(metadata, mask)
+        if elan:
+            flow_info['lport'] = int(elan, 16)
             flow_info['elan-tag'] = int(elan, 16)
-        if mask & LPORT_MASK:
-            lport = ('%x' % (metadata & LPORT_MASK))[:-LPORT_MASK_ZLEN]
+        lport = get_lport_from_metadata(metadata, mask)
+        if lport:
             flow_info['lport'] = int(lport, 16)
     m_ether_dest = get_match_ether_dest(flow)
-    if m_ether_dest and m_ether_dest.get('address'):
-        flow_info['dst-mac'] = m_ether_dest.get('address').lower()
+    if m_ether_dest:
+        flow_info['dst-mac'] = m_ether_dest.lower()
     m_ether_src = get_match_ether_src(flow)
-    if m_ether_src and m_ether_src.get('address'):
-        flow_info['src-mac'] = m_ether_src.get('address').lower()
+    if m_ether_src:
+        flow_info['src-mac'] = m_ether_src.lower()
     if not flow_info.get('lport'):
         reg6_load = get_act_reg6load(flow)
         if reg6_load and reg6_load.get('value'):
@@ -354,8 +437,8 @@ def get_flow_info_from_acl_table(flow_info, flow):
     if m_metadata:
         metadata = m_metadata['metadata']
         mask = m_metadata['metadata-mask']
-        if mask & LPORT_MASK:
-            lport = ('%x' % (metadata & LPORT_MASK))[:-LPORT_MASK_ZLEN]
+        lport = get_lport_from_metadata(metadata, mask)
+        if lport:
             flow_info['lport'] = int(lport, 16)
     a_conntrk = get_act_conntrack(flow)
     if a_conntrk and a_conntrk.get('conntrack-zone'):
@@ -363,6 +446,49 @@ def get_flow_info_from_acl_table(flow_info, flow):
     return flow_info
 
 
+def get_flow_info_from_nat_table(flow_info, flow):
+    m_metadata = get_match_metadata(flow)
+    vpnid = None
+    if m_metadata:
+        metadata = m_metadata['metadata']
+        mask = m_metadata['metadata-mask']
+        lport = get_lport_from_metadata(metadata, mask)
+        if lport:
+            flow_info['lport'] = int(lport, 16)
+        vpnid = get_vpnid_from_metadata(metadata, mask)
+        if vpnid:
+            flow_info['vpnid'] = vpnid
+    if not vpnid:
+        w_metadata = get_instruction_writemeta(flow)
+        metadata = w_metadata['metadata']
+        mask = w_metadata['metadata-mask']
+        vpnid = get_vpnid_from_metadata(metadata, mask)
+        if vpnid:
+            flow_info['vpnid'] = vpnid
+    m_ipv4_dest = get_match_ipv4_dest(flow)
+    m_ipv4_src = get_match_ipv4_src(flow)
+    a_set_ipv4_dest = get_act_set_ipv4_dest(flow)
+    a_set_ipv4_src = get_act_set_ipv4_src(flow)
+    m_ether_src = get_match_ether_src(flow)
+    if flow['table_id'] in [25, 27]:
+        if a_set_ipv4_dest:
+            flow_info['int-ip4'] = a_set_ipv4_dest
+        if m_ipv4_dest:
+            flow_info['ext-ip4'] = m_ipv4_dest
+        m_ether_dest = get_match_ether_dest(flow)
+        if m_ether_dest:
+            flow_info['ext-mac'] = m_ether_dest
+    if flow['table_id'] in [26, 28]:
+        if a_set_ipv4_src:
+            flow_info['ext-ip4'] = a_set_ipv4_src
+        if m_ipv4_src:
+            flow_info['int-ip4'] = m_ipv4_src
+        m_ether_src = get_match_ether_src(flow)
+        if m_ether_src:
+            flow_info['ext-mac'] = m_ether_src
+    return flow_info
+
+
 def get_flow_info_from_acl_table_flowid(flow_info, flow):
     """
         Format for ACL flow ids is as follows: