ARP Response over router - c

I am pretty new to networking and I have been trying to understand ARP requests. I've been using mininet and wireshark in order to test what I'm doing.
When I use mininet to generate 2 hosts (h1 and h2) and a switch, my ARP broadcast is immediately responded with an ARP reply, everything works correctly.
When I use a given router.py script that generates the following on mininet -
*** Creating network
*** Adding controller
*** Adding hosts:
h1x1 h1x2 h2x1 h2x2 h3x1 h3x2 r0
*** Adding switches:
s1 s2 s3
*** Adding links:
(h1x1, s1) (h1x2, s1) (h2x1, s2) (h2x2, s2) (h3x1, s3) (h3x2, s3) (s1, r0) (s2, r0) (s3, r0)
*** Configuring hosts
h1x1 h1x2 h2x1 h2x2 h3x1 h3x2 r0
*** Starting controller
c0
*** Starting 3 switches
s1 s2 s3 ...
*** Routing Table on Router:
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
10.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 r0-eth3
172.16.0.0 0.0.0.0 255.240.0.0 U 0 0 0 r0-eth2
192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 r0-eth1
// ./a.out Send <InterfaceName> <DestIP> <RouterIP> <Message>
mininet> h1x1 ./a.out Send h1x1-eth0 10.0.0.1 192.168.1.100 'This is a test'
This is how I run my command on mininet to run the ARP request.
When I try to run the ARP request using destination IP 10.0.0.1 and the router IP 192.168.1.00 my ARP request broadcasts normally, but I do not get the ARP reply, instead I get a series of ICMPv6 responses.
Here is how I am creating my ARP header
struct arp_hdr construstArpRequest(char if_name[], int sockfd, struct in_addr dst, struct ifreq if_hwaddr) {
printf("Constructing ARP request --\n");
struct arp_hdr arphdr;
arphdr.ar_hrd = htons(0x0001);
arphdr.ar_pro = htons(0x0800);
arphdr.ar_hln = 6;
arphdr.ar_pln = 4;
arphdr.ar_op = htons(0x0001);
unsigned long sip = get_ip_saddr(if_name, sockfd); // source IP
memcpy(arphdr.ar_sip, &sip, 4); // source IP
memcpy(arphdr.ar_tip, &dst.s_addr, 4); // taget IP
memset(arphdr.ar_tha, 0, 6); // taget HA
memcpy(arphdr.ar_sha, if_hwaddr.ifr_hwaddr.sa_data, 6); // source HA
return arphdr;
}
And I create my ARP request
int sockfd = -1;
if((sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0){
perror("socket() failed!");
}
// connect to an internet frame
struct ifreq if_hwaddr;
memset(&if_hwaddr, 0, sizeof(struct ifreq));
strncpy(if_hwaddr.ifr_name, interfaceName, IFNAMSIZ-1);
if(ioctl(sockfd, SIOCGIFHWADDR, &if_hwaddr) < 0){
perror("SIOCGIFHWADDR");
}
struct arp_hdr arpRequest;
arpRequest = construstArpRequest(interfaceName, sockfd, router_ip, if_hwaddr);
If I need to include code about how I am actually sending the request, I can but not sure if it is necessary code. Throughout my research I have come across some answers saying that you will not get the broadcast response because you are running it over a network, it that's the case, how do you get the target MAC address?

ARP requests are for IPv4 only, and use broadcast (IPv6 does not have broadcast, and it uses NDP, not ARP), but routers do not forward broadcasts to a different network.
A source host will mask the destination address with its configured mask to determine if the destination address is on the same network. If the destination is on the same network, it will use ARP (either in the ARP table, or send a new ARP request) to determine the destination host data-link address and use that to build the data-link frame. If the destination is on a different network, the source host will use ARP (either in the ARP table, or send a new ARP request) to determine the data-link address of its configured gateway, and it will use the gateway data-link address to build the data-link frame.
You are trying to use an ARP request for a host on a different network, and that will not work. Trying to send an ARP request for a destination on a different network will get no response, and you are seeing that (you need to implement a timeout for your ARP requests, and send an error message up the network stack to the requesting process when it times out).
The IPv6 traffic you see is normal IPv6 maintenance traffic that periodically happens on a LAN where IPv6 is configured.

Related

DPDK19.11.10: HW offload for IPV4 with VLAN tag is not working properly

I am using DPDK19.11.10 on centos.
The application is working fine with HW offloading if I send only the IPV4 packet without the VLAN header.
If I add the VLAN header with IPV4, HW offloading is not working.
If capture the pcap on ubuntu gateway the IP header is corrupted with Fragmented IP packet even though we are not fragmenting IP packet.
We verified capabalities like this:
if (!(dev->tx_offload_capa & DEV_TX_OFFLOAD_VLAN_INSERT)) {
rte_panic(" VLAN offload not supported");
}
Below is my code:
.offloads = (DEV_TX_OFFLOAD_IPV4_CKSUM |
DEV_TX_OFFLOAD_UDP_CKSUM | DEV_TX_OFFLOAD_TCP_CKSUM | DEV_TX_OFFLOAD_VLAN_INSERT),
m->l2_len = L2_HDR_SIZE;
m->l3_len = L3_IPV4_HDR_SIZE;
ip_hdr->check = 0;
m->ol_flags |= PKT_TX_IPV4 | PKT_TX_IP_CKSUM;
ip_hdr = rte_pktmbuf_mtod(m, struct iphdr *);
vlan1_hdr = (struct vlan1_hdr *) rte_pktmbuf_prepend(m, sizeof(struct vlan1_hdr));
eth_hdr = (struct ethernet_hdr *) rte_pktmbuf_prepend(m, (uint16_t)sizeof(struct ethernet_hdr));
Once I received the packet in the ubuntu gateway the IP packet is corrupted as a fragmented IP packet.
The same code works fine if I removed the VLAN header.
Does anything else need to add here?
By the sound of it,
You might misunderstand the way how HW Tx VLAN offload is supposed to work;
Your code does not update m->l2_len when it inserts a VLAN header.
First of all, your code enables support for HW Tx VLAN offload, but, oddly enough, it does not actually attempt to use it. If one wants to use hardware Tx VLAN offload, they should set PKT_TX_VLAN in m->ol_flags and fill out m->vlan_tci. The VLAN header will be added by the hardware.
However, your code prepends the header itself, like if there was no intent to use a hardware offload in the first place. Your code does m->l2_len = L2_HDR_SIZE;, which, as I presume, only counts for Ethernet header. When your code prepends a VLAN header, this variable has to be updated accordingly:
m->l2_len += sizeof(struct rte_vlan_hdr);
Most DPDK NIC PMD supports HW VLAN offload (RX direction). But a limited number of PMD support the DEV_TX_OFFLOAD_VLAN_INSERT feature namely
Aquantia Atlantic
Marvell OCTEON CN9K/CN10K SoC
Cisco VIC adapter
Pensando NIC
OCTEON TX2
Wangxun 10 Gigabit Ethernet NIC and
Intel NIC - i40e, ice, iavf, ixgbe, igb
To enable HW VLAN INSERT one needs to check
if DEV_TX_OFFLOAD_VLAN_INSERT by checking get_dev_info
configure tx offload for the port with DEV_TX_OFFLOAD_VLAN_INSERT
enable MBUF descriptor with ol_flags = PKT_TX_VLAN and vlan_tci = [desired TCI in big-endian format]
This will allow the driver code in xmit function to check mbuf descriptors ol_flags for PKT_TX_VLAN and enables VLAN Insert offload to Hardware by registering the appropriate command with the Packet Descriptor before DMA.
From DPDK conditions are to be satisfied
at a given instance there should only be 1 thread access and updating the mbuf.
no modification for the mbuf is to be done on the original mbuf (with payload).
If the intention is to perform VLAN insert via SW (especially if HW or virtual NIC PMD does not support), in dpdk one has to do the following
Ensure the refcnt is 1 to prevent multiple thread access and modification on the intended buffer.
There is enough headroom to shift the packet 4 bytes to accommodate the Ether type and VLAN values.
ensure pkt_len and data_len are in bound (greater than 60 bytes and less than 4 bytes of MTU)
MBUF offload descriptors is not enabled for PKT_TX_VLAN
update data_len on the modified MBUF by 4 Bytes.
Update total pkt_len by 4.
(optional for performance consideration) prefetch the 4 bytes prior to mtod of mbuf memory address
Note: All the above things are easily achieved by using the DPDK function rte_vlan_insert. TO use the same follow the steps as
Do not configure the port with DEV_TX_OFFLOAD_VLAN_INSERT.
Update ol_flags with PKT_TX_VLAN and vlan_tci desired value.
Invoke rte_vlan_insert with the mbuf
Sample code:
/* Get burst of RX packets, from first port of pair. */
struct rte_mbuf *bufs[BURST_SIZE];
const uint16_t nb_rx = rte_eth_rx_burst(port, 0, bufs, BURST_SIZE);
if (unlikely(nb_rx == 0))
continue;
for (int i = 0; i < nb_rx; i++) {
bufs[i]->ol_flags = PKT_TX_VLAN;
bufs[i]->vlan_tci = 0x10;
rte_vlan_insert(&bufs[i]);
}
/* Send burst of TX packets, to second port of pair. */
const uint16_t nb_tx = rte_eth_tx_burst(port, 0,
bufs, nb_rx);
/* Free any unsent packets. */
if (unlikely(nb_tx < nb_rx)) {
uint16_t buf;
for (buf = nb_tx; buf < nb_rx; buf++)
rte_pktmbuf_free(bufs[buf]);
}

Get Peer IP Address and Port

I manage to get the host information from particular requested network adapter using code below but i have no idea how to get peer ip address from host ip address.
struct sockaddr_in *sa = (struct sockaddr_in *)&item->ifr_addr;
ipAddr = ntohl(*((u_int32_t *)&sa->sin_addr));
if (pIpAddr != NULL)
{
*pIpAddr = ipAddr;
}
// Get the MAC address
if ( ioctl(s, SIOCGIFHWADDR, item) < 0 )
{
printf("_GetMacAddress : SIOCGIFHWADDR failed!\n");
return 0;
}
else
{
struct sockaddr *eth = (struct sockaddr *) &item->ifr_ifru.ifru_hwaddr;
unsigned long *low = (unsigned long *)&eth->sa_data[2];
unsigned short *high = (unsigned short*)&eth->sa_data[0];
//printf("%s : MAC = 0x%04x, 0x%08x", ntohs(*high), ntohl(*low));
printf("Interface %8s : IP %3d.%3d.%3d.%3d : MAC = %02x:%02x:%02x:%02x:%02x:%02x\n",
item->ifr_name,
((ipAddr >> 24)&0xff), ((ipAddr >> 16)&0xff), ((ipAddr >> 8)&0xff), (ipAddr&0xff),
((ntohs(*high)>> 8)&0x00ff), (ntohs(*high)&0x00ff),
((ntohl(*low)>> 24)&0x00ff), ((ntohl(*low)>> 16)&0x00ff), ((ntohl(*low)>> 8)&0x00ff), (ntohl(*low)&0x00ff));
if ((pMacHigh != NULL) && (pMacLow != NULL))
{
*pMacHigh = *high;
*pMacLow = *low;
}
}
The output is:
_GetMaxNetworkInterfaces 3
Interface lo : IP 127. 0. 0. 1 : MAC = 00:00:00:00:00:00
Interface enp4s0 : IP 192.168.128. 88 : MAC = f4:8e:38:ea:88:23
Interface wlp5s0 : IP 192.168. 53. 63 : MAC = b8:81:98:b7:71:90
If you are server listening for incoming connection, you can get peer address from accept() , in the second its argument, when a new socket is accepted
From man page of accept():
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
The argument addr is a pointer to a sockaddr structure. This structure is filled in with the address of the peer socket, as known to the communications layer. The exact format of the address returned addr is determined by the socket's address family (see socket(2) and the respective protocol man pages). When addr is NULL, nothing is filled in; in this case, addrlen is not used, and should also be NULL.
I have no idea how to get peer ip address from host ip address.
You can't. The question doesn't make sense.
To get a peer address you have to have a peer, and to have a peer you have to have a connected socket, with which you can call getpeername().
Or, if you're a server, you can get it as a side-effect of accept() via the second and third arguments.
I did tried to implement arp-scan to find the connected client Ip address.
First we have to define the max device connected for my case will be 32. I manage to get following information from previous code
Interface lo : IP 127. 0. 0. 1 : MAC = 00:00:00:00:00:00
Interface enp4s0 : IP 192.168.128. 88 : MAC = f4:8e:38:ea:88:23
Interface wlp5s0 : IP 192.168. 53. 63 : MAC = b8:81:98:b7:71:90
The information that require by arp-scan is interface name and the device IP address. In order to get client ip address we have to remove the character after counting the third dot. The end result we will get is:
127.0.0.
192.168.128.
192.168.53.
the last step is to loop for max device
127.0.0.0~31
192.168.128.0~31
192.168.53.0~31
Then feed into arp-scan ping and we will get the response from client ip address. I am using this arp-scan reference.

Neighbor solicitation sent instead of ICMP6 echo resquest

I'm trying to send an ICMPV6 echo request. Bellow my code:
struct icmp6_hdr icmp6;
int sock;
struct icmp6_filter filterv6;
struct ifreq ifr;
sock = socket(AF_INET6, SOCK_RAW,IPPROTO_ICMPV6);
ICMP6_FILTER_SETBLOCKALL(&filterv6);
ICMP6_FILTER_SETPASS(ICMP6_DST_UNREACH, &filterv6);
ICMP6_FILTER_SETPASS(ICMP6_PACKET_TOO_BIG, &filterv6);
ICMP6_FILTER_SETPASS(ICMP6_TIME_EXCEEDED, &filterv6);
ICMP6_FILTER_SETPASS(ICMP6_PARAM_PROB, &filterv6);
ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filterv6);
ICMP6_FILTER_SETPASS(ND_REDIRECT, &filterv6);
setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filterv6, sizeof (filterv6));
...
setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof ifr);
...
icmp6.icmp6_type = ICMP6_ECHO_REQUEST;
icmp6.icmp6_code = 0;
icmp6.icmp6_cksum = 0;
icmp6.icmp6_id = id;
icmp6.icmp6_seq = 100;
if( (sendto(sock, &icmp6, sizeof(struct icmp6_hdr), 0, (struct sockaddr *)dest, socklen)) != sizeof(struct icmp6_hdr))
However, for an unknown reason, the sent packet is an NDS:
[root#jingo ~]# tcpdump -v -i any -s0 | grep icmp6
tcpdump: WARNING: Promiscuous mode not supported on the "any" device
tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
11:57:08.397368 IP6 (hlim 255, next-header: ICMPv6 (58), length: 32) 2001:db8:0:85a3::ac1f:8003 > ff02::1:ff1f:8009: [icmp6 sum ok] ICMP6, neighbor solicitation, length 32, who has 2001:db8:0:85a3::ac1f:8009
11:57:09.397331 IP6 (hlim 64, next-header: ICMPv6 (58), length: 112) 2001:db8:0:85a3::ac1f:8003 > 2001:db8:0:85a3::ac1f:8003: [icmp6 sum ok] ICMP6, destination unreachable, length 112, unreachable address 2001:db8:0:85a3::ac1f:8009
I'm using 2.6.18-308.el5PAE kernel , Red Hat Enterprise Linux Server release 5.1 (Tikanga).
This is normal behavior.
Since you can't send IP traffic until you have the correct MAC address to direct packets to, something has to find that MAC address. In IPv4, you would have seen an ARP packet. NDP (neighbor discovery protocol) replaced ARP in IPv6, which is why you're seeing NDP traffic.
The real problem here is that the destination host is not reachable. It may be down, or the router may not know how to reach it. Your router might be configured incorrectly, but that seems unlikely.
Try pinging a host that is up, and you will see the NDP traffic followed by your ICMP echo request.

TCPDump / libpcap - find memory location of payload data

I am trying to view http traffic going to and from my loopback network adapter using libpcap. I just beginning with network programming and completely new to this library. Thanks to an answer I received previously I have been successful at detecting the link-layer type on my machine's "lo0" adapter (Mac OSx).
//lookup link-layer header type
link_layer_type = pcap_datalink(handle);
if(link_layer_type == DLT_NULL){
printf("DLT_NULL"); // this true in the case of "lo0"
}
The Programming with Pcap guide makes the assumption that each packet will contain an ethernet header. So the logic used to find a packet's payload is as follows:
ethernet = (struct sniff_ethernet*)(packet);
ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
size_ip = IP_HL(ip)*4;
if (size_ip < 20) {
printf(" * Invalid IP header length: %u bytes\n", size_ip);
return;
}
tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip);
size_tcp = TH_OFF(tcp)*4;
if (size_tcp < 20) {
printf(" * Invalid TCP header length: %u bytes\n", size_tcp);
return;
}
}
payload = (u_char *)(packet + SIZE_ETHERNET + size_ip + size_tcp);
This logic is clearing not going to work when inspecting the contents of packet originating from the loopback interface where an ethernet header does not exists. The Link-Layer Header Types documentation states that a Link-Layer type of "DTL_NULL" contains a 4 byte header which consist of a PF_ value containing the network-layer protocol (I'm guess IPv4 in my case).
Given the above information.. how can I properly locate the packet's payload location?
Any guidance or information would be very appreciated. Thanks!
Given the above information.. how can I properly locate the packet's payload location?
For DLT_NULL, your program should extract the first 4 bytes of the packet data as a 32-bit number. If you're doing a live capture, you can extract it in the host's byte order and compare it against your OS's values of AF_INET and AF_INET6 (if it has an AF_INET6 definition; these days, most current OS versions should, as they should support IPv6); if you're reading a capture file, you'd need to byte-swap the value if pcap_is_swapped() returns a non-zero value (you can also use it for live captures; it always returns zero for live captures), and you'll need to compare against several different "IPv6" values (24, 28, and 30), each of which mean "IPv6" on some particular OS (fortunately, AF_INET is 2 on all OSes that support DLT_NULL, as they all took that value from 4.2BSD).
If the value is the IPv4 value (2, as per the above), then after those 4 bytes you have the IPv4 header for the packet. If it's one of the IPv6 values, then after those 4 bytes you have the IPv6 header for the packet. If it's not any of those values, it's some other protocol.

Filtering packets in pcap dump file

I'm writing network analyzer and I need to filter packets saved in file, I have written some code to filter http packets but I'm not sure if it work as it should because when I use my code on a pcap dump the result is 5 packets but in wireshark writing http in filter gives me 2 packets and if I use:
tcpdump port http -r trace-1.pcap
it gives me 11 packets.
Well, 3 different results, that's a little confusing.
The filter and the packet processing in me code is:
...
if (pcap_compile(handle, &fcode, "tcp port 80", 1, netmask) < 0)
...
while ((packet = pcap_next(handle,&header))) {
u_char *pkt_ptr = (u_char *)packet;
//parse the first (ethernet) header, grabbing the type field
int ether_type = ((int)(pkt_ptr[12]) << 8) | (int)pkt_ptr[13];
int ether_offset = 0;
if (ether_type == ETHER_TYPE_IP) // ethernet II
ether_offset = 14;
else if (ether_type == ETHER_TYPE_8021Q) // 802
ether_offset = 18;
else
fprintf(stderr, "Unknown ethernet type, %04X, skipping...\n", ether_type);
//parse the IP header
pkt_ptr += ether_offset; //skip past the Ethernet II header
struct ip_header *ip_hdr = (struct ip_header *)pkt_ptr;
int packet_length = ntohs(ip_hdr->tlen);
printf("\n%d - packet length: %d, and the capture lenght: %d\n", cnt++,packet_length, header.caplen);
}
My question is why there are 3 different result when filtering the http? And/Or if I'm filtering it wrong then how can I do it right, also is there a way to filter http(or ssh, ftp, telnet ...) packets using something else than the port numbers?
Thanks
So I have figured it out. It took a little search and understanding but I did it.
Wireshark filter set to http filter packets that have set in tcp port 80 and also flags set to PSH, ACK. After realizing this, the tcpdump command parameters which result in the same numbers of packets was easy to write.
So now the wireshark and tcpdump gives the same results
What about my code? well I figured that I actually had an error in my question, the filter
if (pcap_compile(handle, &fcode, "tcp port 80", 1, netmask) < 0)
indeed gives 11 packets (src and dst port set to 80 no matter what tcp flags are)
Now to filter the desired packets is a question of good understanding the filter syntax
or setting to filter only port 80 (21,22, ...) and then in callback function or in while loop get the tcp header and from there get the flags and use mask to see if it is the correct packet (PSH, ACK, SYN ...) the flags number are for example here

Resources