// File: yahoo[P]UDPing/src/protocol.cc
// Function: buildFrame(frame*, unsigned char*, unsigned char*, unsigned int, unsigned int, int, int, char const*, int, int)
int buildFrame (struct frame *etherFrame, uint8_t *srcMac, uint8_t *dstMac, uint32_t srcIp, uint32_t dstIp, int srcPort, int dstPort, const char *message, int datalen, int checksumLength) {
    if (checksumLength == 0) {
        checksumLength = datalen;
    }

    // Fill in the Ethernet part
    int frameLength = 6 + 6 + 2 + IP4_HDRLEN + UDP_HDRLEN + datalen;
    memcpy (&(etherFrame->dstMac), dstMac, 6 * sizeof (uint8_t));
    memcpy (&(etherFrame->srcMac), srcMac, 6 * sizeof (uint8_t));
    etherFrame->proto[0] = ETH_P_IP / 256;
    etherFrame->proto[1] = ETH_P_IP % 256;

    // IP Header
    etherFrame->iphdr.ip_hl = IP4_HDRLEN / sizeof (uint32_t);
    etherFrame->iphdr.ip_v = 4;
    etherFrame->iphdr.ip_tos = 0;
    etherFrame->iphdr.ip_len = htons (IP4_HDRLEN + UDP_HDRLEN + datalen);
    etherFrame->iphdr.ip_id = htons (0);
    etherFrame->iphdr.ip_off = 0;
    etherFrame->iphdr.ip_ttl = 255;
    etherFrame->iphdr.ip_p = IPPROTO_UDP;
    etherFrame->iphdr.ip_src.s_addr = srcIp;
    etherFrame->iphdr.ip_dst.s_addr = dstIp;

    etherFrame->iphdr.ip_sum = 0;
    etherFrame->iphdr.ip_sum = checksum ((uint16_t *) &(etherFrame->iphdr), IP4_HDRLEN);

    // UDP Header
    etherFrame->udphdr.source = htons (srcPort);
    etherFrame->udphdr.dest = htons (dstPort);
    etherFrame->udphdr.len = htons (UDP_HDRLEN + datalen);

    etherFrame->udphdr.check = 0;
    // Because the padding at the end of the message is always zeroes, only calculate the checksum on the 
    // header portion of the outbound message
    // Also, use rewritten (zero copy) version of udp checksum.
    uint16_t udpcs2 = udp4_checksum2 (etherFrame->iphdr, etherFrame->udphdr, (uint16_t *)message, checksumLength);
    etherFrame->udphdr.check = udpcs2;
    
    // Payload
    memcpy (&(etherFrame->message), message, datalen);

    return frameLength;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x60, %rsp
movl 0x28(%rbp), %eax
movl 0x20(%rbp), %eax
movq 0x18(%rbp), %rax
movl 0x10(%rbp), %eax
movq %rdi, -0x8(%rbp)
movq %rsi, -0x10(%rbp)
movq %rdx, -0x18(%rbp)
movl %ecx, -0x1c(%rbp)
movl %r8d, -0x20(%rbp)
movl %r9d, -0x24(%rbp)
cmpl $0x0, 0x28(%rbp)
jne 0x4808
movl 0x20(%rbp), %eax
movl %eax, 0x28(%rbp)
movl 0x20(%rbp), %eax
addl $0x2a, %eax
movl %eax, -0x28(%rbp)
movq -0x8(%rbp), %rax
movq -0x18(%rbp), %rcx
movl (%rcx), %edx
movl %edx, (%rax)
movw 0x4(%rcx), %cx
movw %cx, 0x4(%rax)
movq -0x8(%rbp), %rax
movq -0x10(%rbp), %rcx
movl (%rcx), %edx
movl %edx, 0x6(%rax)
movw 0x4(%rcx), %cx
movw %cx, 0xa(%rax)
movq -0x8(%rbp), %rax
movb $0x8, 0xc(%rax)
movq -0x8(%rbp), %rax
movb $0x0, 0xd(%rax)
movq -0x8(%rbp), %rax
movb 0xe(%rax), %cl
andb $-0x10, %cl
orb $0x5, %cl
movb %cl, 0xe(%rax)
movq -0x8(%rbp), %rax
movb 0xe(%rax), %cl
andb $0xf, %cl
orb $0x40, %cl
movb %cl, 0xe(%rax)
movq -0x8(%rbp), %rax
movb $0x0, 0xf(%rax)
movl 0x20(%rbp), %eax
addl $0x1c, %eax
movzwl %ax, %edi
callq 0x2060
movw %ax, %cx
movq -0x8(%rbp), %rax
movw %cx, 0x10(%rax)
xorl %edi, %edi
callq 0x2060
movw %ax, %cx
movq -0x8(%rbp), %rax
movw %cx, 0x12(%rax)
movq -0x8(%rbp), %rax
movw $0x0, 0x14(%rax)
movq -0x8(%rbp), %rax
movb $-0x1, 0x16(%rax)
movq -0x8(%rbp), %rax
movb $0x11, 0x17(%rax)
movl -0x1c(%rbp), %ecx
movq -0x8(%rbp), %rax
movl %ecx, 0x1a(%rax)
movl -0x20(%rbp), %ecx
movq -0x8(%rbp), %rax
movl %ecx, 0x1e(%rax)
movq -0x8(%rbp), %rax
movw $0x0, 0x18(%rax)
movq -0x8(%rbp), %rdi
addq $0xe, %rdi
movl $0x14, %esi
callq 0x49c0
movw %ax, %cx
movq -0x8(%rbp), %rax
movw %cx, 0x18(%rax)
movl -0x24(%rbp), %eax
movzwl %ax, %edi
callq 0x2060
movw %ax, %cx
movq -0x8(%rbp), %rax
movw %cx, 0x22(%rax)
movl 0x10(%rbp), %eax
movzwl %ax, %edi
callq 0x2060
movw %ax, %cx
movq -0x8(%rbp), %rax
movw %cx, 0x24(%rax)
movl 0x20(%rbp), %eax
addl $0x8, %eax
movzwl %ax, %edi
callq 0x2060
movw %ax, %cx
movq -0x8(%rbp), %rax
movw %cx, 0x26(%rax)
movq -0x8(%rbp), %rax
movw $0x0, 0x28(%rax)
movq -0x8(%rbp), %rax
movq 0xe(%rax), %rcx
movq %rcx, -0x40(%rbp)
movq 0x16(%rax), %rcx
movq %rcx, -0x38(%rbp)
movl 0x1e(%rax), %eax
movl %eax, -0x30(%rbp)
movq -0x8(%rbp), %rax
movq 0x22(%rax), %rax
movq %rax, -0x48(%rbp)
movq 0x18(%rbp), %rsi
movl 0x28(%rbp), %edx
movq -0x48(%rbp), %rdi
leaq -0x40(%rbp), %rax
movq (%rax), %rcx
movq %rcx, (%rsp)
movq 0x8(%rax), %rcx
movq %rcx, 0x8(%rsp)
movl 0x10(%rax), %eax
movl %eax, 0x10(%rsp)
callq 0x4a50
movw %ax, -0x2a(%rbp)
movw -0x2a(%rbp), %cx
movq -0x8(%rbp), %rax
movw %cx, 0x28(%rax)
movq -0x8(%rbp), %rdi
addq $0x2a, %rdi
movq 0x18(%rbp), %rsi
movslq 0x20(%rbp), %rdx
callq 0x2230
movl -0x28(%rbp), %eax
addq $0x60, %rsp
popq %rbp
retq