diff --git a/python-scapy.pdf b/python-scapy.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..9eb98effa7195299229cd65c67d385dba77d99de
Binary files /dev/null and b/python-scapy.pdf differ
diff --git a/python-scapy.txt b/python-scapy.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e1fb327aad30a2409b28532974f1ea6f62a50832
--- /dev/null
+++ b/python-scapy.txt
@@ -0,0 +1,187 @@
+Python Scapy Quick-Reference
+============================
+
+The scapy module is extremely useful, but the documentation is
+somewhat lacking. Consequently, here is a simple cookbook of handy
+scapy recipes.
+
+Importing Scapy
+---------------
+
+Unlike most modules, scapy requires a global import to be useful:
+
+    from scapy.all import *
+
+This imports all exported symbols from the scapy.all submodule into
+the global namespace. The rest of our examples assume your program
+has done this.
+
+Reading and Writing Packet Capture Files
+----------------------------------------
+
+Your life will be easiest if all of your captures are in pure pcap
+format, not a format like pcap-ng. Wireshark, tshark, and dumpcap
+will *generally* produce pcap, unless you capture on all interfaces,
+in which case you will get pcap-ng files. That is, unless you
+override the default behavior. For dumpcap, which is our recommended
+way to capture packets (when feasible), the `-P` option will force
+normal pcap output.
+
+Having said all that:
+
+    frames = rdpcap('file.cap')
+
+This opens a pcap file named `file.cap` for reading, and returns
+an iterable, which we've called `frames`, because it potentially
+contains layer-2 frames, rather than layer-3 packets. That will
+depend on the capture file, however.
+
+We can iterate over these as follows:
+
+    for f in frames:
+        pass
+
+This loop does noting (`pass` is a nop in python).
+
+To write packets to a file, we would call:
+
+    wrpcap('outfile.pcap', pkts)
+
+`pkts` may be packets or frames, and should be an iterable, such
+as a list.
+
+
+Dissecting Packets and Frames
+-----------------------------
+
+Scapy stores everything in dict-like objects, which is handy. The
+objects are actually built as a series of *layers*. Consider:
+
+    for f in frames:
+        if IP not in f:
+            continue
+        pkt = f[IP]
+
+First, we verify that there's an IP layer in this object, and if
+not we skip to the next one. Then we get the IP layer of `f`, which
+may be identically `f` or it may be a layer (at any depth).
+
+We can also do more complex things, skipping over layers we don't care
+about:
+
+    for f in frames:
+        if DNS not in f:
+            continue
+        d = f[DNS]
+
+This might be a DNS layer within a UDP layer within an IP layer
+within an Ether layer. The nice thing is that we don't have to care.
+
+At a given layer, there are a number of *fields* that we can access:
+
+    for f in frames:
+        if DNS not in f:
+            continue
+        d = f[DNS]
+        d.opcode
+
+The easiest way to get a feel for what's in a scapy object is
+to call the `display` method:
+
+    for f in frames:
+        f.display()
+        if DNS in f:
+            f[DNS].display()
+
+The first call will print (to stdout) all of the layers, including
+their fields, while the second will only print information about
+the DNS layer.
+
+
+Creating Scapy Objects
+----------------------
+
+Scapy has fairly normal constructors:
+
+    pkt = IP(dst='1.2.3.4')
+
+It also has a layering operator:
+
+    pkt = IP(dst='1.2.3.4')
+    udp = UDP(dport=123)
+    p = pkt/udp
+    pkt.display()
+    udp.display()
+    p.display()
+
+We can simplify this:
+
+    pkt = IP(dst='1.2.3.4')
+    udp = UDP(dport=123)
+    pkt /= udp
+    pkt.display()
+
+We can even simplify it further:
+
+    pkt = IP(dst='1.2.3.4')/UDP(dport=123)
+    pkt.display()
+
+Here are some layers that might interest you:
+
+ * `Ether`
+ * `IP`
+ * `ICMP`
+ * `UDP`
+ * `TCP`
+ * `DNS`
+ * `DNSQR`
+ * `DNSRR`
+ * `Raw`
+
+What's this `Raw` layer? It's literally a raw sequence of bytes:
+
+    pkt = IP(dst='1.2.3.4')/UDP(dport=123)/Raw('This is a test')
+    pkt.display()
+
+This just wraps the bytes in an appropriate scapy layer object, and
+we can shorten this:
+
+    pkt = IP(dst='1.2.3.4')/UDP(dport=123)/'This is a test'
+    pkt.display()
+
+We can also nest things further:
+
+    pkt = IP(dst='1.2.3.4')/UDP(dport=123)/IP(
+           src='2.3.4.5',dst='3.4.5.6')/ICMP()/'This is a test'
+    pkt.display()
+
+
+Sending and Receiving Packets
+-----------------------------
+
+Finally, how do we connect to the actual network, rather than just
+working with files?
+
+If you're creating packets at layer 3 (that is, starting from the
+IP layer), you can just call:
+
+    send(pkt)
+
+You can send and then wait for a response, as well:
+
+    new_pkt = sr1(pkt)
+
+Here, `new_pkt` is the response received.
+
+To just receive packets from an interface:
+
+    def my_callback(pkt):
+        pass
+    
+    sniff(iface=None, count=0, prn=my_callback)
+
+Specifying `None` for iface (the default) captures on all interfaces.
+A count of 0 (the default) captures forever; nonzero will stop after
+that number of packets have been received. See `help(sniff)` for
+more parameters, including filters.
+