diff --git a/neutron/agent/ovn/metadata/agent.py b/neutron/agent/ovn/metadata/agent.py
index d5679e7d565..70d14f4f334 100644
--- a/neutron/agent/ovn/metadata/agent.py
+++ b/neutron/agent/ovn/metadata/agent.py
@@ -43,7 +43,7 @@ CHASSIS_METADATA_LOCK = 'chassis_metadata_lock'
 
 NS_PREFIX = 'ovnmeta-'
 MAC_PATTERN = re.compile(r'([0-9A-F]{2}[:-]){5}([0-9A-F]{2})', re.I)
-OVN_VIF_PORT_TYPES = ("", "external", )
+OVN_VIF_PORT_TYPES = ("", "external", ovn_const.LSP_TYPE_LOCALPORT, )
 
 MetadataPortInfo = collections.namedtuple('MetadataPortInfo', ['mac',
                                                                'ip_addresses'])
@@ -82,6 +82,20 @@ class PortBindingChassisEvent(row_event.RowEvent):
         resync = False
         if row.type not in OVN_VIF_PORT_TYPES:
             return
+        if row.type == ovn_const.LSP_TYPE_LOCALPORT:
+            new_ext_ids = row.external_ids
+            old_ext_ids = old.external_ids
+            device_id = row.external_ids.get(
+                ovn_const.OVN_DEVID_EXT_ID_KEY, "")
+            if not device_id.startswith(NS_PREFIX):
+                return
+            new_cidrs = new_ext_ids.get(ovn_const.OVN_CIDRS_EXT_ID_KEY, "")
+            old_cidrs = old_ext_ids.get(ovn_const.OVN_CIDRS_EXT_ID_KEY, "")
+            # If old_cidrs is "", it is create event,
+            # nothing needs to be done.
+            # If old_cidrs equals new_cidrs, the ip does not change.
+            if old_cidrs in ("", new_cidrs, ):
+                return
         with _SYNC_STATE_LOCK.read_lock():
             try:
                 net_name = ovn_utils.get_network_name_from_datapath(
@@ -96,6 +110,24 @@ class PortBindingChassisEvent(row_event.RowEvent):
             self.agent.resync()
 
 
+class PortBindingMetaPortUpdatedEvent(PortBindingChassisEvent):
+    LOG_MSG = "Metadata Port %s in datapath %s updated."
+
+    def __init__(self, metadata_agent):
+        events = (self.ROW_UPDATE,)
+        super(PortBindingMetaPortUpdatedEvent, self).__init__(
+            metadata_agent, events)
+
+    def match_fn(self, event, row, old):
+        if row.type == ovn_const.LSP_TYPE_LOCALPORT:
+            if hasattr(row, 'external_ids') and hasattr(old, 'external_ids'):
+                device_id = row.external_ids.get(
+                    ovn_const.OVN_DEVID_EXT_ID_KEY, "")
+                if device_id.startswith(NS_PREFIX):
+                    return True
+        return False
+
+
 class PortBindingChassisCreatedEvent(PortBindingChassisEvent):
     LOG_MSG = "Port %s in datapath %s bound to our chassis"
 
@@ -245,7 +277,8 @@ class MetadataAgent(object):
                   'Chassis')
         events = (PortBindingChassisCreatedEvent(self),
                   PortBindingChassisDeletedEvent(self),
-                  SbGlobalUpdateEvent(self))
+                  SbGlobalUpdateEvent(self),
+                  PortBindingMetaPortUpdatedEvent(self))
 
         # TODO(lucasagomes): Remove this in the future. Try to register
         # the Chassis_Private table, if not present, fallback to the normal
diff --git a/neutron/tests/functional/agent/ovn/metadata/test_metadata_agent.py b/neutron/tests/functional/agent/ovn/metadata/test_metadata_agent.py
index 80ecaa7b040..abf485db872 100644
--- a/neutron/tests/functional/agent/ovn/metadata/test_metadata_agent.py
+++ b/neutron/tests/functional/agent/ovn/metadata/test_metadata_agent.py
@@ -141,7 +141,19 @@ class TestMetadataAgent(base.TestOVNFunctionalBase):
                 type=ovn_const.LSP_TYPE_LOCALPORT,
                 addresses='AA:AA:AA:AA:AA:AA 192.168.122.123',
                 external_ids={
-                    ovn_const.OVN_CIDRS_EXT_ID_KEY: '192.168.122.123/24'}))
+                    ovn_const.OVN_CIDRS_EXT_ID_KEY: '192.168.122.123/24',
+                    ovn_const.OVN_DEVID_EXT_ID_KEY: 'ovnmeta-' + lswitch_name
+                }))
+        return mdt_port_name
+
+    def _update_metadata_port_ip(self, metadata_port_name):
+        external_ids = {
+            ovn_const.OVN_CIDRS_EXT_ID_KEY: "192.168.122.2/24",
+            ovn_const.OVN_DEVID_EXT_ID_KEY:
+                'ovnmeta-' + uuidutils.generate_uuid()
+        }
+        self.nb_api.set_lswitch_port(lport_name=metadata_port_name,
+                                     external_ids=external_ids).execute()
 
     def _create_logical_switch_port(self, type_=None):
         lswitch_name = 'ovn-' + uuidutils.generate_uuid()
@@ -192,16 +204,25 @@ class TestMetadataAgent(base.TestOVNFunctionalBase):
 
         n_utils.wait_until_true(check_mock_pbinding, timeout=10, exception=exc)
 
-    def _test_agent_events(self, delete, type_=None):
+    def _test_agent_events(self, delete, type_=None, update=False):
         m_pb_created = mock.patch.object(
             agent.PortBindingChassisCreatedEvent, 'run').start()
         m_pb_deleted = mock.patch.object(
             agent.PortBindingChassisDeletedEvent, 'run').start()
+        m_pb_updated = mock.patch.object(
+            agent.PortBindingMetaPortUpdatedEvent, 'run').start()
 
         lswitchport_name, lswitch_name = self._create_logical_switch_port(
             type_)
         self.sb_api.lsp_bind(lswitchport_name, self.chassis_name).execute(
             check_error=True, log_errors=True)
+        if update and type_ == ovn_const.LSP_TYPE_LOCALPORT:
+            with self.nb_api.transaction(
+                    check_error=True, log_errors=True) as txn:
+                mdt_port_name = self._create_metadata_port(txn, lswitch_name)
+            self.sb_api.lsp_bind(mdt_port_name, self.chassis_name).execute(
+                check_error=True, log_errors=True)
+            self._update_metadata_port_ip(mdt_port_name)
 
         def pb_created():
             if m_pb_created.call_count < 1:
@@ -219,6 +240,31 @@ class TestMetadataAgent(base.TestOVNFunctionalBase):
                 "PortBindingChassisCreatedEvent didn't happen on port "
                 "binding."))
 
+        def pb_updated():
+            if m_pb_updated.call_count < 1:
+                return False
+            args = m_pb_updated.call_args[0]
+            self.assertEqual('update', args[0])
+            self.assertTrue(args[1].external_ids)
+            self.assertTrue(args[2].external_ids)
+            device_id = args[1].external_ids.get(
+                ovn_const.OVN_DEVID_EXT_ID_KEY, "")
+            self.assertTrue(device_id.startswith("ovnmeta-"))
+            new_cidrs = args[1].external_ids.get(
+                ovn_const.OVN_CIDRS_EXT_ID_KEY, "")
+            old_cidrs = args[2].external_ids.get(
+                ovn_const.OVN_CIDRS_EXT_ID_KEY, "")
+            self.assertNotEqual(new_cidrs, old_cidrs)
+            self.assertNotEqual(old_cidrs, "")
+            return True
+        if update and type_ == ovn_const.LSP_TYPE_LOCALPORT:
+            n_utils.wait_until_true(
+                pb_updated,
+                timeout=10,
+                exception=Exception(
+                    "PortBindingMetaPortUpdatedEvent didn't happen on "
+                    "metadata port ip address updated."))
+
         if delete:
             self.nb_api.delete_lswitch_port(
                 lswitchport_name, lswitch_name).execute(
@@ -290,6 +336,10 @@ class TestMetadataAgent(base.TestOVNFunctionalBase):
             timeout=10,
             exception=exc)
 
+    def test_agent_metadata_port_ip_update_event(self):
+        self._test_agent_events(
+            delete=False, type_=ovn_const.LSP_TYPE_LOCALPORT, update=True)
+
     def test_metadata_agent_only_monitors_own_chassis(self):
         # We already have the fake chassis which we should be monitoring, so
         # create an event looking for a change to another chassis
diff --git a/releasenotes/notes/bug-1996677-64851b476a0c5a37.yaml b/releasenotes/notes/bug-1996677-64851b476a0c5a37.yaml
new file mode 100644
index 00000000000..0743a617881
--- /dev/null
+++ b/releasenotes/notes/bug-1996677-64851b476a0c5a37.yaml
@@ -0,0 +1,6 @@
+---
+fixes:
+  - |
+    `1996677 <https://bugs.launchpad.net/neutron/+bug/1996677>`_
+    When the fixed_ips of metadata port is modified, the ip address of
+    tap device in metadata agent is modified.