diff --git a/tests/test_ssdp_datagram.py b/tests/test_ssdp_datagram.py
new file mode 100644
index 0000000..674735e
--- /dev/null
+++ b/tests/test_ssdp_datagram.py
@@ -0,0 +1,101 @@
+from twisted.trial import unittest
+from txupnp.ssdp_datagram import SSDPDatagram
+from txupnp.fault import UPnPError
+
+
+class TestParseMSearchRequest(unittest.TestCase):
+    datagram = b'M-SEARCH * HTTP/1.1\r\n' \
+               b'HOST: 239.255.255.250:1900\r\n' \
+               b'ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n' \
+               b'MAN: "ssdp:discover"\r\n' \
+               b'MX: 1\r\n' \
+               b'\r\n'
+
+    def test_parse_m_search_response(self):
+        packet = SSDPDatagram.decode(self.datagram)
+        self.assertTrue(packet._packet_type, packet._M_SEARCH)
+        self.assertEqual(packet.host, '239.255.255.250:1900')
+        self.assertEqual(packet.st, 'urn:schemas-upnp-org:device:InternetGatewayDevice:1')
+        self.assertEqual(packet.man, 'ssdp:discover')
+        self.assertEqual(packet.mx, 1)
+
+    def test_serialize_m_search(self):
+        packet = SSDPDatagram.decode(self.datagram)
+        self.assertEqual(packet.encode().encode(), self.datagram)
+
+
+class TestParseMSearchResponse(unittest.TestCase):
+    datagram = "\r\n".join([
+        'HTTP/1.1 200 OK',
+        'CACHE_CONTROL: max-age=1800',
+        'LOCATION: http://10.0.0.1:49152/InternetGatewayDevice.xml',
+        'SERVER: Linux, UPnP/1.0, DIR-890L Ver 1.20',
+        'ST: urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1',
+        'USN: uuid:00000000-0000-0000-0000-000000000000::urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1'
+    ]).encode()
+
+    def test_parse_m_search_response(self):
+        packet = SSDPDatagram.decode(self.datagram)
+        self.assertTrue(packet._packet_type, packet._OK)
+        self.assertEqual(packet.cache_control, 'max-age=1800')
+        self.assertEqual(packet.location, 'http://10.0.0.1:49152/InternetGatewayDevice.xml')
+        self.assertEqual(packet.server, 'Linux, UPnP/1.0, DIR-890L Ver 1.20')
+        self.assertEqual(packet.st, 'urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1')
+        self.assertEqual(packet.usn, 'uuid:00000000-0000-0000-0000-000000000000::urn:'
+                                     'schemas-upnp-org:service:WANCommonInterfaceConfig:1'
+)
+
+
+class TestParseMSearchResponseDashCacheControl(TestParseMSearchResponse):
+    datagram = "\r\n".join([
+        'HTTP/1.1 200 OK',
+        'CACHE-CONTROL: max-age=1800',
+        'LOCATION: http://10.0.0.1:49152/InternetGatewayDevice.xml',
+        'SERVER: Linux, UPnP/1.0, DIR-890L Ver 1.20',
+        'ST: urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1',
+        'USN: uuid:00000000-0000-0000-0000-000000000000::urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1'
+    ]).encode()
+
+
+class TestParseMSearchResponseCaseInsensitive(TestParseMSearchResponse):
+    datagram = "\r\n".join([
+        'HTTP/1.1 200 OK',
+        'cache-CONTROL: max-age=1800',
+        'LOCATION: http://10.0.0.1:49152/InternetGatewayDevice.xml',
+        'Server: Linux, UPnP/1.0, DIR-890L Ver 1.20',
+        'st: urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1',
+        'USN: uuid:00000000-0000-0000-0000-000000000000::urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1'
+    ]).encode()
+
+
+class TestFailToParseMSearchResponseNoST(unittest.TestCase):
+    datagram = "\r\n".join([
+        'HTTP/1.1 200 OK',
+        'CACHE_CONTROL: max-age=1800',
+        'LOCATION: http://10.0.0.1:49152/InternetGatewayDevice.xml',
+        'SERVER: Linux, UPnP/1.0, DIR-890L Ver 1.20',
+        'USN: uuid:00000000-0000-0000-0000-000000000000::urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1'
+    ]).encode()
+
+    def test_fail_to_parse_m_search_response(self):
+        self.assertRaises(UPnPError, SSDPDatagram.decode, self.datagram)
+
+
+class TestFailToParseMSearchResponseNoOK(TestFailToParseMSearchResponseNoST):
+    datagram = "\r\n".join([
+        'cache-CONTROL: max-age=1800',
+        'LOCATION: http://10.0.0.1:49152/InternetGatewayDevice.xml',
+        'Server: Linux, UPnP/1.0, DIR-890L Ver 1.20',
+        'st: urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1',
+        'USN: uuid:00000000-0000-0000-0000-000000000000::urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1'
+    ]).encode()
+
+
+class TestFailToParseMSearchResponseNoLocation(TestFailToParseMSearchResponseNoST):
+    datagram = "\r\n".join([
+        'HTTP/1.1 200 OK',
+        'cache-CONTROL: max-age=1800',
+        'Server: Linux, UPnP/1.0, DIR-890L Ver 1.20',
+        'st: urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1',
+        'USN: uuid:00000000-0000-0000-0000-000000000000::urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1'
+    ]).encode()
diff --git a/txupnp/ssdp_datagram.py b/txupnp/ssdp_datagram.py
index 0eed52d..2c7c519 100644
--- a/txupnp/ssdp_datagram.py
+++ b/txupnp/ssdp_datagram.py
@@ -15,7 +15,7 @@ _ssdp_datagram_patterns = {
     'nts': (re.compile("^(?i)(nts):(.*)$"), str),
     'usn': (re.compile("^(?i)(usn):(.*)$"), str),
     'location': (re.compile("^(?i)(location):(.*)$"), str),
-    'cache_control': (re.compile("^(?i)(cache-control):(.*)$"), str),
+    'cache_control': (re.compile("^(?i)(cache[-|_]control):(.*)$"), str),
     'server': (re.compile("^(?i)(server):(.*)$"), str),
 }
 
@@ -124,8 +124,12 @@ class SSDPDatagram(object):
         return self._lines_to_content_dict(self.encode().split(line_separator))
 
     @classmethod
-    def decode(cls, datagram):
+    def decode(cls, datagram: bytes):
         packet = cls._from_string(datagram.decode())
+        if packet is None:
+            raise UPnPError(
+                "failed to decode datagram: {}".format(binascii.hexlify(datagram))
+            )
         for attr_name in packet._required_fields[packet._packet_type]:
             attr = getattr(packet, attr_name)
             if attr is None: