Is this a bug in inet_pton with IPv6? - c

When trying to resolve Facebook's numeric IP address as a test 2620:0:1cfe:face:b00c::3:, if I leave the terminating 0 off the address, inet_pton() barfs. If I put it back on everything works.
Running ubuntu 9.10:
rc = inet_pton(AF_INET6, "2620:0:1cfe:face:b00c::3:0", &ip); -> OK
rc = inet_pton(AF_INET6, "2620:0:1cfe:face:b00c::3:", &ip); -> returns -2
ping6 -n www.v6.facebook.com returns the IP address w/o the trailing 0.

It seems that ping, in it's great wisdom, adds a colon after the IP address like so:
PING maclawran.ca (173.230.128.18) 56(84) bytes of data.
64 bytes from ns.maclawran.ca (173.230.128.18): icmp_seq=1 ttl=51 time=50.3 ms
Of course if you're pinging an IPv6 address, it's already got lots of colons in it:
PING 2620:0:1cfe:face:b00c::3(2620:0:1cfe:face:b00c::3) 56 data bytes
64 bytes from 2620:0:1cfe:face:b00c::3: icmp_seq=1 ttl=52 time=9.44 ms
======================================^ << THANKS PING

Related

ping successfully but gethostbyname() returns null

I'm programming for a device running Linux OS.
The device runs ping command to youtube.com successfully. However, in source code, if I call gethostbyname("youtube.com"), the function returns NULL.
UPDATE LOG
My code looks like this (this is just a short pseudo code to describe my issue, not full code)
void my_func()
{
struct hostent *hostEntry;
hostEntry = gethostbyname("youtube.com");
if (hostEntry) {
// gethostbyname() resolve host name successfully, no need to care
} else {
printf("Fail to get host, herrno=%d, strErr=%s\n", h_errno, hstrerror(h_errno));
}
}
And the device's output when gethostbyname() returns NULL is
Fail to get host, herrno=1, strErr=Unknown host
This is the device's output when I exit the program and run ping youtube.com
$ ping youtube.com
PING youtube.com (64.233.189.91): 56 data bytes
64 bytes from 64.233.189.91: seq=0 ttl=55 time=57.824 ms
64 bytes from 64.233.189.91: seq=1 ttl=55 time=56.306 ms
64 bytes from 64.233.189.91: seq=2 ttl=55 time=56.790 ms
64 bytes from 64.233.189.91: seq=3 ttl=55 time=56.831 ms
64 bytes from 64.233.189.91: seq=4 ttl=55 time=56.417 ms
What should I check now?
I add res_init() to resolve this issue and fortunately, it works.
#include <resolv.h>
void my_func()
{
struct hostent *hostEntry;
hostEntry = gethostbyname("youtube.com");
if (hostEntry) {
res_init();
hostEntry = gethostbyname("youtube.com");
}
if (hostEntry) {
// gethostbyname() resolve host name successfully, no need to care
} else {
printf("Fail to get host, herrno=%d, strErr=%s\n", h_errno, hstrerror(h_errno));
}
}
This may be the reason why the function helps
The res_init() function reads the configuration files (see resolv.conf(5)) to get the default domain name, search order and name server address(es).

ip_rcv (in ip_input.c for ipv4) behaviour in loopback mode

So I wanted to put a few printk messages in ip_rcv function to see whether after receiving a packet from a particular IP there is a message printed. I am attaching the entire ip_rcv function which has the printk modifications:
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
{
const struct iphdr *iph;
struct net *net;
u32 len;
/* When the interface is in promisc. mode, drop all the crap
* that it receives, do not try to analyse it.
*/
if (skb->pkt_type == PACKET_OTHERHOST)
goto drop;
net = dev_net(dev);
__IP_UPD_PO_STATS(net, IPSTATS_MIB_IN, skb->len);
skb = skb_share_check(skb, GFP_ATOMIC);
if (!skb) {
__IP_INC_STATS(net, IPSTATS_MIB_INDISCARDS);
goto out;
}
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
goto inhdr_error;
iph = ip_hdr(skb);
//**PSK's Modification**
if (iph->saddr == 0x08080808)
printk("\n***PSK: %x IP's message recieved: Google***\n", iph->saddr);
if (iph->saddr == 0x0202000A)
printk("\n***PSK: %x IP's message recieved: Gateway***\n", iph->saddr);
if (iph->saddr == 0x010000FF)
printk("\n***PSK: %x IP's message recieved : Home***\n", iph->saddr);
/* RFC1122: 3.2.1.2 MUST silently discard any IP frame that fails the checksum.
*
* Is the datagram acceptable?
*
* 1. Length at least the size of an ip header
* 2. Version of 4
* 3. Checksums correctly. [Speed optimisation for later, skip loopback checksums]
* 4. Doesn't have a bogus length
*/
if (iph->ihl < 5 || iph->version != 4)
goto inhdr_error;
BUILD_BUG_ON(IPSTATS_MIB_ECT1PKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_ECT_1);
BUILD_BUG_ON(IPSTATS_MIB_ECT0PKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_ECT_0);
BUILD_BUG_ON(IPSTATS_MIB_CEPKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_CE);
__IP_ADD_STATS(net,
IPSTATS_MIB_NOECTPKTS + (iph->tos & INET_ECN_MASK),
max_t(unsigned short, 1, skb_shinfo(skb)->gso_segs));
if (!pskb_may_pull(skb, iph->ihl*4))
goto inhdr_error;
iph = ip_hdr(skb);
if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
goto csum_error;
len = ntohs(iph->tot_len);
if (skb->len < len) {
__IP_INC_STATS(net, IPSTATS_MIB_INTRUNCATEDPKTS);
goto drop;
} else if (len < (iph->ihl*4))
goto inhdr_error;
/* Our transport medium may have padded the buffer out. Now we know it
* is IP we can trim to the true length of the frame.
* Note this now means skb->len holds ntohs(iph->tot_len).
*/
if (pskb_trim_rcsum(skb, len)) {
__IP_INC_STATS(net, IPSTATS_MIB_INDISCARDS);
goto drop;
}
iph = ip_hdr(skb);
skb->transport_header = skb->network_header + iph->ihl*4;
/* Remove any debris in the socket control block */
memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
IPCB(skb)->iif = skb->skb_iif;
/* Must drop socket now because of tproxy. */
skb_orphan(skb);
return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,
net, NULL, skb, dev, NULL,
ip_rcv_finish);
csum_error:
__IP_INC_STATS(net, IPSTATS_MIB_CSUMERRORS);
inhdr_error:
__IP_INC_STATS(net, IPSTATS_MIB_INHDRERRORS);
drop:
kfree_skb(skb);
out:
return NET_RX_DROP;
}
I should get a message printed into the kernel buffer after getting a packet from either Google DNS, my local gateway (10.0.2.2) or loop-back address (127.0.0.1). This is working fine for the DNS and the gateway, but not when I ping localhost or try to run a program which involves back and forth from a localhost. Are there some other kernel function calls that specifically handle packets to localhost, or am I missing something very basic? I thought the loopback packets should trace the same path through the stack as any other packet at least until L3. I would also appreciate if someone briefly explains the handling of loopback traffic along with the answer.
System Specs:
System- Ubuntu on Virtual Machine
Kernel- 4.15.0-70 generic
I think there is something wrong here:
if (iph->saddr == 0x010000FF)
Perhaps you mean:
if (iph->saddr == 0x0100007F)
loopback packets should trace the same path
Yep, in general.
Investigate more about the details of loopback device.
Also you always can operate with some useful tools like trace-cmd. F.e. to see the function graph you can do:
trace-cmd record -p function_graph -g net_rx_action
Then start ping 127.0.0.1, then stop tracing and watch report like
trace-cmd report | vim -
Here you can see the "path" and ensure that your localhost pinging eventually falls into ip_rcv().

Wrong size of IPv4 packets in DPDK

I have these packets saved in pcap file (shown in wireshark)
Iam parsing these packets using DPDK and for some reason i cannot use mbufs. To parse them iam using struct ipv4_hdr and struct ether_hdr as following:
eth_hdr = (struct ether_hdr *) pckt;
ip_hdr = (struct ipv4_hdr *)((unsigned char *) pckt + sizeof(struct ether_hdr));
The total_length in ipv4_hdr is size of ipv4 header + size of data. With size of etherhet header it should be the total length of packet, right?
size = rte_bswap16(ip_hdr->total_length) + sizeof(struct ether_hdr);
which gives me for these 6 packets output:
54, 54, 54, 54, 54, 54
For first, third and fifth packets its correct, but for the others its 6 bytes less than it should be.
Iam asking what these 6 bytes in packets are and how to find out the correct sizes using ipv4 and ether headers:
54, 60, 54, 60, 54, 60
In wireshark all of these packets has total_length 40 bytes and sizeof(ether_hdr) is 14 bytes -> it should be 54.
I guess the root cause is the minimum Ethernet frame length. As Wikipedia says, the minimum frame length is 64 bytes.
At the end of each Ethernet frame we add Frame Check Sequence (FCS, 4 octets), so it makes minimum 60 octets for Ethernet header and payload.
Now answering your questions:
I am asking what these 6 bytes in packets
Those are zero paddings to make ethernet frame (with FCS) at least 64 octets.
For outgoing packets, there are no paddings yet, they will be added later by the driver or NIC itself. So Wireshark shows unpadded frames, i.e. 40 bytes of IP + 14 bytes of Ethernet header (without FCS) makes 54 bytes.
For incoming packets, there are already paddings added by the sending part. So Wireshark shows frames with padding, i.e. 64 octets - 4 octets FCS = 60 octets.
how to find out the correct sizes using ipv4 and ether headers
Your method is completely correct. Those zeros at the end of the frame are just padding and should be ignored. If we really need a correct length, we should take into account the minimum frame length as described above.

Binding to fe80::%<scope>

Trying to get daemon to bind to fe80::%en0.
I'm not 100% sure this is possible, but it really seems like for the link-local address to be useful it should be.
If you want to accept packets from your peer on the other side of the link, it seems stupid to have to specify the full link-local address.
The code looks like this:
if (DEBUG_ENABLED4) {
if (salocal.ss_family == AF_INET) {
char buffer[INET_ADDRSTRLEN];
struct sockaddr_in *addr = (struct sockaddr_in *)&salocal;
inet_ntop(addr->sin_family, &addr->sin_addr, buffer, sizeof(buffer));
DEBUG4("[FD %i] Binding to address %s port %u -- bind(%i, %p, %u)",
this->fd, buffer, ntohs(addr->sin_port), this->fd, &salocal, salen);
} else if (salocal.ss_family == AF_INET6) {
char buffer[INET6_ADDRSTRLEN];
struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&salocal;
inet_ntop(addr->sin6_family, &addr->sin6_addr, buffer, sizeof(buffer));
DEBUG4("[FD %i] Binding to address %s port %u scope ID %i -- bind(%i, %p, %u)",
this->fd, buffer, ntohs(addr->sin6_port), addr->sin6_scope_id,
this->fd, &salocal, salen);
}
}
rad_suid_up();
rcode = bind(this->fd, (struct sockaddr *)&salocal, salen);
rad_suid_down();
The debug output looks like this:
Sat Nov 7 19:58:28 2015 : Debug: [FD 12] Binding to address fe80:: port 1812 scope ID 4 -- bind(12, 0x7fff599dc4d0, 28)
Sat Nov 7 19:58:28 2015 : Error: Failed binding to auth address fe80:: port 1812 bound to server default: EADDRNOTAVAIL: Can't assign requested address
Sat Nov 7 19:58:28 2015 : Error: /usr/local/freeradius/etc/raddb/sites-enabled/default[215]: Error binding to port for fe80:: port 1812
Ifconfig output looks like this:
en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
options=10b<RXCSUM,TXCSUM,VLAN_HWTAGGING,AV>
ether a8:20:66:xx:xx:xx
inet6 fe80::aa20:66ff:fe1a:8bcd%en0 prefixlen 64 scopeid 0x4
Values all seem to match up, but bind returns EADDRNOTAVAIL. Is there some magic socket option I need to enable to make this work? Or do I need to check for that link local IP prefix specifically and then resolve it to the full address for the interface described by the scope_id/zone_id?
In short: if you want to bind to a specific address you'll have to specify that address. A host can have multiple link-local addresses. If you want to bind to all link-local addresses automatically you'll have to auto-discover them and create a socket for each of them.

IPv4 to decimal different values?

Why is the IPv4's decimal value different with inet_pton and inet_addr (1734763876) than what you get if you use these 2 websites (1684366951) ?
struct sockaddr_in sin;
inet_pton(AF_INET, "100.101.102.103", &(sin.sin_addr));
printf("%i\n%i\n", inet_addr("100.101.102.103"), sin.sin_addr);
http://www.allredroster.com/iptodec.htm
http://www.ipaddresslocation.org/convertip.php
Endianness - they have the four bytes in opposite orders:
1734763876 = 0x67 66 65 64
1684366951 = 0x64 65 66 67
The value you'll need to use for URLs etc. is the one in 'Network' order, Most-Significant-Byte first. Use htonl() (host-to-network-long) to convert the value, i.e.
printf("%i\n%i\n", htonl(inet_addr("100.101.102.103")), htonl(sin.sin_addr));
caf points out below that I probably have this backwards: the issue is really that you need to convert the network-order data from the socket functions back into host-order for display, i.e.
printf("%i\n%i\n", ntohl(inet_addr("100.101.102.103")), ntohl(sin.sin_addr));
inet_addr gives the result in network-byte order.
1684366951 and 1734763876 are the same number ;-) if you change the endianess.

Resources