Tunnel ethernet over EVERYTHING!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

pytap.py 3.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. from fcntl import ioctl
  2. import subprocess
  3. import os
  4. import struct
  5. class TapDevice:
  6. """ TUN/TAP device class """
  7. # magic numbers and structlayout
  8. TUNSETIFF = 0x400454ca
  9. IFF_TUN = 0x0001
  10. IFF_TAP = 0x0002
  11. DEVPATH = "/dev/net/tun"
  12. _ifreq = "16sh"
  13. def __init__(self, name='', tap=True, conf=None, stripHeader=True):
  14. """ Constructor for the device.
  15. name - the device name, use a %d for a generated device numer
  16. tap - if this device should be a tap device
  17. conf - conf to pass to the ifconfig function of this class
  18. (if None ifconfig() won't be called)
  19. stripHeader - strips the first 4 bytes, they don't belong to
  20. the actual network traffic ("\x00\x00\x08\x00")
  21. """
  22. self._mode = (tap and self.IFF_TAP) or self.IFF_TUN
  23. self._fd = None
  24. self._nametpl = name
  25. self._tap = tap
  26. self._mac = None
  27. self._mtu = 1500
  28. self.conf = conf
  29. self._stripHeader = stripHeader
  30. if name == '':
  31. self._nametpl = (tap and "tap%d") or "tun%d"
  32. self._createDev()
  33. if self.conf:
  34. self.ifconfig(**conf)
  35. def _createDev(self):
  36. if self._fd:
  37. self.close()
  38. self._fd = os.open(self.DEVPATH, os.O_RDWR)
  39. ifreq = struct.pack(self._ifreq, self._nametpl, self._mode)
  40. ret = ioctl(self._fd, self.TUNSETIFF, ifreq)
  41. # retmode should be the same as self._mode
  42. (retname, retmode) = struct.unpack(self._ifreq, ret)
  43. self._name = retname.strip("\x00")
  44. def _ifconfig(self, params):
  45. args = ["/sbin/ifconfig"] + params
  46. ret = subprocess.Popen(args).wait()
  47. if ret != 0:
  48. raise PyTapException("Command '%s' did not return 0" % (" ".join(args),))
  49. def ifconfig(self, **kwargs):
  50. """ Calls ifconfig for the device.
  51. All arguments will be passed to ifconfig. Use 'address' for the device address.
  52. E.g. device.ifconfig(address="12.34.56.78", mtu=1500)
  53. """
  54. args = [self._name]
  55. if kwargs.has_key("address"):
  56. args.append(kwargs["address"])
  57. del(kwargs["address"])
  58. args = reduce(lambda l, key: l+[key, str(kwargs[key])],
  59. kwargs, args)
  60. self._ifconfig(args)
  61. def getMac(self):
  62. """ Get the device mac """
  63. # this "could" be buffered, but we never know who when changed the mac
  64. # ==> we re-get the mac on every request
  65. proc = subprocess.Popen("LC_ALL=C /sbin/ifconfig %s|head -n 1|egrep -o '([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}'" % self.getName(), shell=True, stdout=subprocess.PIPE)
  66. mac = proc.stdout.read().strip()
  67. return mac
  68. def up(self):
  69. """ Bring the device up """
  70. self._ifconfig([self._name, "up"])
  71. def down(self):
  72. """ Bring the device down """
  73. self._ifconfig([self._name, "down"])
  74. def getFD(self):
  75. """ Get the device file descriptor (e.g. to use in select()) """
  76. return self._fd
  77. def getName(self):
  78. """ Get the (real) name of the device """
  79. return self._name
  80. def read(self):
  81. """ Read a packet from the device """
  82. readSize = self._mtu
  83. if self._tap:
  84. # don't forget the ethernet frame (not included in MTU)
  85. readSize += 18
  86. data = os.read(self._fd, self._mtu)
  87. if self._stripHeader:
  88. data = data[4:]
  89. return data
  90. def write(self, data):
  91. """ Write a packet to the device """
  92. if self._stripHeader:
  93. data= "\x00\x00\x08\x00" + data
  94. os.write(self._fd, data)
  95. def close(self):
  96. """ Close the device """
  97. os.close(self._fd)
  98. self._fd = self._name = None
  99. class PyTapException(Exception):
  100. pass