diff --git a/2021/12/24/Endianness/index.html b/2021/12/24/Endianness/index.html index abc84cb1..9d80d0d4 100644 --- a/2021/12/24/Endianness/index.html +++ b/2021/12/24/Endianness/index.html @@ -5,7 +5,7 @@ - + @@ -327,7 +327,7 @@

How can I remember these two data storing modes? It is quite simple. First, remember that the addresses of the memory units we are talking about are always arranged from low to high. For a multi-byte number, if the first byte in the low address you see is the least-significant byte, the system is Little Endian, where Little matches low. On the contrary is Big Endian, where Big corresponds to "high".

Program Example

To deepen our understanding of Endianness, let's look at the following example of a C program:

-
1
2
3
4
char a = 1; 	 	 	 
char b = 2;
short c = 255; /* 0x00ff */
long d = 0x44332211;
+
1
2
3
4
char a = 1; 	 	 	 
char b = 2;
short c = 255; /* 0x00ff */
long d = 0x44332211;

On Intel 80x86 based systems, the memory content corresponding to variables a, b, c, and d are shown in the following table:

@@ -356,11 +356,11 @@

Program Example

  • Little Endian: Intel x86, AMD64, DEC VAX
  • How to detect the Endianess of local system in the program? The following function can be called for a quick check. If the return value is 1, it is Little Endian, else Big Endian

    -
    1
    2
    3
    4
    int test_endian() {
    int x = 1;
    return *((char *)&x);
    }
    +
    1
    2
    3
    4
    int test_endian() {
    int x = 1;
    return *((char *)&x);
    }

    Network Order

    Endianness is also important for computer communications. Imagine that when a Little Endian system communicates with a Big Endian system, the receiver and sender will interpret the data completely differently if not handled properly. For example, for the variable d in the C program segment above, the Little Endian sender sends 11 22 33 44 four bytes, which the Big Endian receiver converts to the value 0x11223344. This is very different from the original value. To solve this problem, the TCP/IP protocol specifies a special "network byte order" (referred to as "network order"), which means that regardless of the Endian supported by the computer system, the most-significant byte is always sent first while transmitting data. From the definition, we can see that the network order corresponds to the Big Endian.

    To avoid communication problems caused by Endianness and to facilitate software developers to write portable programs, some C preprocessing macros are defined for conversion between network bytes and local byte order. htons() and htonl() are used to convert local byte order to network byte order, the former works with 16-bit unsigned numbers and the latter for 32-bit unsigned numbers. ntohs() and ntohl() implement the conversion in the opposite direction. The prototype definitions of these four macros can be found as follows (available in the netinet/in.h file on Linux systems).

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #if defined(BIG_ENDIAN) && !defined(LITTLE_ENDIAN)

    #define htons(A) (A)
    #define htonl(A) (A)
    #define ntohs(A) (A)
    #define ntohl(A) (A)

    #elif defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN)

    #define htons(A) ((((uint16)(A) & 0xff00) >> 8) | \
    (((uint16)(A) & 0x00ff) << 8))
    #define htonl(A) ((((uint32)(A) & 0xff000000) >> 24) | \
    (((uint32)(A) & 0x00ff0000) >> 8) | \
    (((uint32)(A) & 0x0000ff00) << 8) | \
    (((uint32)(A) & 0x000000ff) << 24))
    #define ntohs htons
    #define ntohl htohl

    #else

    #error "Either BIG_ENDIAN or LITTLE_ENDIAN must be #defined, but not both."

    #endif
    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #if defined(BIG_ENDIAN) && !defined(LITTLE_ENDIAN)

    #define htons(A) (A)
    #define htonl(A) (A)
    #define ntohs(A) (A)
    #define ntohl(A) (A)

    #elif defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN)

    #define htons(A) ((((uint16)(A) & 0xff00) >> 8) | \
    (((uint16)(A) & 0x00ff) << 8))
    #define htonl(A) ((((uint32)(A) & 0xff000000) >> 24) | \
    (((uint32)(A) & 0x00ff0000) >> 8) | \
    (((uint32)(A) & 0x0000ff00) << 8) | \
    (((uint32)(A) & 0x000000ff) << 24))
    #define ntohs htons
    #define ntohl htohl

    #else

    #error "Either BIG_ENDIAN or LITTLE_ENDIAN must be #defined, but not both."

    #endif
    diff --git a/2021/12/26/IPv4-IPv6-checksum/index.html b/2021/12/26/IPv4-IPv6-checksum/index.html index 18bb7eaf..c6e0ed48 100644 --- a/2021/12/26/IPv4-IPv6-checksum/index.html +++ b/2021/12/26/IPv4-IPv6-checksum/index.html @@ -5,7 +5,7 @@ - + @@ -333,9 +333,9 @@

    IPv4 Header Checksum

    Notice at step (1) we replace the checksum field with 0x0000. As can be seen, the calculated header checksum 0x598f is the same as the value in the captured packet. This calculating process is only used for the sender to generate the initial checksum. In practice, for the intermediate forwarding router and the final receiver, they can just sum up all header fields of the received IP packet by the same algorithm. If the result is 0xffff, the checksum verification passes.

    C Program Implementation

    How to program IPv4 header checksum computing? RFC 1071 (Computing the Internet Checksum) shows a reference "C" language implementation:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    {
    /* Compute Internet Checksum for "count" bytes
    * beginning at location "addr".
    */
    register long sum = 0;

    while( count > 1 ) {
    /* This is the inner loop */
    sum += * (unsigned short *) addr++;
    count -= 2;
    }

    /* Add left-over byte, if any */
    if ( count > 0 )
    sum += * (unsigned char *) addr;

    /* Fold 32-bit sum to 16 bits */
    while (sum>>16)
    sum = (sum & 0xffff) + (sum >> 16);

    checksum = ~sum;
    }
    +
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    {
    /* Compute Internet Checksum for "count" bytes
    * beginning at location "addr".
    */
    register long sum = 0;

    while( count > 1 ) {
    /* This is the inner loop */
    sum += * (unsigned short *) addr++;
    count -= 2;
    }

    /* Add left-over byte, if any */
    if ( count > 0 )
    sum += * (unsigned char *) addr;

    /* Fold 32-bit sum to 16 bits */
    while (sum>>16)
    sum = (sum & 0xffff) + (sum >> 16);

    checksum = ~sum;
    }

    In a real network connection, the source device can call the above code to generate the initial IPv4 header checksum. This checksum is then updated at each step of the routing hop because the router must decrement the Time To Live (TTL) field. RFC 1141 (Incremental Updating of the Internet Checksum) gives a reference implementation of fast checksum update:

    -
    1
    2
    3
    4
    unsigned long sum;
    ipptr->ttl--; /* decrement ttl */
    sum = ipptr->Checksum + 0x100; /* increment checksum high byte*/
    ipptr->Checksum = (sum + (sum>>16)); /* add carry */
    +
    1
    2
    3
    4
    unsigned long sum;
    ipptr->ttl--; /* decrement ttl */
    sum = ipptr->Checksum + 0x100; /* increment checksum high byte*/
    ipptr->Checksum = (sum + (sum>>16)); /* add carry */

    TCP/UDP Header Checksum

    For TCP segment and UDP datagram, both have 16-bit header checksum fields used for error-checking by the destination host. The checksum computing algorithm is the same as the IP header, except for the difference of covered data. Here the checksum is calculated over the whole TCP/UDP header and the payload, plus a pseudo-header that mimics the IPv4 header as shown below:

    1
    2
    3
    4
    5
    6
    7
    8
     0      7 8     15 16    23 24    31 
    +--------+--------+--------+--------+
    | source address |
    +--------+--------+--------+--------+
    | destination address |
    +--------+--------+--------+--------+
    | zero |protocol| TCP/UDP length |
    +--------+--------+--------+--------+
    @@ -395,7 +395,7 @@

    UDP-Lite Application

    For multimedia applications running VoIP or streaming video data transmission protocols, it'd better receive data with some degree of corruption than not receiving any data at all. Another example is the CAPWAP protocol used to connect Cisco wireless controller and access points. It specifies UDP-Lite as the default transport protocol for the CAPWAP Data channel, while the connection is established over the IPv6 network.

    At last, share a C program snippet to present how to initialize a Berkeley socket to establish an IPv6 UDP-Lite connection:

    -
    1
    2
    3
    4
    5
    6
    7
    8
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <net/udplite.h>

    int udplite_conn = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDPLITE);
    int val = 8; /* checksum only covers 8-byte UDP-Lite header */
    (void)setsockopt(udplite_conn, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV, &val, sizeof val);
    (void)setsockopt(udplite_conn, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV, &val, sizeof val);
    +
    1
    2
    3
    4
    5
    6
    7
    8
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <net/udplite.h>

    int udplite_conn = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDPLITE);
    int val = 8; /* checksum only covers 8-byte UDP-Lite header */
    (void)setsockopt(udplite_conn, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV, &val, sizeof val);
    (void)setsockopt(udplite_conn, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV, &val, sizeof val);

    Here IPPROTO_UDPLITE is protocol number 136, which is used together with AF_INET6 address family parameter in socket() function call for IPv6 socket creation. The UDPLITE_SEND_CSCOV(10) and UDPLITE_RECV_CSCOV(11) are the control parameters of socket options configuration function setsockopt(), used for setting the Checksum Coverage value in the sender and the receiver respectively. Remember that both the sender and the receiver must set the same value, otherwise, the receiver will not be able to verify the checksum properly.

    diff --git a/2021/12/29/RPi-NAS-Plex/index.html b/2021/12/29/RPi-NAS-Plex/index.html index caab1d21..09cb3fa9 100644 --- a/2021/12/29/RPi-NAS-Plex/index.html +++ b/2021/12/29/RPi-NAS-Plex/index.html @@ -5,7 +5,7 @@ - + @@ -52,7 +52,7 @@ - + @@ -295,7 +295,7 @@

    Edited on - +