home *** CD-ROM | disk | FTP | other *** search
/ Hackers Magazine 57 / CdHackersMagazineNr57.iso / Software / Networking / nmap-5.00-setup.exe / nselib / packet.lua < prev    next >
Text File  |  2009-07-06  |  21KB  |  583 lines

  1. --- Facilities for manipulating raw packets.
  2. -- @author Marek Majkowski <majek04+nse@gmail.com>
  3. -- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html
  4.  
  5. module(... or "packet" ,package.seeall)
  6.  
  7. require "bit"
  8.  
  9.  
  10. ----------------------------------------------------------------------------------------------------------------
  11. --- Get an 8-bit integer at a 0-based byte offset in a byte string.
  12. -- @param b A byte string.
  13. -- @param i Offset.
  14. -- @return An 8-bit integer.
  15. function u8(b, i)
  16.         return string.byte(b, i+1)
  17. end
  18. --- Get a 16-bit integer at a 0-based byte offset in a byte string.
  19. -- @param b A byte string.
  20. -- @param i Offset.
  21. -- @return A 16-bit integer.
  22. function u16(b, i)
  23.         local b1,b2
  24.         b1, b2 = string.byte(b, i+1), string.byte(b, i+2)
  25.         --        2^8     2^0
  26.         return b1*256 + b2
  27. end
  28. --- Get a 32-bit integer at a 0-based byte offset in a byte string.
  29. -- @param b A byte string.
  30. -- @param i Offset.
  31. -- @return A 32-bit integer.
  32. function u32(b,i)
  33.         local b1,b2,b3,b4
  34.         b1, b2 = string.byte(b, i+1), string.byte(b, i+2)
  35.         b3, b4 = string.byte(b, i+3), string.byte(b, i+4)
  36.         --        2^24          2^16       2^8     2^0
  37.         return b1*16777216 + b2*65536 + b3*256 + b4
  38. end
  39.  
  40. --- Set an 8-bit integer at a 0-based byte offset in a byte string
  41. -- (big-endian).
  42. -- @param b A byte string.
  43. -- @param i Offset.
  44. -- @param num Integer to store.
  45. function set_u8(b, i, num)
  46.     local s = string.char(bit.band(num, 0xff))
  47.     return b:sub(0+1, i+1-1) .. s .. b:sub(i+1+1)
  48. end
  49. --- Set a 16-bit integer at a 0-based byte offset in a byte string
  50. -- (big-endian).
  51. -- @param b A byte string.
  52. -- @param i Offset.
  53. -- @param num Integer to store.
  54. function set_u16(b, i, num)
  55.     local s = string.char(bit.band(bit.rshift(num, 8), 0xff)) .. string.char(bit.band(num, 0xff))
  56.     return b:sub(0+1, i+1-1) .. s .. b:sub(i+1+2)
  57. end
  58. --- Set a 32-bit integer at a 0-based byte offset in a byte string
  59. -- (big-endian).
  60. -- @param b A byte string.
  61. -- @param i Offset.
  62. -- @param num Integer to store.
  63. function set_u32(b,i, num)
  64.     local s = string.char(bit.band(bit.rshift(num,24), 0xff)) ..
  65.         string.char(bit.band(bit.rshift(num,16), 0xff)) ..
  66.         string.char(bit.band(bit.rshift(num,8), 0xff)) ..
  67.         string.char(bit.band(num, 0xff))
  68.     return b:sub(0+1, i+1-1) .. s .. b:sub(i+1+4)
  69. end
  70.  
  71.  
  72. --- Calculate a standard Internet checksum.
  73. -- @param b Data to checksum.
  74. -- @return Checksum.
  75. function in_cksum(b)
  76.     local sum = 0
  77.     local c
  78.     local x = b
  79.  
  80.     while x:len() > 1 do
  81.         c = x:sub(1,2)
  82.         x = x:sub(3)
  83.         sum = sum + u16(c, 0)
  84.     end
  85.  
  86.     sum = bit.rshift(sum, 16) + bit.band(sum, 0xffff)
  87.     sum = sum + bit.rshift(sum, 16)
  88.     sum = bit.bnot(sum)
  89.     sum = bit.band(sum, 0xffff) -- trunctate to 16 bits
  90.     return sum
  91. end
  92.  
  93. -- ip protocol field
  94. IPPROTO_IP   = 0                   --  Dummy protocol for TCP
  95. IPPROTO_ICMP = 1                 --  Internet Control Message Protocol
  96. IPPROTO_IGMP = 2                 --  Internet Group Management Protocol
  97. IPPROTO_IPIP = 4                 --  IPIP tunnels (older KA9Q tunnels use 94)
  98. IPPROTO_TCP  = 6                  --  Transmission Control Protocol
  99. IPPROTO_EGP  = 8                  --  Exterior Gateway Protocol
  100. IPPROTO_PUP  = 12                 --  PUP protocol
  101. IPPROTO_UDP  = 17                 --  User Datagram Protocol
  102. IPPROTO_IDP  = 22                 --  XNS IDP protocol
  103. IPPROTO_DCCP = 33                --  Datagram Congestion Control Protocol
  104. IPPROTO_RSVP = 46                --  RSVP protocol
  105. IPPROTO_GRE  = 47                 --  Cisco GRE tunnels (rfc 1701,1702)
  106. IPPROTO_IPV6 = 41              --  IPv6-in-IPv4 tunnelling
  107.  
  108. IPPROTO_ESP  = 50                --  Encapsulation Security Payload protocol
  109. IPPROTO_AH   = 51                 --  Authentication Header protocol
  110. IPPROTO_BEETPH  = 94             --  IP option pseudo header for BEET
  111. IPPROTO_PIM     = 103             --  Protocol Independent Multicast
  112.  
  113. IPPROTO_COMP    = 108            --  Compression Header protocol
  114. IPPROTO_SCTP    = 132             --  Stream Control Transport Protocol
  115. IPPROTO_UDPLITE = 136            --  UDP-Lite (RFC 3828)
  116.  
  117.  
  118. ----------------------------------------------------------------------------------------------------------------
  119. -- Packet is a class
  120. Packet = {}
  121.  
  122. --- Create a new Packet object.
  123. -- @param packet Binary string with packet data.
  124. -- @param packet_len Packet length. It could be more than
  125. -- <code>string.len(packet)</code>.
  126. -- @param force_continue whether an error in parsing headers should be fatal or
  127. -- not. This is especially useful when parsing ICMP packets, where a small ICMP
  128. -- payload could be a TCP header. The problem is that parsing this payload
  129. -- normally would fail because the TCP header is too small.
  130. -- @return A new Packet.
  131. function Packet:new(packet, packet_len, force_continue)
  132.     local o = setmetatable({}, {__index = Packet})
  133.     o.buf        = packet
  134.     o.packet_len    = packet_len
  135.     if not o:ip_parse(force_continue) then
  136.         return nil
  137.     end
  138.     if o.ip_p == IPPROTO_TCP then
  139.         if not o:tcp_parse(force_continue) then
  140.             io.write("Error while parsing TCP packet\n")
  141.         end
  142.     elseif o.ip_p == IPPROTO_ICMP then
  143.         if not o:icmp_parse(force_continue) then
  144.             io.write("Error while parsing ICMP packet\n")
  145.         end
  146.     end
  147.     return o
  148. end
  149.  
  150. -- Helpers
  151.  
  152.  
  153. --- Convert a dotted-quad IP address string (like <code>"1.2.3.4"</code>) to a
  154. -- raw string four bytes long.
  155. -- @param str IP address string.
  156. -- @return Four-byte string.
  157. function iptobin(str)
  158.     local ret = ""
  159.         for c in string.gmatch(str, "[0-9]+") do
  160.                 ret = ret .. string.char(c+0) -- automatic conversion to int
  161.         end
  162.     return ret
  163. end
  164. --- Convert a four-byte raw string to a dotted-quad IP address string.
  165. -- @param raw_ip_addr Four-byte string.
  166. -- @return IP address string.
  167. function toip(raw_ip_addr)
  168.     if not raw_ip_addr then
  169.         return "?.?.?.?"
  170.     end
  171.     return string.format("%i.%i.%i.%i", string.byte(raw_ip_addr,1,4))
  172. end
  173. --- Get an 8-bit integer at a 0-based byte offset in the packet.
  174. -- @param index Offset.
  175. -- @return An 8-bit integer.
  176. function Packet:u8(index)
  177.         return u8(self.buf, index)
  178. end
  179. --- Get a 16-bit integer at a 0-based byte offset in the packet.
  180. -- @param index Offset.
  181. -- @return A 16-bit integer.
  182. function Packet:u16(index)
  183.         return u16(self.buf, index)
  184. end
  185. --- Get a 32-bit integer at a 0-based byte offset in the packet.
  186. -- @param index Offset.
  187. -- @return An 32-bit integer.
  188. function Packet:u32(index)
  189.         return u32(self.buf, index)
  190. end
  191. --- Return part of the packet contents as a byte string.
  192. -- @param index The beginning of the part of the packet to extract.
  193. -- @param length The length of the part of the packet to extract.
  194. -- @return A string.
  195. function Packet:raw(index, length)
  196.         return string.char(string.byte(self.buf, index+1, index+1+length-1))
  197. end
  198.  
  199. --- Set an 8-bit integer at a 0-based byte offset in the packet.
  200. -- (big-endian).
  201. -- @param index Offset.
  202. -- @param num Integer to store.
  203. function Packet:set_u8(index, num)
  204.         self.buf = set_u8(self.buf, index, num)
  205.         return self.buf
  206. end
  207. --- Set a 16-bit integer at a 0-based byte offset in the packet.
  208. -- (big-endian).
  209. -- @param index Offset.
  210. -- @param num Integer to store.
  211. function Packet:set_u16(index, num)
  212.         self.buf = set_u16(self.buf, index, num)
  213.         return self.buf
  214. end
  215. --- Set a 32-bit integer at a 0-based byte offset in the packet.
  216. -- (big-endian).
  217. -- @param index Offset.
  218. -- @param num Integer to store.
  219. function Packet:set_u32(index, num)
  220.         self.buf = set_u32(self.buf, index, num)
  221.         return self.buf
  222. end
  223.  
  224. --- Parse an IP packet header.
  225. -- @param force_continue Ignored.
  226. -- @return Whether the parsing succeeded.
  227. function Packet:ip_parse(force_continue)
  228.     self.ip_offset        = 0
  229.     if    string.len(self.buf) < 20 then     -- too short
  230.         return false
  231.     end
  232.     self.ip_v        = bit.rshift(bit.band(self:u8(self.ip_offset + 0), 0xF0), 4)
  233.     self.ip_hl        =            bit.band(self:u8(self.ip_offset + 0), 0x0F)        -- header_length or data_offset
  234.     if    self.ip_v ~= 4 then     -- not ip
  235.         return false
  236.     end
  237.     self.ip = true
  238.     self.ip_tos        = self:u8(self.ip_offset + 1)
  239.     self.ip_len        = self:u16(self.ip_offset + 2)
  240.     self.ip_id        = self:u16(self.ip_offset + 4)
  241.     self.ip_off        = self:u16(self.ip_offset + 6)
  242.     self.ip_rf        = bit.band(self.ip_off, 0x8000)~=0        -- true/false
  243.     self.ip_df        = bit.band(self.ip_off, 0x4000)~=0
  244.     self.ip_mf        = bit.band(self.ip_off, 0x2000)~=0
  245.     self.ip_off        = bit.band(self.ip_off, 0x1FFF)        -- fragment offset
  246.     self.ip_ttl        = self:u8(self.ip_offset + 8)
  247.     self.ip_p        = self:u8(self.ip_offset + 9)
  248.     self.ip_sum        = self:u16(self.ip_offset + 10)
  249.     self.ip_bin_src        = self:raw(self.ip_offset + 12,4)    -- raw 4-bytes string
  250.     self.ip_bin_dst        = self:raw(self.ip_offset + 16,4)
  251.     self.ip_src        = toip(self.ip_bin_src)        -- formatted string
  252.     self.ip_dst        = toip(self.ip_bin_dst)
  253.     self.ip_opt_offset    = self.ip_offset + 20
  254.     self.ip_options        = self:parse_options(self.ip_opt_offset, ((self.ip_hl*4)-20))
  255.     self.ip_data_offset    = self.ip_offset + self.ip_hl*4
  256.     return true
  257. end
  258. --- Set the header length field.
  259. function Packet:ip_set_hl(len)
  260.     self:set_u8(self.ip_offset + 0, bit.bor(bit.lshift(self.ip_v, 4), bit.band(len, 0x0F)))
  261.     self.ip_v        = bit.rshift(bit.band(self:u8(self.ip_offset + 0), 0xF0), 4)
  262.     self.ip_hl        =            bit.band(self:u8(self.ip_offset + 0), 0x0F)        -- header_length or data_offset
  263. end
  264. --- Set the packet length field.
  265. -- @param len Packet length.
  266. function Packet:ip_set_len(len)
  267.     self:set_u16(self.ip_offset + 2, len)
  268. end
  269. --- Set the TTL.
  270. -- @param ttl TTL.
  271. function Packet:ip_set_ttl(ttl)
  272.     self:set_u8(self.ip_offset + 8, ttl)
  273. end
  274. --- Set the checksum.
  275. -- @param checksum Checksum.
  276. function Packet:ip_set_checksum(checksum)
  277.     self:set_u16(self.ip_offset + 10, checksum)
  278. end
  279. --- Count checksum for packet and save it.
  280. function Packet:ip_count_checksum()
  281.     self:ip_set_checksum(0)
  282.     local csum = in_cksum( self.buf:sub(0, self.ip_offset + self.ip_hl*4)  )
  283.     self:ip_set_checksum(csum)
  284. end
  285. --- Set the source IP address.
  286. -- @param binip The source IP address as a byte string.
  287. function Packet:ip_set_bin_src(binip)
  288.     nrip = u32(binip, 0)
  289.     self:set_u32(self.ip_offset + 12, nrip)
  290.     self.ip_bin_src        = self:raw(self.ip_offset + 12,4)    -- raw 4-bytes string
  291. end
  292. --- Set the destination IP address.
  293. -- @param binip The destination IP address as a byte string.
  294. function Packet:ip_set_bin_dst(binip)
  295.     nrip = u32(binip, 0)
  296.     self:set_u32(self.ip_offset + 16, nrip)
  297.     self.ip_bin_dst        = self:raw(self.ip_offset + 16,4)
  298. end
  299. --- Set the IP options field (and move the data, count new length,
  300. -- etc.).
  301. -- @param ipoptions IP options.
  302. function Packet:ip_set_options(ipoptions)
  303.     -- packet = <ip header> + ipoptions + <payload>
  304.     local buf = self.buf:sub(0+1,self.ip_offset + 20) .. ipoptions .. self.buf:sub(self.ip_data_offset+1)
  305.     self.buf = buf
  306.     -- set ip_len
  307.     self:ip_set_len(self.buf:len())
  308.     -- set ip_hl
  309.     self:ip_set_hl(5 + ipoptions:len()/4)
  310.     -- set data offset correctly
  311.     self.ip_options        = self:parse_options(self.ip_opt_offset, ((self.ip_hl*4)-20))
  312.     self.ip_data_offset    = self.ip_offset + self.ip_hl*4
  313.     if self.tcp then
  314.         self.tcp_offset        = self.ip_data_offset
  315.     elseif self.icmp then
  316.         self.icmp_offset    = self.ip_data_offset
  317.     end
  318. end
  319.  
  320. --- Get a short string representation of the IP header.
  321. -- @return A string representation of the IP header.
  322. function Packet:ip_tostring()
  323.     return string.format(
  324.         "IP %s -> %s",
  325.         self.ip_src,
  326.         self.ip_dst)
  327. end
  328.  
  329. --- Parse IP/TCP options into a table.
  330. -- @param offset Offset at which options start.
  331. -- @param length Length of options.
  332. -- @return Table of options.
  333. function Packet:parse_options(offset, length)
  334.     local options = {}
  335.     local op = 1
  336.     local opt_ptr = 0
  337.     while opt_ptr < length do
  338.         local t, l, d
  339.         options[op] = {}
  340.  
  341.         t = self:u8(offset + opt_ptr)
  342.         options[op].type = t
  343.         if t==0 or t==1 then
  344.             l = 1
  345.             d = nil
  346.         else
  347.             l = self:u8(offset + opt_ptr + 1)
  348.             if l > 2 then
  349.             d = self:raw(offset + opt_ptr + 2, l-2)
  350.             end
  351.         end
  352.         options[op].len  = l
  353.         options[op].data = d
  354.         opt_ptr = opt_ptr + l
  355.         op = op + 1
  356.     end
  357.     return options
  358. end
  359.  
  360. --- Get a short string representation of the packet.
  361. -- @return A string representation of the packet.
  362. function Packet:tostring()
  363.     if self.tcp then
  364.         return self:tcp_tostring()
  365.     elseif self.icmp then
  366.         return self:icmp_tostring()
  367.     elseif self.ip then
  368.         return self:ip_tostring()
  369.     end
  370.     return "<no tostring!>"
  371. end
  372.  
  373. ----------------------------------------------------------------------------------------------------------------
  374. --- Parse an ICMP packet header.
  375. -- @param force_continue Ignored.
  376. -- @return Whether the parsing succeeded.
  377. function Packet:icmp_parse(force_continue)
  378.     self.icmp_offset    = self.ip_data_offset
  379.     if string.len(self.buf) < self.icmp_offset + 8 then -- let's say 8 bytes minimum
  380.         return false
  381.     end
  382.     self.icmp = true
  383.     self.icmp_type        = self:u8(self.icmp_offset + 0)
  384.     self.icmp_code        = self:u8(self.icmp_offset + 1)
  385.     self.icmp_sum        = self:u16(self.icmp_offset + 2)
  386.  
  387.     if self.icmp_type == 3 or self.icmp_type == 4 or self.icmp_type == 11 or self.icmp_type == 12 then
  388.         self.icmp_payload = true
  389.         self.icmp_r0      = self:u32(self.icmp_offset + 4)
  390.         self.icmp_payload_offset = self.icmp_offset + 8
  391.         if string.len(self.buf) < self.icmp_payload_offset + 24 then
  392.             return false
  393.         end
  394.         self.icmp_payload = Packet:new(self.buf:sub(self.icmp_payload_offset+1), self.packet_len - self.icmp_payload_offset, true)
  395.     end
  396.     return true
  397. end
  398. --- Get a short string representation of the ICMP header.
  399. -- @return A string representation of the ICMP header.
  400. function Packet:icmp_tostring()
  401.     return self:ip_tostring() .. " ICMP(" .. self.icmp_payload:tostring() .. ")"
  402. end
  403.  
  404. ----------------------------------------------------------------------------------------------------------------
  405. -- Parse a TCP packet header.
  406. -- @param force_continue Whether a short packet causes parsing to fail.
  407. -- @return Whether the parsing succeeded.
  408. function Packet:tcp_parse(force_continue)
  409.     self.tcp = true
  410.     self.tcp_offset        = self.ip_data_offset
  411.     if string.len(self.buf) < self.tcp_offset + 4 then
  412.         return false
  413.     end
  414.     self.tcp_sport        = self:u16(self.tcp_offset + 0)
  415.     self.tcp_dport        = self:u16(self.tcp_offset + 2)
  416.     if string.len(self.buf) < self.tcp_offset + 20 then
  417.         if force_continue then
  418.             return true
  419.         else
  420.             return false
  421.         end
  422.     end
  423.     self.tcp_seq        = self:u32(self.tcp_offset + 4)
  424.     self.tcp_ack        = self:u32(self.tcp_offset + 8)
  425.     self.tcp_hl        = bit.rshift(bit.band(self:u8(self.tcp_offset+12), 0xF0), 4)    -- header_length or data_offset
  426.     self.tcp_x2        =            bit.band(self:u8(self.tcp_offset+12), 0x0F)
  427.     self.tcp_flags        = self:u8(self.tcp_offset + 13)
  428.     self.tcp_th_fin        = bit.band(self.tcp_flags, 0x01)~=0        -- true/false
  429.     self.tcp_th_syn        = bit.band(self.tcp_flags, 0x02)~=0
  430.     self.tcp_th_rst        = bit.band(self.tcp_flags, 0x04)~=0
  431.     self.tcp_th_push    = bit.band(self.tcp_flags, 0x08)~=0
  432.     self.tcp_th_ack        = bit.band(self.tcp_flags, 0x10)~=0
  433.     self.tcp_th_urg        = bit.band(self.tcp_flags, 0x20)~=0
  434.     self.tcp_th_ece        = bit.band(self.tcp_flags, 0x40)~=0
  435.     self.tcp_th_cwr        = bit.band(self.tcp_flags, 0x80)~=0
  436.     self.tcp_win        = self:u16(self.tcp_offset + 14)
  437.     self.tcp_sum        = self:u16(self.tcp_offset + 16)
  438.     self.tcp_urp        = self:u16(self.tcp_offset + 18)
  439.     self.tcp_opt_offset    = self.tcp_offset + 20
  440.     self.tcp_options    = self:parse_options(self.tcp_opt_offset, ((self.tcp_hl*4)-20))
  441.     self.tcp_data_offset    = self.tcp_offset + self.tcp_hl*4
  442.     self.tcp_data_length    = self.ip_len - self.tcp_offset - self.tcp_hl*4
  443.         self:tcp_parse_options()
  444.     return true
  445. end
  446.  
  447. --- Get a short string representation of the TCP packet.
  448. -- @return A string representation of the ICMP header.
  449. function Packet:tcp_tostring()
  450.     return string.format(
  451.         "TCP %s:%i -> %s:%i",
  452.         self.ip_src, self.tcp_sport,
  453.         self.ip_dst, self.tcp_dport
  454.         )
  455. end
  456.  
  457. --- Parse options for TCP header.
  458. function Packet:tcp_parse_options()
  459.         local eoo = false
  460.     for _,opt in ipairs(self.tcp_options) do
  461.                 if eoo then
  462.                         self.tcp_opt_after_eol = true
  463.                 end
  464.  
  465.         if      opt.type == 0 then    -- end of options
  466.                         eoo = true
  467.                 elseif     opt.type == 2 then    -- MSS
  468.                         self.tcp_opt_mss = u16(opt.data, 0)
  469.                         self.tcp_opt_mtu = self.tcp_opt_mss + 40
  470.         elseif    opt.type == 3 then     -- widow scaling
  471.                         self.tcp_opt_ws  = u8(opt.data, 0)
  472.         elseif    opt.type == 8 then     -- timestamp
  473.                         self.tcp_opt_t1 = u32(opt.data, 0)
  474.                         self.tcp_opt_t2 = u32(opt.data, 4)
  475.         end
  476.     end
  477. end
  478.  
  479. --- Set the TCP source port.
  480. -- @param port Source port.
  481. function Packet:tcp_set_sport(port)
  482.     self:set_u16(self.tcp_offset + 0, port)
  483. end
  484. --- Set the TCP destination port.
  485. -- @param port Destination port.
  486. function Packet:tcp_set_dport(port)
  487.     self:set_u16(self.tcp_offset + 2, port)
  488. end
  489. --- Set the TCP sequence field.
  490. -- @param new_seq Sequence.
  491. function Packet:tcp_set_seq(new_seq)
  492.     self:set_u32(self.tcp_offset + 4, new_seq)
  493. end
  494. --- Set the TCP flags field (like SYN, ACK, RST).
  495. -- @param new_flags Flags, represented as an 8-bit number.
  496. function Packet:tcp_set_flags(new_flags)
  497.     self:set_u8(self.tcp_offset + 13, new_flags)
  498. end
  499. --- Set the urgent pointer field.
  500. -- @param urg_ptr Urgent pointer.
  501. function Packet:tcp_set_urp(urg_ptr)
  502.     self:set_u16(self.tcp_offset + 18, urg_ptr)
  503. end
  504. --- Set the TCP checksum field.
  505. -- @param checksum Checksum.
  506. function Packet:tcp_set_checksum(checksum)
  507.     self:set_u16(self.tcp_offset + 16, checksum)
  508. end
  509. --- Count and save the TCP checksum field.
  510. function Packet:tcp_count_checksum()
  511.     self:tcp_set_checksum(0)
  512.     local proto    = self.ip_p
  513.     local length    = self.buf:len() - self.tcp_offset
  514.     local b = self.ip_bin_src ..
  515.         self.ip_bin_dst ..
  516.         string.char(0) ..
  517.         string.char(proto) ..
  518.         set_u16("..", 0, length) ..
  519.         self.buf:sub(self.tcp_offset+1)
  520.  
  521.     self:tcp_set_checksum(in_cksum(b))
  522. end
  523.  
  524. --- Map an MTU to a link type string. Stolen from p0f.
  525. -- @return A string describing the link type.
  526. function Packet:tcp_lookup_link()
  527.         local mtu_def = {
  528.             {["mtu"]=256,   ["txt"]= "radio modem"},
  529.             {["mtu"]=386,   ["txt"]= "ethernut"},
  530.             {["mtu"]=552,   ["txt"]= "SLIP line / encap ppp"},
  531.             {["mtu"]=576,   ["txt"]= "sometimes modem"},
  532.             {["mtu"]=1280,  ["txt"]= "gif tunnel"},
  533.             {["mtu"]=1300,  ["txt"]= "PIX, SMC, sometimes wireless"},
  534.             {["mtu"]=1362,  ["txt"]= "sometimes DSL (1)"},
  535.             {["mtu"]=1372,  ["txt"]= "cable modem"},
  536.             {["mtu"]=1400,  ["txt"]= "(Google/AOL)"},
  537.             {["mtu"]=1415,  ["txt"]= "sometimes wireless"},
  538.             {["mtu"]=1420,  ["txt"]= "GPRS, T1, FreeS/WAN"},
  539.             {["mtu"]=1423,  ["txt"]= "sometimes cable"},
  540.             {["mtu"]=1440,  ["txt"]= "sometimes DSL (2)"},
  541.             {["mtu"]=1442,  ["txt"]= "IPIP tunnel"},
  542.             {["mtu"]=1450,  ["txt"]= "vtun"},
  543.             {["mtu"]=1452,  ["txt"]= "sometimes DSL (3)"},
  544.             {["mtu"]=1454,  ["txt"]= "sometimes DSL (4)"},
  545.             {["mtu"]=1456,  ["txt"]= "ISDN ppp"},
  546.             {["mtu"]=1458,  ["txt"]= "BT DSL (?)"},
  547.             {["mtu"]=1462,  ["txt"]= "sometimes DSL (5)"},
  548.             {["mtu"]=1470,  ["txt"]= "(Google 2)"},
  549.             {["mtu"]=1476,  ["txt"]= "IPSec/GRE"},
  550.             {["mtu"]=1480,  ["txt"]= "IPv6/IPIP"},
  551.             {["mtu"]=1492,  ["txt"]= "pppoe (DSL)"},
  552.             {["mtu"]=1496,  ["txt"]= "vLAN"},
  553.             {["mtu"]=1500,  ["txt"]= "ethernet/modem"},
  554.             {["mtu"]=1656,  ["txt"]= "Ericsson HIS"},
  555.             {["mtu"]=2024,  ["txt"]= "wireless/IrDA"},
  556.             {["mtu"]=2048,  ["txt"]= "Cyclom X.25 WAN"},
  557.             {["mtu"]=2250,  ["txt"]= "AiroNet wireless"},
  558.             {["mtu"]=3924,  ["txt"]= "loopback"},
  559.             {["mtu"]=4056,  ["txt"]= "token ring (1)"},
  560.             {["mtu"]=4096,  ["txt"]= "Sangoma X.25 WAN"},
  561.             {["mtu"]=4352,  ["txt"]= "FDDI"},
  562.             {["mtu"]=4500,  ["txt"]= "token ring (2)"},
  563.             {["mtu"]=9180,  ["txt"]= "FORE ATM"},
  564.             {["mtu"]=16384, ["txt"]= "sometimes loopback (1)"},
  565.             {["mtu"]=16436, ["txt"]= "sometimes loopback (2)"},
  566.             {["mtu"]=18000, ["txt"]= "token ring x4"},
  567.             }
  568.         if not self.tcp_opt_mss or self.tcp_opt_mss==0 then
  569.                 return "unspecified"
  570.         end
  571.         for _,x in ipairs(mtu_def) do
  572.                 local mtu = x["mtu"]
  573.                 local txt = x["txt"]
  574.                 if self.tcp_opt_mtu == mtu then
  575.                         return txt
  576.                 end
  577.                 if self.tcp_opt_mtu < mtu then
  578.                         return string.format("unknown-%i", self.tcp_opt_mtu)
  579.                 end
  580.         end
  581.         return string.format("unknown-%i", self.tcp_opt_mtu)
  582. end
  583.