Class: Dap::Filter::FilterDecodeNTPReply
- Inherits:
-
Object
- Object
- Dap::Filter::FilterDecodeNTPReply
- Includes:
- BaseDecoder
- Defined in:
- lib/dap/filter/udp.rb
Overview
Decode a NTP reply
Instance Attribute Summary
Attributes included from Base
Instance Method Summary collapse
Methods included from BaseDecoder
Methods included from Base
Instance Method Details
#decode(sdata) ⇒ Object
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 |
# File 'lib/dap/filter/udp.rb', line 461 def decode(sdata) info = {} return if sdata.length < 4 # Make a copy since our parser is destructive data = sdata.dup # TODO: all of this with bitstruct? # The format of the packet depends largely on the version, so extract just the version. # Fortunately the version is in the same place regardless of NTP protocol version -- # The 3rd-5th bits of the first byte of the response ntp_flags = data.slice!(0,1).unpack('C').first ntp_version = (ntp_flags & 0b00111000) >> 3 info['ntp.version'] = ntp_version info['ntp.mode'] = (ntp_flags & 0b00000111) # NTP 2 & 3 share a common header, so parse those together if ntp_version == 2 || ntp_version == 3 if info['ntp.mode'] == 7 # if it is mode 7, parse that: # 0 1 2 3 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # |R|M| VN | Mode|A| Sequence | Implementation| Req Code | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Err | Number of data items | MBZ | Size of data item | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # ... data ... return info if data.size < 8 info['ntp.response'] = ntp_flags >> 7 info['ntp.more'] = (ntp_flags & 0b01000000) >> 6 ntp_auth_seq, ntp_impl, ntp_rcode = data.slice!(0,3).unpack('C*') info['ntp.implementation'] = ntp_impl info['ntp.request_code'] = ntp_rcode mode7_data = data.slice!(0,4).unpack('n*') info['ntp.mode7.err'] = mode7_data.first >> 11 info['ntp.mode7.data_items_count'] = mode7_data.first & 0b0000111111111111 info['ntp.mode7.mbz'] = mode7_data.last >> 11 info['ntp.mode7.data_item_size'] = mode7_data.last & 0b0000111111111111 # extra monlist response data if ntp_rcode == 42 if info['ntp.mode7.data_item_size'] == 72 remote_addresses = [] local_addresses = [] idx = 0 1.upto(info['ntp.mode7.data_items_count']) do #u_int32 firsttime; /* first time we received a packet */ #u_int32 lasttime; /* last packet from this host */ #u_int32 restr; /* restrict bits (was named lastdrop) */ #u_int32 count; /* count of packets received */ #u_int32 addr; /* host address V4 style */ #u_int32 daddr; /* destination host address */ #u_int32 flags; /* flags about destination */ #u_short port; /* port number of last reception */ data_block = data[idx, 30] # Occasionally not all of data captured, need to defensively handle this case. if data_block firsttime,lasttime,restr,count,raddr,laddr,flags,dport = data_block.unpack("NNNNNNNn") # even if data_block is not nil, might not have all of the 30 bytes of data, so make sure # that remote and local address are non-nil. remote_addresses << [raddr].pack("N").unpack("C*").map{|x| x.to_s }.join(".") if raddr local_addresses << [laddr].pack("N").unpack("C*").map{|x| x.to_s }.join(".") if laddr idx += info['ntp.mode7.data_item_size'] else break end end info['ntp.monlist.remote_addresses'] = remote_addresses.join(' ') info['ntp.monlist.remote_addresses.count'] = remote_addresses.size info['ntp.monlist.local_addresses'] = local_addresses.join(' ') info['ntp.monlist.local_addresses.count'] = local_addresses.size end end elsif info['ntp.mode'] == 6 # control responses, supposedly. abort if there isn't enough to have an empty control response return info if data.size < 12 ntp_control_flags = data.slice!(0,1).unpack('C').first info["ntp.control.response"] = ntp_control_flags >> 7 info["ntp.control.error"] = (ntp_control_flags & 0b01000000) >> 6 info["ntp.control.more"] = (ntp_control_flags & 0b00100000) >> 5 info["ntp.control.opcode"] = (ntp_control_flags & 0b00011111) %w(seq status association_id offset count).each do |field| info["ntp.control.#{field}"] = data.slice!(0,2).unpack('n').first end data = data.slice(0,info["ntp.control.count"]) # decode readvar responses if info["ntp.control.opcode"] == 2 info["ntp.control.readvar"] = data data.strip! data.gsub!(/\r\n/, ' ') data.split(/, /).each do |pair| next unless pair =~ /=/ key, value = pair.split(/=/) if value value.gsub!(/^['"]/, '') value.gsub!(/['"]$/, '') end info["ntp.control.readvar.#{key}"] = value end else info["ntp.control.data"] = data end end elsif ntp_version == 4 info['ntp.leap_indicator'] = ntp_flags >> 6 info['ntp.peer.stratum'], info['ntp.peer.interval'], info['ntp.peer.precision'] = data.slice!(0,3).unpack('C*') info['ntp.root.delay'], info['ntp.root.dispersion'], info['ntp.ref_id'] = data.slice!(0,12).unpack('N*') info['ntp.timestamp.reference'], info['ntp.timestamp.origin'], info['ntp.timestamp.receive'], info['ntp.timestamp.transmit'] = data.slice!(0,32).unpack('Q*') end info end |