diff --git a/inventory_provider/config.py b/inventory_provider/config.py
index 9cd8085ce85dbac1f61fa46d290e2d29d1809092..f332f5bb2bdf60b66c99c231addf5f0c23fcc94e 100644
--- a/inventory_provider/config.py
+++ b/inventory_provider/config.py
@@ -184,11 +184,16 @@ CONFIG_SCHEMA = {
             'items': {'$ref': '#/definitions/gws-direct-nren-isp'}
         },
         'nren-asn-map': {
-            'type': 'object',
-            'patternProperties': {
-                r'^\d+$': {'type': 'string'}
+            'type': 'array',
+            'items': {
+                'type': 'object',
+                'properties': {
+                  'nren': {'type': 'string'},
+                  'asn': {'type': 'integer'}
+                },
+                'required': ['nren', 'asn'],
+                'additionalProperties': False
             },
-            'additionalProperties': False
         }
     },
 
diff --git a/inventory_provider/routes/msr.py b/inventory_provider/routes/msr.py
index 6f5087b2fdf6ff14fa2ddd40c298a0ba140c2733..f179a52c73e46727f6834e901383ab6b4351a00c 100644
--- a/inventory_provider/routes/msr.py
+++ b/inventory_provider/routes/msr.py
@@ -59,6 +59,11 @@ These endpoints are intended for use by MSR.
 
 .. autofunction:: inventory_provider.routes.msr.bgp_all_peerings
 
+/msr/mdpvn
+--------------------------------------------
+
+.. autofunction:: inventory_provider.routes.msr.mdvpn
+
 /msr/services
 --------------------------------------------
 
@@ -256,6 +261,66 @@ SYSTEM_CORRELATION_SERVICES_LIST_SCHEMA = {
     'minItems': 1  # otherwise the route should return 404
 }
 
+MDVPN_LIST_SCHEMA = {
+    '$schema': 'http://json-schema.org/draft-07/schema#',
+    'definitions': {
+        'mdvpn_group': {
+            'type': 'object',
+            'properties': {
+                'asn': {'type': 'integer'},
+                'AP': {'$ref': '#/definitions/ap_peerings'},
+                'VRR': {'$ref': '#/definitions/vrr_peerings'}
+            },
+            'required': [
+                'asn', 'AP', 'VRR'
+            ],
+            'additionalProperties': False
+        },
+        'ap_peerings': {
+            'type': 'array',
+            'items': {
+                '$ref': '#/definitions/bgplu_peering'
+            }
+        },
+        'bgplu_peering': {
+            'type': 'object',
+            'properties': {
+                'name': {'type': 'string'},
+                'v4': {'type': 'string'},
+                'v6': {'type': 'string'},
+                'hostname': {'type': 'string'}
+            },
+            'required': [
+                'name', 'v4', 'v6', 'hostname'
+            ],
+            'additionalProperties': False
+        },
+        'vrr_peerings': {
+            'type': 'array',
+            'items': {
+                '$ref': '#/definitions/vpn_peering'
+            }
+        },
+        'vpn_peering': {
+            'type': 'object',
+            'properties': {
+                'description': {'type': 'string'},
+                'v4': {'type': 'string'},
+                'hostname': {
+                    'type': 'array',
+                    'items': {
+                        'type': 'string'
+                    },
+                    'minItems': 1
+                }
+            },
+            'additionalProperties': False
+        }
+    },
+    'type': 'array',
+    'items': {'$ref': '#/definitions/mdvpn_group'}
+}
+
 
 @routes.after_request
 def after_request(resp):
@@ -875,3 +940,116 @@ def bgp_all_peerings():
     r = common.get_current_redis()
     response = r.get('juniper-peerings:all')
     return Response(response.decode('utf-8'), mimetype="application/json")
+
+
+@routes.route('/mdvpn', methods=['GET', 'POST'])
+@common.require_accepts_json
+def mdvpn():
+    """
+    Handler for `/mdvpn`
+
+    This method returns a list of all BGP-LU peerings, and the VR peerings
+    for both Paris & Ljubljana.
+
+    The response will be formatted according to the following schema:
+
+    .. asjson::
+       inventory_provider.routes.msr.MDVPN_LIST_SCHEMA
+
+    :return:
+    """
+
+    def _get_consistent_description(description):
+        """
+        The same interface in VRR peerings can have multiple names.
+        These names are (currently) the same but with a different local prefix,
+        with no ordering guaranteed by the redis cache.
+        As only one description is returned by this endpoint for each
+        IPv4 address, this serves as a quick and dirty way of merging these
+        multiple descriptions into one an external user can use to identify
+        the peering reliably.
+
+        :param description: The raw description for a VRR peering
+        :return: The same description with location prefix removed
+        """
+        # it is incredibly likely this will need revision later down the line
+        expected_prefixes = [
+            "MD-VPN-VRR-PARIS-",
+            "MD-VPN-VRR-LJUBLJANA-"
+        ]
+        for prefix in expected_prefixes:
+            if description.startswith(prefix):
+                return description.replace(prefix, '')
+        return description
+
+    def _make_group_index(group, index_key):
+        """
+        Utility function to take a list and make it a dict based off a given
+        key, for fast lookup of a specific key field.
+
+        :param group: A list of dicts which should all have `index_key` as a
+        field
+        :param index_key: Name of the key to index on
+        :return: Dict with `index_key` as the key field and a list of all
+        matching dicts as the value
+        """
+        index = {}
+        for peering in group:
+            key = peering.get(index_key)
+            index.setdefault(key, []).append(peering)
+        return index
+
+    def _bgplu_peerings(asn, bgplu_index):
+        for peering in bgplu_index.get(asn, []):
+            formatted_peering = {
+                "name": peering['description'],
+                "v4": peering['address'],
+                "v6": '',
+                "hostname": peering['hostname']
+            }
+            yield formatted_peering
+
+    def _vpnrr_peerings(asn, vpnrr_index):
+        if asn in vpnrr_index:
+            vrr_peering_group = vpnrr_index[asn]
+            # rearrange into index using ipv4 as key
+            # this will collect related entries under the same ipv4
+            ip_index = _make_group_index(vrr_peering_group, 'address')
+            for ip in ip_index:
+                ip_details = ip_index[ip]  # a list of all info for given ipv4
+                hostnames = [item['hostname'] for item in ip_details]
+                description = ip_details[0]['description']
+
+                formatted_peering = {
+                    "description": _get_consistent_description(description),
+                    "v4": ip,
+                    "hostname": hostnames
+                }
+                yield formatted_peering
+
+    def _peerings_for_nren(asn, bgplu_index, vpnrr_index):
+        return {
+            "asn": asn,
+            "AP": list(_bgplu_peerings(asn, bgplu_index)),
+            "VRR": list(_vpnrr_peerings(asn, vpnrr_index))
+        }
+
+    r = common.get_current_redis()
+    cache_key = 'classifier-cache:msr:mdvpn'
+    response = _ignore_cache_or_retrieve(request, cache_key, r)
+    if not response:
+        bgplu = json.loads(
+            r.get('juniper-peerings:group:BGPLU').decode('utf-8'))
+        vpnrr = json.loads(
+            r.get('juniper-peerings:group:VPN-RR').decode('utf-8'))
+        bgplu_index = _make_group_index(bgplu, 'remote-asn')
+        vpnrr_index = _make_group_index(vpnrr, 'remote-asn')
+        config = current_app.config['INVENTORY_PROVIDER_CONFIG']
+        nren_asn_map = config['nren-asn-map']
+        nren_details = [
+            _peerings_for_nren(pair['asn'],
+                               bgplu_index,
+                               vpnrr_index)
+            for pair in nren_asn_map]
+        response = json.dumps(nren_details)
+    return Response(response, mimetype='application/json')
diff --git a/test/conftest.py b/test/conftest.py
index b1be8a02b80f93784bbc4aee244ca66efbfc766d..4e6d794b9c1417fee51526d360f79ed58a04c844 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -60,11 +60,20 @@ def data_config_filename():
                 }
             ],
             'gws-direct': {},
-            'nren-asn-map': {
-                "100": "BogusNREN",
-                "200": "FoobarNREN",
-                "300": "AlsoNET"
-            }
+            'nren-asn-map': [
+                {
+                    "nren": "FOO",
+                    "asn": 1930
+                },
+                {
+                    "nren": "BAR",
+                    "asn": 680
+                },
+                {
+                    "nren": "BAT",
+                    "asn": 2200
+                }
+            ]
         }
 
         with open(os.path.join(TEST_DATA_DIRNAME, 'gws-direct.json')) as gws:
diff --git a/test/test_msr_routes.py b/test/test_msr_routes.py
index 0bec3d504185fa7070f8e06fa5a3686ec91c6c74..eda590d2613d9b45d0383cb660c0626ee96e7098 100644
--- a/test/test_msr_routes.py
+++ b/test/test_msr_routes.py
@@ -5,7 +5,8 @@ import pytest
 
 from inventory_provider.routes.msr import PEERING_LIST_SCHEMA, \
     PEERING_GROUP_LIST_SCHEMA, PEERING_ADDRESS_SERVICES_LIST, \
-    SYSTEM_CORRELATION_SERVICES_LIST_SCHEMA, _get_services_for_address
+    SYSTEM_CORRELATION_SERVICES_LIST_SCHEMA, _get_services_for_address, \
+    MDVPN_LIST_SCHEMA
 from inventory_provider.routes.poller import SERVICES_LIST_SCHEMA
 from inventory_provider.tasks.common import _get_redis
 
@@ -321,3 +322,15 @@ def test_get_all_peerings(client):
     response_data = json.loads(rv.data.decode('utf-8'))
     jsonschema.validate(response_data, PEERING_LIST_SCHEMA)
     assert response_data  # test data is non-empty
+
+
+def test_get_mdvpn_peerings(client, mocked_redis):
+    rv = client.get(
+        '/msr/mdvpn',
+        headers=DEFAULT_REQUEST_HEADERS
+    )
+    assert rv.status_code == 200
+    assert rv.is_json
+    response_data = json.loads(rv.data.decode('utf-8'))
+    jsonschema.validate(response_data, MDVPN_LIST_SCHEMA)
+    assert response_data  # test data is non-empty