Why does Go net.IPv4 create a 16 byte address internally - go

I noticed Go creates a 16 byte internal representation of a 4 byte IPv4 Address when using:
// IPv4 returns the IP address (in 16-byte form) of the
// IPv4 address a.b.c.d.
func IPv4(a, b, c, d byte) IP {
p := make(IP, IPv6len)
copy(p, v4InV6Prefix)
p[12] = a
p[13] = b
p[14] = c
p[15] = d
return p
}
https://golang.org/src/net/ip.go
Is there a reason that IPv4 is initially created with 16 bytes? I was doing some calculations for Broadcast and Networkaddress where I accessed the internal byte[] directly and got confused that I had to call To4() to do something like
start := binary.BigEndian.Uint32([]byte(ip))
and actually get the IPv4 Address as uint32.

It looks like they're using a representation that allows them to easily handle both IPv4 and IPv6 addresses with the same code. They're using a single 16-byte representation for either type of IP.
The comments at the top of the code (that you linked to) explain in pretty clearly:
// IPv4 addresses are 4 bytes; IPv6 addresses are 16 bytes.
// An IPv4 address can be converted to an IPv6 address by
// adding a canonical prefix (10 zeros, 2 0xFFs).
// This library accepts either size of byte slice but always
// returns 16-byte addresses.
You can pass in either 4-byte or 16-byte representations of IPv4 addresses, but the library will internally use 16-byte ones and always return those.

The 16 byte address for IPV4 is known the IPv4-mapped address.
For 1.2.3.4 it looks like 0:0:0:0:0:ffff:12:34.
IPv4-mapped addresses are one way of storing an IPv4 address inside an IPv6 address.
Seems that going straight to the IPv4-mapped address was some kind of design choice made with that package, the reasoning is not clear to me though.

Related

Ruby: How to convert ipv6 uo address to ipv4 ip address?

It is easy to convert ipv4 to ipv6 with ipv4_mapped. But how about the other around?
I did not find a way to do that in IpAddr documentation.
Firstly, it's important to understand that it's impossible to convert most IPv6 addresses to IPv4 addresses (for several reasons, the most obvious being that there's no way to fit a 128-bit number into 32 bits). The only IPv6 addresses that can be converted to IPv4 are ones that are mapped from IPv4 address (such as those produced by IPAddr#ipv4_mapped). For that, we have IPAddr#native:
require "ipaddr"
ip = IPAddr.new("192.168.2.128")
ipv6 = ip.ipv4_mapped
puts ipv6.native
# => 192.168.2.128

Filling up struct sockaddr_in from the ip header __be32 saddr and udp header __be16 source

I'm implementing a protocol in the linux kernel on top of UDP and want to send a reply from within the kernel when I receive a packet. For this, I want to run ip4_datagram_connect to get a route to the destination (which is the source address in the received packet) and then send a reply.
To call the ip4_datagram_connect, I need to fill in a sockaddr_in structure to pass as the address to the function. On comparing,
struct sockaddr_in
unsigned short sin_port;
struct in_addr sin_addr;
struct udphdr
__be16 source;
and struct iphdr
__be32 saddr;
So my question is, do I need any helper function to copy the address and the port from the packet header into the sockaddr_in structure (like how we use htons etc. in socket programming)?
The value in the raw IP and UDP header are already in network byte order. The be in e.g. __be16 stands for "Big Endian", which is network byte order. The numbers in __be16 and __be32 is the number of bits.
The fields in the sockadd_in structures are also supposed to be in network byte order. The name htons stands for "Host To Network Short", i.e. it converts a short (which usually is 16 bits) from host byte order to network byte order.
So to answer your question: No, you do not need to do anything, plain assignment to sin_port and sin_addr.s_addr should be enough.

windows winsock2 socket return 10014 WSAEFAULT from WSAGetLastError (ipv4 / ipv6 related)

Steps to reproduce:
Create IPv6 socket AF_INET6/SOCK_DGRAM/UDP
Bind the socket to an IPv6 address
Use sendto(...) winsocket API but specify an IPv4
socket AF_INET family address address to send to
Winsock2 API will return WSAEFAULT with WSAGetLastError.
If you attempt to send a UDP packet to an IPv4 address using an IPv6 socket, sendto() will report the WSAEFAULT error. This behavior is described by MSDN:
sendto function
Return value
If no error occurs, sendto returns the total number of bytes sent, which can be less than the number indicated by len. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError.
...
WSAEFAULT
The buf or to parameters are not part of the user address space, or
the tolen parameter is too small.
Technically that's true because an IPv4 address is smaller than an IPv6 address. It might be a bit misleading to think sizeof was wrong when it's actually the wrong address family used and hence the sizeof represents the wrong size to use.
See also windows error codes here:
Windows Sockets Error Codes

ipv6calc outputs wrong address when converting from ipv4 to ipv6?

Having a strange issue while trying to convert an ipv4 list file to ipv6:
ipv6calc -q --action conv6to4 --in ipv4 1.1.23.1 --out ipv6
2002:101:1701::
Trying to validate that result is correct, I used some online converters and it seems that 1.1.23.1 is 2002:0:0:0:0:0:101:1701 (or else 2002::101:1701).
So the last "::" should be removed & 2002 should have extra ":".
I really don't want to use sed/awk commands in order to manipulate this result, so the questions are:
is there alternative cmd/linux SW?
is this somehow fixed inside ipv6 calc, am I doing something wrong?
Thanks
This is the correct 6to4 address. A 6to4 subnet is on the format 2002:IP4_HI:IP4_LO::/48. IP4_HI is the top 16 bits of the IPv4 address, while IP4_LO is the low 16 bits of the address.
For example, the IPv4 address 1.2.3.4 gives you the 6to4 subnet 2002:0102:0304::/48.
See 6to4 address block allocation for more details.
A different question is whether this is actually the address you want? There are other ways to map IPv4 addresses to IPv6 addresses. For example, there are IPv4-mapped IPv6 addresses, which are typically written as ::ffff:1.2.3.4.
The address format you need depends on what you are going to use it for.

Mac changes IP total length field

I'm programming with sockets in Mac 10.6.8. Whenever I receive a packet, it starts with the IP header. I've been using Wireshark to analyze incoming packets, and I noticed that my machine's socket implementation will consistently change the "total length" field in the IP header. Specifically, it will subtract the IP header length and reverse the bytes (from network to host order).
For example, here's the beginning of an IP header as reported by Wireshark:
45 c0 00 38 ...
That breaks down as follows:
4 bits (0x4): IP version: 4
4 bits (0x5): IP header length: 5 words (20 bytes)
8 bits (0xc0): differentiated services flags
16 bits (0x0038): total length: 56 bytes
However, when I print the contents of the buffer filled by recvfrom for the same packet, I get a different lede:
ssize_t recvbytes = recvfrom(sock->fd, buffer, size, /*flags=*/0,
(struct sockaddr*)src, &src_len);
returns
45 c0 24 00 ...
4 bits (0x4): IP version: 4
4 bits (0x5): IP header length: 5 words (20 bytes)
8 bits (0xc0): differentiated services flags
16 bits (0x2400): total length: 9216 bytes (?!)
I figured out that before I get access to the buffer, the socket implementation is reading the total length, subtracting the IP header length, and then writing it back in host order (little endian on my machine) rather than network order (big endian). In this example, that means:
read the total length: 0x0038 = 56
subtract the header length: 56 - 20 = 36
write back in host order: 36 = 0x0024 (big endian) = 0x2400 (little endian = host order on my machine)
The problem gets worse. It won't just change the total length of the outermost IP header. It will also change the total length fields of internal IP headers, e.g., the one buried in an ICMP "time exceeded" message (which must include the original IP header of the dropped packet). Funnier still, it won't subtract the IP header length from the internal headers; it just reverses the byte order.
Is this happening to anyone else? Is it part of a standard I'm unaware of? Is there a way to fix my machine's socket implementation to stop tampering with packets? How is Wireshark able to get around this problem?
Thanks in advance for your consideration.
EDIT: My code and Makefile are available on GitHub. I wrote a fixip_osx function to allow verifying IP checksums:
https://github.com/thejohnfreeman/netutils/blob/master/lib/ip.c
void fixip_osx(struct ip* ip) {
/* Something on my Mac subtracts the header length from `ip_len` and stores
* it in host order (little endian). */
u16_t ip_hdrlen = ip->ip_hl << 2;
u16_t ip_totlen = ip->ip_len + ip_hdrlen;
ip->ip_len = htons(ip_totlen);
}
However, it's still a problem for verifying ICMP checksums when the payload contains another IP header.
The problem exists whether I compile with Clang 3.2 (built from trunk) or GCC 4.7 (MacPorts port), so I assume the problem lies in either the sockets implementation (packaged with Mac OS) or in Mac OS X itself.
The BSD suite of platforms (excluding OpenBSD) present the IP offset and length in host byte order. All other platforms present in the received network byte order. This is a "feature", and is referenced in the man page for IP(4) - Internet Protocol (FreeBSD, OS X).
The ip_len and ip_off fields must be provided in host byte order .
All other fields must be provided in network byte order.
IP length can equal packet length - IP header length in FreeBSD/NetBSD.
Reference: Stevens/Fenner/Rudolph, Unix Network Programming Vol.1, p.739
I have to deal with these anomalies with a user space implementation of the PGM network protocol, specific code:
https://code.google.com/p/openpgm/source/browse/trunk/openpgm/pgm/packet_parse.c#76
It's actually quite annoying to detect for AutoConf, I think all packages have this hard coded on a per-platform basis. I've seen a bug report (header byte order config options detected incorrectly) raised this week on this very issue.
It is very unlikely that Mac itself is doing that. That would fundamentally break the IP protocol if it were. More likely whatever is capturing the packets and delivering them to recvfrom() (presumably you are doing a promiscuous network capture, right?) is what is transforming the data after Mac is done processing it. ireshark operates on a lower level and has access to the actual network data.

Resources