diff --git a/scapy/contrib/macsec.py b/scapy/contrib/macsec.py
new file mode 100644
index 0000000000000000000000000000000000000000..b038d38460e4d04aef01ea2b8d241cc9cfa3dff4
--- /dev/null
+++ b/scapy/contrib/macsec.py
@@ -0,0 +1,215 @@
+# This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Sabrina Dubroca <sd@queasysnail.net>
+## This program is published under a GPLv2 license
+
+"""
+Classes and functions for MACsec.
+"""
+
+from __future__ import absolute_import
+from __future__ import print_function
+import struct
+
+from scapy.config import conf
+from scapy.fields import *
+from scapy.packet import Packet, Raw, bind_layers
+from scapy.layers.l2 import Ether, Dot1AD, Dot1Q
+from scapy.layers.eap import MACsecSCI
+from scapy.layers.inet import IP
+from scapy.layers.inet6 import IPv6
+
+if conf.crypto_valid:
+    from cryptography.exceptions import InvalidTag
+    from cryptography.hazmat.backends import default_backend
+    from cryptography.hazmat.primitives import interfaces
+    from cryptography.hazmat.primitives.ciphers import (
+        Cipher,
+        algorithms,
+        modes,
+    )
+else:
+    log_loading.info("Can't import python-cryptography v1.7+. "
+                     "Disabled MACsec encryption/authentication.")
+
+
+NOSCI_LEN = 14 + 6
+SCI_LEN = 8
+DEFAULT_ICV_LEN = 16
+
+
+class MACsecSA(object):
+    """Representation of a MACsec Secure Association
+
+    Provides encapsulation, decapsulation, encryption, and decryption
+    of MACsec frames
+    """
+    def __init__(self, sci, an, pn, key, icvlen, encrypt, send_sci):
+        if isinstance(sci, int) or isinstance(sci, long):
+            self.sci = struct.pack('!Q', sci)
+        elif isinstance(sci, str):
+            self.sci = sci
+        else:
+            raise TypeError("SCI must be either str or int")
+        self.an = an
+        self.pn = pn
+        self.key = key
+        self.icvlen = icvlen
+        self.do_encrypt = encrypt
+        self.send_sci = send_sci
+
+    def make_iv(self, pkt):
+        """generate an IV for the packet"""
+        return self.sci + struct.pack('!I', pkt[MACsec].pn)
+
+    @staticmethod
+    def split_pkt(pkt, assoclen, icvlen=0):
+        """
+        split the packet into associated data, plaintext or ciphertext, and
+        optional ICV
+        """
+        data = raw(pkt)
+        assoc = data[:assoclen]
+        if icvlen:
+            icv = data[-icvlen:]
+            enc = data[assoclen:-icvlen]
+        else:
+            icv = b''
+            enc = data[assoclen:]
+        return assoc, enc, icv
+
+    def e_bit(self):
+        """returns the value of the E bit for packets sent through this SA"""
+        return self.do_encrypt
+
+    def c_bit(self):
+        """returns the value of the C bit for packets sent through this SA"""
+        return self.do_encrypt or self.icvlen != DEFAULT_ICV_LEN
+
+    @staticmethod
+    def shortlen(pkt):
+        """determine shortlen for a raw packet (not encapsulated yet)"""
+        datalen = len(pkt) - 2*6
+        if datalen < 48:
+            return datalen
+        return 0
+
+    def encap(self, pkt):
+        """encapsulate a frame using this Secure Association"""
+        if pkt.name != Ether().name:
+            raise TypeError('cannot encapsulate packet in MACsec, must be Ethernet')
+        hdr = copy.deepcopy(pkt)
+        payload = hdr.payload
+        del hdr.payload
+        tag = MACsec(sci=self.sci, an=self.an,
+                     SC=self.send_sci,
+                     E=self.e_bit(), C=self.c_bit(),
+                     shortlen=MACsecSA.shortlen(pkt),
+                     pn=self.pn, type=pkt.type)
+        hdr.type = ETH_P_MACSEC
+        return hdr/tag/payload
+
+    # this doesn't really need to be a method, but for symmetry with
+    # encap(), it is
+    def decap(self, orig_pkt):
+        """decapsulate a MACsec frame"""
+        if orig_pkt.name != Ether().name or orig_pkt.payload.name != MACsec().name:
+            raise TypeError('cannot decapsulate MACsec packet, must be Ethernet/MACsec')
+        packet = copy.deepcopy(orig_pkt)
+        prev_layer = packet[MACsec].underlayer
+        prev_layer.type = packet[MACsec].type
+        next_layer = packet[MACsec].payload
+        del prev_layer.payload
+        if prev_layer.name == Ether().name:
+            return Ether(raw(prev_layer/next_layer))
+        return prev_layer/next_layer
+
+    def encrypt(self, orig_pkt, assoclen=None):
+        """encrypt a MACsec frame for this Secure Association"""
+        hdr = copy.deepcopy(orig_pkt)
+        del hdr[MACsec].payload
+        del hdr[MACsec].type
+        pktlen = len(orig_pkt)
+        if self.send_sci:
+            hdrlen = NOSCI_LEN + SCI_LEN
+        else:
+            hdrlen = NOSCI_LEN
+        if assoclen is None or not self.do_encrypt:
+            if self.do_encrypt:
+                assoclen = hdrlen
+            else:
+                assoclen = pktlen
+        iv = self.make_iv(orig_pkt)
+        assoc, pt, _ = MACsecSA.split_pkt(orig_pkt, assoclen)
+        encryptor = Cipher(
+            algorithms.AES(self.key),
+            modes.GCM(iv),
+            backend=default_backend()
+        ).encryptor()
+        encryptor.authenticate_additional_data(assoc)
+        ct = encryptor.update(pt) + encryptor.finalize()
+        hdr[MACsec].payload = Raw(assoc[hdrlen:assoclen] + ct + encryptor.tag)
+        return hdr
+
+    def decrypt(self, orig_pkt, assoclen=None):
+        """decrypt a MACsec frame for this Secure Association"""
+        hdr = copy.deepcopy(orig_pkt)
+        del hdr[MACsec].payload
+        pktlen = len(orig_pkt)
+        if self.send_sci:
+            hdrlen = NOSCI_LEN + SCI_LEN
+        else:
+            hdrlen = NOSCI_LEN
+        if assoclen is None or not self.do_encrypt:
+            if self.do_encrypt:
+                assoclen = hdrlen
+            else:
+                assoclen = pktlen - self.icvlen
+        iv = self.make_iv(hdr)
+        assoc, ct, icv = MACsecSA.split_pkt(orig_pkt, assoclen, self.icvlen)
+        decryptor = Cipher(
+               algorithms.AES(self.key),
+               modes.GCM(iv, icv),
+               backend=default_backend()
+           ).decryptor()
+        decryptor.authenticate_additional_data(assoc)
+        pt = assoc[hdrlen:assoclen]
+        pt += decryptor.update(ct)
+        pt += decryptor.finalize()
+        hdr[MACsec].type = struct.unpack('!H', pt[0:2])[0]
+        hdr[MACsec].payload = Raw(pt[2:])
+        return hdr
+
+
+class MACsec(Packet):
+    """representation of one MACsec frame"""
+    name = '802.1AE'
+    fields_desc = [BitField('Ver', 0, 1),
+                   BitField('ES', 0, 1),
+                   BitField('SC', 0, 1),
+                   BitField('SCB', 0, 1),
+                   BitField('E', 0, 1),
+                   BitField('C', 0, 1),
+                   BitField('an', 0, 2),
+                   BitField('reserved', 0, 2),
+                   BitField('shortlen', 0, 6),
+                   IntField("pn", 1),
+                   ConditionalField(PacketField("sci", None, MACsecSCI), lambda pkt: pkt.SC),
+                   ConditionalField(XShortEnumField("type", None, ETHER_TYPES),
+                                    lambda pkt: pkt.type is not None)]
+
+    def mysummary(self):
+        summary = self.sprintf("an=%MACsec.an%, pn=%MACsec.pn%")
+        if self.SC:
+            summary += self.sprintf(", sci=%MACsec.sci%")
+        if self.type is not None:
+            summary += self.sprintf(", %MACsec.type%")
+        return summary
+
+
+bind_layers(MACsec, IP, type=ETH_P_IP)
+bind_layers(MACsec, IPv6, type=ETH_P_IPV6)
+
+bind_layers( Dot1AD,        MACsec,        type=ETH_P_MACSEC)
+bind_layers( Dot1Q,         MACsec,        type=ETH_P_MACSEC)
+bind_layers( Ether,         MACsec,        type=ETH_P_MACSEC)
diff --git a/scapy/contrib/macsec.uts b/scapy/contrib/macsec.uts
new file mode 100644
index 0000000000000000000000000000000000000000..32ed859012550b7d70444a2d26a3bb61505492e2
--- /dev/null
+++ b/scapy/contrib/macsec.uts
@@ -0,0 +1,269 @@
+# MACsec unit tests
+# run with:
+#   test/run_tests  -P "load_contrib('macsec')" -t scapy/contrib/macsec.uts -F
+
++ MACsec
+~ crypto not_pypy
+# Note: these tests are disabled with pypy, as the cryptography module does
+#       not currently work with the pypy version used by Travis CI.
+
+= MACsec - basic encap - encrypted
+sa = MACsecSA(sci='\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=100, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
+m = sa.encap(p)
+assert(m.type == ETH_P_MACSEC)
+assert(m[MACsec].type == ETH_P_IP)
+assert(len(m) == len(p) + 16)
+assert(m[MACsec].an == 0)
+assert(m[MACsec].pn == 100)
+assert(m[MACsec].shortlen == 0)
+assert(m[MACsec].SC)
+assert(m[MACsec].E)
+assert(m[MACsec].C)
+assert(m[MACsec].sci == b'\x52\x54\x00\x13\x01\x56\x00\x01')
+
+= MACsec - basic encryption - encrypted
+sa = MACsecSA(sci='\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=100, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
+m = sa.encap(p)
+e = sa.encrypt(m)
+assert(e.type == ETH_P_MACSEC)
+assert(e[MACsec].type == None)
+assert(len(e) == len(p) + 16 + 16)
+assert(e[MACsec].an == 0)
+assert(e[MACsec].pn == 100)
+assert(e[MACsec].shortlen == 0)
+assert(e[MACsec].SC)
+assert(e[MACsec].E)
+assert(e[MACsec].C)
+assert(e[MACsec].sci == b'\x52\x54\x00\x13\x01\x56\x00\x01')
+
+= MACsec - basic decryption - encrypted
+sa = MACsecSA(sci='\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=100, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
+m = sa.encap(p)
+e = sa.encrypt(m)
+d = sa.decrypt(e)
+assert(d.type == ETH_P_MACSEC)
+assert(d[MACsec].type == ETH_P_IP)
+assert(len(d) == len(m))
+assert(d[MACsec].an == 0)
+assert(d[MACsec].pn == 100)
+assert(d[MACsec].shortlen == 0)
+assert(d[MACsec].SC)
+assert(d[MACsec].E)
+assert(d[MACsec].C)
+assert(d[MACsec].sci == b'\x52\x54\x00\x13\x01\x56\x00\x01')
+assert(raw(d) == raw(m))
+
+= MACsec - basic decap - decrypted
+sa = MACsecSA(sci='\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=100, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
+m = sa.encap(p)
+e = sa.encrypt(m)
+d = sa.decrypt(e)
+r = sa.decap(d)
+assert(raw(r) == raw(p))
+
+
+
+= MACsec - basic encap - integrity only
+sa = MACsecSA(sci='\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
+m = sa.encap(p)
+assert(m.type == ETH_P_MACSEC)
+assert(m[MACsec].type == ETH_P_IP)
+assert(len(m) == len(p) + 16)
+assert(m[MACsec].an == 0)
+assert(m[MACsec].pn == 200)
+assert(m[MACsec].shortlen == 0)
+assert(m[MACsec].SC)
+assert(not m[MACsec].E)
+assert(not m[MACsec].C)
+assert(m[MACsec].sci == b'\x52\x54\x00\x13\x01\x56\x00\x01')
+
+= MACsec - basic encryption - integrity only
+sa = MACsecSA(sci='\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
+m = sa.encap(p)
+e = sa.encrypt(m)
+assert(m.type == ETH_P_MACSEC)
+assert(e[MACsec].type == None)
+assert(len(e) == len(p) + 16 + 16)
+assert(e[MACsec].an == 0)
+assert(e[MACsec].pn == 200)
+assert(e[MACsec].shortlen == 0)
+assert(e[MACsec].SC)
+assert(not e[MACsec].E)
+assert(not e[MACsec].C)
+assert(e[MACsec].sci == b'\x52\x54\x00\x13\x01\x56\x00\x01')
+assert(raw(e)[:-16] == raw(m))
+
+= MACsec - basic decryption - integrity only
+sa = MACsecSA(sci='\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
+m = sa.encap(p)
+e = sa.encrypt(m)
+d = sa.decrypt(e)
+assert(d.type == ETH_P_MACSEC)
+assert(d[MACsec].type == ETH_P_IP)
+assert(len(d) == len(m))
+assert(d[MACsec].an == 0)
+assert(d[MACsec].pn == 200)
+assert(d[MACsec].shortlen == 0)
+assert(d[MACsec].SC)
+assert(not d[MACsec].E)
+assert(not d[MACsec].C)
+assert(d[MACsec].sci == b'\x52\x54\x00\x13\x01\x56\x00\x01')
+assert(raw(d) == raw(m))
+
+= MACsec - basic decap - integrity only
+sa = MACsecSA(sci='\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
+m = sa.encap(p)
+e = sa.encrypt(m)
+d = sa.decrypt(e)
+r = sa.decap(d)
+assert(raw(r) == raw(p))
+
+= MACsec - encap - shortlen 2
+sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')
+m = sa.encap(p)
+assert(m[MACsec].shortlen == 2)
+assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0))
+
+= MACsec - encap - shortlen 10
+sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 8)
+m = sa.encap(p)
+assert(m[MACsec].shortlen == 10)
+assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0))
+
+= MACsec - encap - shortlen 18
+sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 16)
+m = sa.encap(p)
+assert(m[MACsec].shortlen == 18)
+assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0))
+
+= MACsec - encap - shortlen 32
+sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 30)
+m = sa.encap(p)
+assert(m[MACsec].shortlen == 32)
+assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0))
+
+= MACsec - encap - shortlen 40
+sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 38)
+m = sa.encap(p)
+assert(m[MACsec].shortlen == 40)
+assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0))
+
+= MACsec - encap - shortlen 47
+sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 45)
+m = sa.encap(p)
+assert(m[MACsec].shortlen == 47)
+assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0))
+
+= MACsec - encap - shortlen 0 (48)
+sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 45 + "y")
+m = sa.encap(p)
+assert(m[MACsec].shortlen == 0)
+
+
+= MACsec - encap - shortlen 2/nosci
+sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=0)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')
+m = sa.encap(p)
+assert(m[MACsec].shortlen == 2)
+assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0))
+
+= MACsec - encap - shortlen 32/nosci
+sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=0)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 30)
+m = sa.encap(p)
+assert(m[MACsec].shortlen == 32)
+assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0))
+
+= MACsec - encap - shortlen 47/nosci
+sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=0)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 45)
+m = sa.encap(p)
+assert(m[MACsec].shortlen == 47)
+assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0))
+
+
+= MACsec - authenticate
+tx = Ether(b"RT\x00\x12\x01V.\xbc\x84\xd5\xca\x13\x88\xe5 \x00\x00\x00\x00\rRT\x00\x13\x01V\x00\x01\x08\x00E\x00\x00T\x11:@\x00@\x01\xa6\x1b\xc0\xa8\x01\x01\xc0\xa8\x01\x02\x08\x00a\xeaG+\x00\x01\xc0~RY\x00\x00\x00\x00w>\x06\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\xf1\xb8\xe4,b\xb00\x98L\x85m1Q9\t:")
+rx = Ether(b".\xbc\x84\xd5\xca\x13RT\x00\x12\x01V\x88\xe5 \x00\x00\x00\x00#RT\x00\x12\x01V\x00\x01\x08\x00E\x00\x00T\xd4\x1a\x00\x00@\x01#;\xc0\xa8\x01\x02\xc0\xa8\x01\x01\x00\x00i\xeaG+\x00\x01\xc0~RY\x00\x00\x00\x00w>\x06\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37z\x11\xf8S\xe5u\x81\xe8\x19\\nxX\x02\x13\x01")
+rxsa = MACsecSA(sci=0x5254001201560001, an=0, pn=31, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1)
+txsa = MACsecSA(sci=0x5254001301560001, an=0, pn=31, key=b'\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61', icvlen=16, encrypt=0, send_sci=1)
+txdec = txsa.decap(txsa.decrypt(tx))
+rxdec = rxsa.decap(rxsa.decrypt(rx))
+txref = b"RT\x00\x12\x01V.\xbc\x84\xd5\xca\x13\x08\x00E\x00\x00T\x11:@\x00@\x01\xa6\x1b\xc0\xa8\x01\x01\xc0\xa8\x01\x02\x08\x00a\xeaG+\x00\x01\xc0~RY\x00\x00\x00\x00w>\x06\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37"
+rxref = b".\xbc\x84\xd5\xca\x13RT\x00\x12\x01V\x08\x00E\x00\x00T\xd4\x1a\x00\x00@\x01#;\xc0\xa8\x01\x02\xc0\xa8\x01\x01\x00\x00i\xeaG+\x00\x01\xc0~RY\x00\x00\x00\x00w>\x06\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37"
+assert(raw(txdec) == raw(txref))
+assert(raw(rxdec) == raw(rxref))
+
+
+
+= MACsec - authenticate - invalid packet
+rx = Ether(b".\xbc\x84\xd5\xca\x13RT\x00\x12\x01V\x88\xe5 \x00\x00\x00\x00#RT\x00\x12\x01V\x00\x01\x08\x00E\x00\x00T\xd4\x1a\x00\x00@\x01#;\xc0\xa8\x01\x02\xc0\xa8\x01\x01\x00\x00i\xeaG+\x00\x01\xc0~RY\x00\x00\x00\x00w>\x06\x00\x00\x00\x00\x00\xba\xdb\xba\xdb\xad\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37z\x11\xf8S\xe5u\x81\xe8\x19\\nxX\x02\x13\x01")
+txsa = MACsecSA(sci=0x5254001301560001, an=0, pn=31, key=b'\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61', icvlen=16, encrypt=0, send_sci=1)
+try:
+    rxdec = rxsa.decap(rxsa.decrypt(rx))
+    assert(not "This packet shouldn't have been authenticated as correct")
+except InvalidTag:
+    pass
+
+
+
+= MACsec - decrypt
+tx = Ether(b"RT\x00\x12\x01V.\xbc\x84\xd5\xca\x13\x88\xe5,\x00\x00\x00\x00\x1fRT\x00\x13\x01V\x00\x01\xf1\xd6\xf7\x03\xf0%\x19\x8e\x88\xb0\xac\xa1\x82\x98\x94]\x85&\xc4U*\x84kX#O\xb6\x8f\xf1\x9d\xc5\xdc\xe0\x80,\x98\x8e_\xd53e\x16b0\xaf\xd9\x9e;A\x8aM\xfe\x16\xf6j\xe6\xea\xb7\x9c\xf3\x9bCc#,\x93\xf7\xc0\xdb\x9a\xd0\x0c\xfd?\xcbd\xe5D\xb7\xc9\xbb\xf5\x93M\xc5\x1d'LR\xed\xf3\xbc\xa0\xdf\x86\xf7\xc2JN\xcd\x19\xc1?\xf7\xd2\xbe\x00\x0f`\xe0\x04\xcfX5\xdc\xe7\xb6\xe6\x82\xc4\xac\xd7\x06\xe31\xbe|\x98l\xc8\xc1#*n+x|\xad\x0b<\xfd\xb8\xceoR\x1e")
+rx = Ether(b".\xbc\x84\xd5\xca\x13RT\x00\x12\x01V\x88\xe5,\x00\x00\x00\x005RT\x00\x12\x01V\x00\x01\x04\xbaV\xe6\xcf\xcfbhy\x7f\xce\x12.\x80WI\xe5\xd5\x98)6\xe1vjVO@\x84\xde\x9b\x83\xbaw\xef\x13\xc3\xfd\xad\x81\xd4S?\x01\x01\xab\xa8 PzSq2\x905\xf6\x8cT\xd7\xb0P\xe2\xd04\xc7F\x88\x85\x10\xc3\x99\x80\xe3(\t\x10\x87\xa9{z\x22\xce>;Mr\xbe\xc1\xa0\x07%\x01\x96\xe5\xa3\x18]\xec\xbb\x7f\xde0\xa1\x99\xb2\xad\x93j\x97\xef\xf4\xee\xf0\xe4s\xb7h\x95\xc3\x8b[~hX\xbf|\xee\x99\x97\xe0;Q\x9aa\xb9\x13$\xd6\xe4\xb4\xce\njt\xc0\xa1.")
+rxsa = MACsecSA(sci=0x5254001201560001, an=0, pn=31, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1)
+txsa = MACsecSA(sci=0x5254001301560001, an=0, pn=31, key=b'\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61', icvlen=16, encrypt=1, send_sci=1)
+txdec = txsa.decap(txsa.decrypt(tx))
+rxdec = rxsa.decap(rxsa.decrypt(rx))
+txref = b"RT\x00\x12\x01V.\xbc\x84\xd5\xca\x13\x08\x00E\x00\x00\x80#D@\x00@\x01\x93\xe5\xc0\xa8\x01\x01\xc0\xa8\x01\x02\x08\x00E\xd5\x0f\xb3\x00\x01SrSY\x00\x00\x00\x00\x8b\x1d\r\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abc"
+rxref = b".\xbc\x84\xd5\xca\x13RT\x00\x12\x01V\x08\x00E\x00\x00\x80\x05\xab\x00\x00@\x01\xf1~\xc0\xa8\x01\x02\xc0\xa8\x01\x01\x00\x00M\xd5\x0f\xb3\x00\x01SrSY\x00\x00\x00\x00\x8b\x1d\r\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abc"
+assert(raw(txdec) == raw(txref))
+assert(raw(rxdec) == raw(rxref))
+
+
+
+= MACsec - decrypt - invalid packet
+rx = Ether(b".\xbc\x84\xd5\xca\x13RT\x00\x12\x01V\x88\xe5,\x00\x00\x00\x005RT\x00\x12\x01V\x00\x01\x04\xbaV\xe6\xcf\xcfbhy\x7f\xce\x12.\x80WI\xe5\xd5\x98)6\xe1vjVO@\x84\xde\x9b\x83\xbaw\xef\x13\xc3\xfd\xad\x81\xd4S?\x01\x01\xab\xa8 PzSq2\x905\xf6\x8cT\xd7\xb0P\xe2\xd04\xc7F\x88\x85\x10\xc3\x99\x80\xe3(\t\x10\x87\xa9{z\x22\xce>;Mr\xbe\xc1\xa0\x07%\x01\x96\xe5\xa3\x18]\xec\xbb\x7f\xde0\xa1\x99\xb2\xad\x93j\x97\xba\xdb\xad\xf0\xe4s\xb7h\x95\xc3\x8b[~hX\xbf|\xee\x99\x97\xe0;Q\x9aa\xb9\x13$\xd6\xe4\xb4\xce\njt\xc0\xa1.")
+rxsa = MACsecSA(sci=0x5254001201560001, an=0, pn=31, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1)
+try:
+    rxdec = rxsa.decap(rxsa.decrypt(rx))
+    assert(not "This packet shouldn't have been decrypted correctly")
+except InvalidTag:
+    pass
+
+
+
+= MACsec - decap - non-Ethernet
+rxsa = MACsecSA(sci=0x5254001201560001, an=0, pn=31, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1)
+try:
+    rxsa.decap(IP())
+except TypeError as e:
+    assert(str(e) == "cannot decapsulate MACsec packet, must be Ethernet/MACsec")
+
+= MACsec - decap - non-MACsec
+rxsa = MACsecSA(sci=0x5254001201560001, an=0, pn=31, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1)
+try:
+    rxsa.decap(Ether()/IP())
+except TypeError as e:
+    assert(str(e) == "cannot decapsulate MACsec packet, must be Ethernet/MACsec")
+
+= MACsec - encap - non-Ethernet
+txsa = MACsecSA(sci=0x5254001201560001, an=0, pn=31, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1)
+try:
+    txsa.encap(IP())
+except TypeError as e:
+    assert(str(e) == "cannot encapsulate packet in MACsec, must be Ethernet")
diff --git a/scapy/data.py b/scapy/data.py
index 70fb35d186d410791a15d7f48573240352a795d3..435134d225d8e9d5eaf4a569a29e35fbe2399847 100644
--- a/scapy/data.py
+++ b/scapy/data.py
@@ -31,6 +31,7 @@ ETH_P_ALL = 3
 ETH_P_IP = 0x800
 ETH_P_ARP = 0x806
 ETH_P_IPV6 = 0x86dd
+ETH_P_MACSEC = 0x88e5
 
 # From net/if_arp.h
 ARPHDR_ETHER = 1
diff --git a/scapy/layers/l2.py b/scapy/layers/l2.py
index 3aa148adcdcb28f00ecff0742253caabc355e32f..d461c0419eaeed00962fc2aa9644c42855096db8 100644
--- a/scapy/layers/l2.py
+++ b/scapy/layers/l2.py
@@ -143,6 +143,7 @@ class ARPSourceMACField(SourceMACField):
 ### Layers
 
 ETHER_TYPES['802_AD'] = 0x88a8
+ETHER_TYPES['802_1AE'] = ETH_P_MACSEC
 
 class Ether(Packet):
     name = "Ethernet"
@@ -410,6 +411,7 @@ class Loopback(Packet):
 class Dot1AD(Dot1Q):
     name = '802_1AD'
 
+
 bind_layers( Dot3,          LLC,           )
 bind_layers( Ether,         LLC,           type=122)
 bind_layers( Ether,         LLC,           type=34928)