# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.

"""
This is an example of using Twisted Pair's Linux tun/tap integration to receive
and react to ethernet, ip, and udp datagrams as if the example process were a
network device reachable via a system network interface.

Some system setup is required to successfully run this example.  As a
privileged user (in other words, root), these commands will create the
necessary tap network interface:

$ ip tuntap add dev tap0 mode tap user ${USER_ID}
$ ip link set dev tap0 up
$ ip neigh add ${IP_ADDRESS} lladdr ${MAC_ADDRESS} dev tap0
$ ip route add to ${IP_NETWORK} dev tap0

Once you've done this, sending UDP datagrams to ${IP_ADDRESS} on port 42 will
trigger a response from this example.

Use the UID of the user you want to be able to run the example for ${USER_ID}
and use something like 10.0.0.0/24 for ${IP_NETWORK}.

Invent any valid value for ${MAC_ADDRESS} (though avoid re-using an address
already in use on your network).

See the Twisted Pair configuration howto for more information about this
system-level setup.

When the tap device is configured, the example is running, and a UDP datagram
is sent to the right address, the example will display information about the
datagram on its standard out.
"""


from sys import stdout

from twisted.internet import protocol
from twisted.internet.defer import Deferred
from twisted.internet.task import react
from twisted.pair.ethernet import EthernetProtocol
from twisted.pair.ip import IPProtocol
from twisted.pair.rawudp import RawUDPProtocol
from twisted.pair.tuntap import TuntapPort
from twisted.python.log import startLogging


class MyProto(protocol.DatagramProtocol):
    def datagramReceived(self, datagram, address):
        print("from", address, "received", repr(datagram))


def main(reactor):
    startLogging(stdout, setStdout=False)
    udp = RawUDPProtocol()
    udp.addProto(42, MyProto())
    ip = IPProtocol()
    ip.addProto(17, udp)
    eth = EthernetProtocol()
    eth.addProto(0x800, ip)

    port = TuntapPort(interface="tap0", proto=eth, reactor=reactor)
    port.startListening()

    # Run forever
    return Deferred()


if __name__ == "__main__":
    react(main, [])
