1 Reply
      Latest reply: Nov 4, 2016 3:37 AM by eskimo RSS
      ankitthakur_fico Level 1 Level 1 (0 points)

        I am having a struct object. And a method, whose input is payload. Now I am creating a mutableData named packet, and it's mutable bytes are referring to ICMPHeader struct.

         

         

        struct ICMPHeader {
            var type:UInt8
            var code:UInt8
            var checksum:UInt16
            var identifier:UInt16
            var sequenceNumber:UInt16
        };
        

         

        func createPacket(payload:NSData) -> NSData(){
            var packet:NSMutableData?
            var icmpPtr:ICMPHeader = ICMPHeader(type: 0, code: 0, checksum: 0, identifier: 0, sequenceNumber: 0)
            packet = NSMutableData(length: Int(MemoryLayout<ICMPHeader>.size + payload.length))
        
            if packet != nil {
        
               icmpPtr = packet!.mutableBytes.assumingMemoryBound(to: ICMPHeader.self).pointee
        
               icmpPtr.type = type
               icmpPtr.code = 0
               icmpPtr.checksum = 0
               icmpPtr.identifier = CFSwapInt16BigToHost(identifier)
               icmpPtr.sequenceNumber = CFSwapInt16HostToBig(identifier)
               memcpy(&icmpPtr + 1, payload.bytes, payload.length)
        
               if (requiresChecksum) {
                   icmpPtr.checksum = in_cksum(packet!.bytes, bufferLen: packet!.length);
               }
        
           }
           return packet
        }
        

         

        Mutable bytes are successfully getting binded to struct, and values are also getting updated in struct ICMPHeader.

         

        The issue is changing the values in struct is not changing the value of mutable data packet.

         

        And if, I am trying to recreate the packet after creating struct, then it is crashing.

        package = NSMutableData(bytes: unsafeBitCast(icmpPtr, to: UnsafeMutableRawPointer.self), length: Int(MemoryLayout<ICMPHeader>.size + payload.length))
        
        • Re: NSMutableData's bytes to UnsafeMutableRawPointer not updating the value of mutableData
          eskimo Apple Staff Apple Staff (6,505 points)

          The immediate cause of your problem is this:

          icmpPtr = packet!.mutableBytes.assumingMemoryBound(to: ICMPHeader.self).pointee
          

          icmpPtr is not a pointer, its a ICMPHeader value, and, as with all value types, changes to it do not get reflected elsewhere.

          However, there’s a bunch of other things I’d change in your code:

          • From what you posted it seems like you’re defining ICMPHeader in Swift.  That’s not good.  Swift makes no guarantees about how such a structure would be laid out.  You’ll have to define it in C (as shown below) and then import that into Swift.  Note my use of __Check_Compile_Time to check, at compile time, that the compiler has laid out the structure correctly.

          • Swift has had native endian swizzling for a while now.

          • In my experience Foundation value types are much easier to use than NSData.

          Here’s how I’d write this code:

          import Foundation
          
          func in_cksum(buffer: UnsafeBufferPointer<UInt8>) -> UInt16 {
              return 12345
          }
          
          func createPacket(payload: Data) -> Data {
              let type: UInt8 = 42
              let identifier: UInt16 = 666
          
              var result = Data(count: MemoryLayout<ICMPHeader>.size)
              result.withUnsafeMutableBytes { (icmpPtr: UnsafeMutablePointer<ICMPHeader>) in
                icmpPtr.pointee.type = type 
                icmpPtr.pointee.code = 0
                icmpPtr.pointee.checksum = 0 
                icmpPtr.pointee.identifier = identifier.bigEndian
                icmpPtr.pointee.sequenceNumber = identifier.bigEndian 
              }
              result.append(payload)
              let checksum = result.withUnsafeBytes { (base: UnsafePointer<UInt8>) in
                  return in_cksum(buffer: UnsafeBufferPointer(start: base, count: result.count))
              }
              result.withUnsafeMutableBytes { (icmpPtr: UnsafeMutablePointer<ICMPHeader>) in
                  icmpPtr.pointee.checksum = checksum
              }
          
              return result
          }
          

          Note I’m not byte swapping checksum because the traditional IP checksum implementation returns the value big endian anyway.

          Share and Enjoy

          Quinn “The Eskimo!”
          Apple Developer Relations, Developer Technical Support, Core OS/Hardware
          let myEmail = "eskimo" + "1" + "@apple.com"

          @import Darwin;
          
          #include <AssertMacros.h>
          
          struct ICMPHeader { 
              uint8_t type; 
              uint8_t code; 
              uint16_t checksum;
              uint16_t identifier;
              uint16_t sequenceNumber;
          };
          typedef struct ICMPHeader ICMPHeader;
          __Check_Compile_Time(sizeof(ICMPHeader) == 8);
          __Check_Compile_Time(offsetof(ICMPHeader, type) == 0);
          __Check_Compile_Time(offsetof(ICMPHeader, code) == 1);
          __Check_Compile_Time(offsetof(ICMPHeader, checksum) == 2);
          __Check_Compile_Time(offsetof(ICMPHeader, identifier) == 4);
          __Check_Compile_Time(offsetof(ICMPHeader, sequenceNumber) == 6);