diff --git a/ebpfcat/ebpf.rst b/ebpfcat/ebpf.rst
index cfc68387f5ea54382d3599fc7ccd2f8a065f097e..570e7c869768deced196013571e067ae9fbd8e57 100644
--- a/ebpfcat/ebpf.rst
+++ b/ebpfcat/ebpf.rst
@@ -47,15 +47,28 @@ for synchronization::
 Once attached, our little program will be executed each time a packet
 arrives on the interface. We can read the result in a loop::
 
-    for i in range(100):
+    for i in range(10):
         await sleep(0.1)
         print("packets arrived so far:", c.count)
 
+With ``attach`` the program is attached indefinitely on the interface,
+even beyond the end of the program. Use ``detach`` to detach it, or you
+may use the async contextmanager ``run`` to detach automatically, as in::
+
+   async with c.run("eth0"):
+        await sleep(1)
+        print("packets arrived so far:", c.count)
+
 Note that here we access the member variable ``count`` from user space.
 While generating EBPF, the code generator knows it needs to write out
 commands to access that variable from EBPF, once accessed outside of
 generation context, we access it from the user side.
 
+Both ``attach`` and ``detach`` have an additional parameter ``flags`` to
+choose in which mode to attach the program, use ``XDPFlags.SKB_MODE`` (the
+default) to use the generic kernel driver, or ``XDPFlags.DRV_MODE`` to let
+the interface device driver run the program.
+
 For reference, this is the full example:
 
 .. literalinclude:: /examples/count.py
@@ -158,11 +171,11 @@ race conditions.
 Fixed-point arithmetic
 ~~~~~~~~~~~~~~~~~~~~~~
 
-as a bonus beyond standard ebpf, we support fixed-point values as a type `x`.
+as a bonus beyond standard ebpf, we support fixed-point values as a type ``x``.
 Within ebpf they are calculated as per-10000, so a 0.2 is represented as
 20000. From outside, the variables seem to be doubles. Vaguely following
-Python, all true divisions `/` result in a fixed-point result, while all
-floor divisions `//` result in a standard integer. Some examples:
+Python, all true divisions ``/`` result in a fixed-point result, while all
+floor divisions ``//`` result in a standard integer. Some examples::
 
     class FixedPoint(EPBF):
         array_map = ArrayMap()
diff --git a/ebpfcat/xdp.py b/ebpfcat/xdp.py
index 7d462c9f2955c0644f5be16dd018e10352cc1d37..dee0e1e7ee1b3187f1c06405b71b20c5e85e360e 100644
--- a/ebpfcat/xdp.py
+++ b/ebpfcat/xdp.py
@@ -36,13 +36,19 @@ class XDPExitCode(Enum):
     REDIRECT = 4
 
 
+class XDPFlags(Enum):
+    SKB_MODE = 2
+    DRV_MODE = 4  # XDP done by the network driver
+
+
 class XDRFD(DatagramProtocol):
     """just implement enough of the NETLINK protocol to attach programs"""
 
-    def __init__(self, ifindex, fd, future):
+    def __init__(self, ifindex, fd, future, flags):
         self.ifindex = ifindex
         self.fd = fd
         self.seq = None
+        self.flags = flags
         self.future = future
 
     def connection_made(self, transport):
@@ -73,7 +79,7 @@ class XDRFD(DatagramProtocol):
                 self.fd,
                 8,
                 3,  # IFLA_XDP_FLAGS,
-                2)
+                self.flags.value)
         transport.sendto(p, (0, 0))
 
     def datagram_received(self, data, addr):
@@ -82,12 +88,15 @@ class XDRFD(DatagramProtocol):
             ln, type, flags, seq, pid = unpack("IHHII", data[pos : pos+16])
             if type == 3:  # DONE
                 self.future.set_result(0)
+                return
             elif type == 2:  # ERROR
                 errno, *args = unpack("iIHHII", data[pos+16 : pos+36])
                 if errno != 0:
-                    self.future.set_result(errno)
+                    self.future.set_exception(OSError(errno, os.strerror(-errno)))
+                    return
             if flags & 2 == 0:  # not a multipart message
                 self.future.set_result(0)
+                return
             pos += ln
 
 
@@ -149,32 +158,40 @@ class XDP(EBPF):
 
         self.packetSize = PacketSize(self)
 
-    async def _netlink(self, ifindex, fd):
+    async def _netlink(self, ifindex, fd, flags):
         future = Future()
         transport, proto = await get_event_loop().create_datagram_endpoint(
-                lambda: XDRFD(ifindex, fd, future),
+                lambda: XDRFD(ifindex, fd, future, flags),
                 family=AF_NETLINK, proto=NETLINK_ROUTE)
-        await future
-        transport.get_extra_info("socket").close()
+        try:
+            await future
+        finally:
+            transport.get_extra_info("socket").close()
 
-    async def attach(self, network):
+    async def attach(self, network, flags=XDPFlags.SKB_MODE):
         """attach this program to a `network`"""
         ifindex = if_nametoindex(network)
         fd, _ = self.load(log_level=1)
-        await self._netlink(ifindex, fd)
+        await self._netlink(ifindex, fd, flags)
 
-    async def detach(self, network):
+    async def detach(self, network, flags=XDPFlags.SKB_MODE):
         """attach this program from a `network`"""
         ifindex = if_nametoindex(network)
         await self._netlink(ifindex, -1)
 
     @asynccontextmanager
-    async def run(self, network):
+    async def run(self, network, flags=XDPFlags.SKB_MODE):
+        """attach this program to a `network` during context
+
+        attach this program to the `network` while the context
+        manager is running, and detach it afterwards."""
         ifindex = if_nametoindex(network)
         fd, _ = self.load(log_level=1)
-        await self._netlink(ifindex, fd)
-        os.close(fd)
+        try:
+            await self._netlink(ifindex, fd, flags)
+        finally:
+            os.close(fd)
         try:
             yield
         finally:
-            await self._netlink(ifindex, -1)
+            await self._netlink(ifindex, -1, flags)
diff --git a/examples/count.py b/examples/count.py
index fe7b478199382d2c6bed957ff8403cc4b728b4c0..be333ffe7d547d7b02d5946f5e2ea2d649359d94 100644
--- a/examples/count.py
+++ b/examples/count.py
@@ -1,6 +1,6 @@
 from asyncio import get_event_loop, sleep
 from ebpfcat.hashmap import HashMap
-from ebpfcat.xdp import XDP, XDPExitCode
+from ebpfcat.xdp import XDP, XDPExitCode, XDPFlags
 
 class Count(XDP):
     license = "GPL"
@@ -15,11 +15,10 @@ class Count(XDP):
 
 async def main():
     c = Count()
-    await c.attach("eth0")
-
-    for i in range(100):
-        await sleep(0.1)
-        print(c.count)
+    async with c.run("eth0", XDPFlags.DRV_MODE):
+        for i in range(10):
+            await sleep(0.1)
+            print(c.count)
 
 
 if __name__ == "__main__":