pcap ethertype not consistent with ip version - c++

I'm using pcap to capture ip (both v4 and v6) packets on my router. It works just fine but I've noticed that sometimes the ethertype of an ethernet frame (LINKTYPE_ETHERNET) or a linux cooked capture encapsulation (LINKTYPE_LINUX_SLL) does not correctly indicate the version of the ip packet they contain.
I was expecting that if I get a frame whose ethertype is 0x0800 (ETHERTYPE_IP) then it should contain an ipv4 packet with version == 4 and if I get a frame whose ethertype is 0x86DD (ETHERTYPE_IPV6) then it should contain an ipv6 packet with version == 6.
Most of the time the above is true but sometimes it's not. I would get a frame whose ethertype is ETHERTYPE_IP but somehow it contains an ipv6 packet or I get a frame whose ethertype is ETHERTYPE_IPV6 but it contains an ipv4 packet.
I seem to have heard "ipv4 over ipv6" or "ipv6 over ipv4" but I don't know exactly how they work or if they apply to my problem, but otherwise I'm not sure what's causing this inconsistency.
EDIT
I think my actually question is whether such behavior is normal. If so should I simply ignore the ethertype field and just check the version field in the ip header to determine if it's ipv4 or ipv6.

According to my (limited) understanding, both IPv4 and IPv6 can appear after an IPv4 (0x800) Ethernet type (ethtype). This is related to transmitting IPv4 packets over IPv6. When the ethtype is 0x800 and the IP header is version 6, then the address in the IP header is an IPv4 address mapped to IPv6.
One example that shows this is Linux UDP receive code, which checks for ethtype 0x800 and then converts the source address to ipv4 using ipv6_addr_set_v4mapped

Related

No DPDK packet fragmentation supported in Mellanox ConnectX-3?

Hello Stackoverflow Experts,
I am using DPDK on Mellanox NIC, but am struggling with applying the packet
fragmentation in DPDK application.
sungho#c3n24:~$ lspci | grep Mellanox
81:00.0 Ethernet controller: Mellanox Technologies MT27500 Family
[ConnectX-3]
the dpdk application(l3fwd, ip-fragmentation, ip-assemble) did not
recognized the received packet as the ipv4 header.
At first, I have crafted my own packets when sending ipv4 headers so I
assumed that I was crafting the packets in a wrong way.
So I have used DPDK-pktgen but dpdk-application (l3fwd, ip-fragmentation,
ip-assemble) did not recognized the ipv4 header.
As the last resort, I have tested the dpdk-testpmd, and found out this in
the status info.
********************* Infos for port 1 *********************
MAC address: E4:1D:2D:D9:CB:81
Driver name: net_mlx4
Connect to socket: 1
memory allocation on the socket: 1
Link status: up
Link speed: 10000 Mbps
Link duplex: full-duplex
MTU: 1500
Promiscuous mode: enabled
Allmulticast mode: disabled
Maximum number of MAC addresses: 127
Maximum number of MAC addresses of hash filtering: 0
VLAN offload:
strip on
filter on
qinq(extend) off
No flow type is supported.
Max possible RX queues: 65408
Max possible number of RXDs per queue: 65535
Min possible number of RXDs per queue: 0
RXDs number alignment: 1
Max possible TX queues: 65408
Max possible number of TXDs per queue: 65535
Min possible number of TXDs per queue: 0
TXDs number alignment: 1
testpmd> show port
According to DPDK documentation.
in the flow type of the info status of port 1 should show, but mine shows
that no flow type is supported.
The below example should be the one that needs to be displayed in flow types:
Supported flow types:
ipv4-frag
ipv4-tcp
ipv4-udp
ipv4-sctp
ipv4-other
ipv6-frag
ipv6-tcp
ipv6-udp
ipv6-sctp
ipv6-other
l2_payload
port
vxlan
geneve
nvgre
So Is my NIC, Mellanox Connect X-3 does not support DPDK IP fragmentation? Or is
there additional configuration that needs to be done before trying out the packet fragmentation?
-- [EDIT]
So I have checked the packets from DPDK-PKTGEN and the packets received by DPDK application.
The packets that I receive is the exact one that I have sent from the application. (I get the correct data)
The problem begins at the code
struct rte_mbuf *pkt
RTE_ETH_IS_IPV4_HDR(pkt->packet_type)
This determines the whether the packet is ipv4 or not.
and the value of pkt->packet_type is both zero from DPDK-PKTGEN and DPDK application. and if the pkt-packet_type is zero then the DPDK application reviews this packet as NOT IPV4 header.
This basic type checker is wrong from the start.
So what I believe is that either the DPDK sample is wrong or the NIC cannot support ipv4 for some reason.
The data I received have some pattern at the beginning I receive the correct message but after that sequence of packets have different data between the MAC address and the data offset
So what I assume is they are interpreting the data differently, and getting the wrong result.
I am pretty sure any NIC, including Mellanox ConnectX-3 MUST support ip fragments.
The flow type you are referring is for the Flow Director, i.e. mapping specific flows to specific RX queues. Even if your NIC does not support flow director, it does not matter for the IP fragmentation.
I guess there is an error in the setup or in the app. You wrote:
the dpdk application did not recognized the received packet as the ipv4 header.
I would look into this more closely. Try to dump those packets with dpdk-pdump or even by simply dumping the receiving packet on the console with rte_pktmbuf_dump()
If you still suspect the NIC, the best option would be to temporary substitute it with another brand or a virtual device. Just to confirm it is the NIC indeed.
EDIT:
Have a look at mlx4_ptype_table for fragmented IPv4 packets it should return packet_type set to RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN | RTE_PTYPE_L4_FRAG
Please note the functionality was added in DPDK 17.11.
I suggest you to dump pkt->packet_type on console to make sure it is zero indeed. Also make sure you have the latest libmlx4 installed.

How to fix 'Cannot assign requested address?'

Both the IP address and Port are confirmed not used by netstat -a -n. When I use gdb and break in the method calling bind I see that the correct IP address and Port are being used along with a reasonable socket address length of 16. This is for a UDP Listener. The remote IP is static and read from a configuration file.
This is the code,
void CSocket::Bind(IpEndPoint& endPoint)
{
int bindResult = bind( socketHandle, endPoint.GetSockAddrPtr(),
endPoint.GetAddrLength());
if(bindResult < 0)
{
TRACE_ERROR("Failed to bind to socket. %s. IpAddress %s Port %d AddrLength %d",
strerror(errno), endPoint.GetIpAddressString(),
ntohs(endPoint.GetPort()), endPoint.GetAddrLength());
this is from gdb,
Breakpoint 1, CSocket::Bind (this=0x819fa24, ipAddress="192.0.2.77",
port=4185) at Socket.cpp:126
and this is the TRACE_ERROR from the code above
ERROR: Failed to bind to socket. errno 99 (Cannot assign requested address).
IpAddress 192.0.2.77 Port 4185 AddrLength 16
I've been re-reading Beej's Guide to Network Programming but not finding a clue. This is UDP so a connection should not be required to bind. The firewall is off. Where else should I be looking?
Following on what #Aconcagua said: You want to bind an address that is local (not one that's "not in use"). You can't just make up a local address. You either use INADDR_ANY to bind to any address, or you need to bind one that is assigned to one of your local interfaces. This is likely the problem. (bind sets the local address, connect sets the remote address -- or, with UDP, you can specify the remote address per packet with sendto.) – Gil Hamilton

How to resolve the real broadcast address for a IPv4 address with Boost.Asio

Is there any possibility to obtain the "real local" broadcast address for a given IPv4 address with Boost.Asio?
ip::address_v4::netmask only returns the netmask based on the IPv4 address class, so it cannot be used reliably with the overload of ip::address_v4::broadcast.
I need to resolve a specific broadcast address for a given (existing) host IPv4 address on the local machine, since I have trouble using an UDP endpoint with a broadcast address of 255.255.255.255.
Edit: For clarification, I do not want to resolve a hostname to an IP address or vice versa.
Edit: Here is an example:
eth0:
host address: 192.168.0.1
net mask...: 255.255.0.0 (**not** class C)
So I want to retrieve the broadcast address 192.168.255.255, but the only input I want to specify is the local net address. Therefore "resolving" or "querying" is the correct term, since I do not want to calculate it.
I have a UDPv4 server, which I bind to a specific local endpoint (by specifying the local IPv4 host address). I want to use this server in both unicast mode and broadcast mode, so I need to specify the remote endpoint as well. I am unable to do this with the "global broadcast" address returned by ip::address_v4::broadcast, because that always returns 255.255.255.255 which leads to undesired behaviour. In addition to the host address I specify a flag to correctly set the socket option to enable broadcasting via basic_datagram_socket::broadcast.
If you have an example how-to achieve this, I would be grateful. Maybe I am thinking too complicated...
I think indeed the boost::asio::ip::address_v4::broadcast overloads are what you are after:
#include <boost/asio.hpp>
#include <iostream>
using boost::asio::ip::tcp;
int main() {
typedef boost::asio::ip::address_v4 A;
A address = A::from_string("192.168.0.1");
std::cout << "Address " << address << " has broadcast address " << A::broadcast(address, A::from_string("255.255.0.0")) << "\n";
}
Prints:
Address 192.168.0.1 has broadcast address 192.168.255.255
See it Live On Coliru
Note Note: if the confusion is from 192.168.0.1 being classified as Class C, this is by design.
IANA defines the 16-bit block 192.168.0.0/16 as "256 contiguous class C networks" (look at the Classful description here The footnote is enlightening, and the Asio implementation matches it).

get SOCK_RAW frames with different rate [duplicate]

I'm writing code to send raw Ethernet frames between two Linux boxes. To test this I just want to get a simple client-send and server-receive.
I have the client correctly making packets (I can see them using a packet sniffer).
On the server side I initialize the socket like so:
fd = socket(PF_PACKET, SOCK_RAW, htons(MY_ETH_PROTOCOL));
where MY_ETH_PROTOCOL is a 2 byte constant I use as an ethertype so I don't hear extraneous network traffic.
when I bind this socket to my interface I must pass it a protocol again in the socket_addr struct:
socket_address.sll_protocol = htons(MY_ETH_PROTOCOL);
If I compile and run the code like this then it fails. My server does not see the packet. However if I change the code like so:
socket_address.sll_protocol = htons(ETH_P_ALL);
The server then can see the packet sent from the client (as well as many other packets) so I have to do some checking of the packet to see that it matches MY_ETH_PROTOCOL.
But I don't want my server to hear traffic that isn't being sent on the specified protocol so this isn't a solution. How do I do this?
I have resolved the issue.
According to http://linuxreviews.org/dictionary/Ethernet/ referring to the 2 byte field following the MAC addresses:
"values of that field between 64 and 1522 indicated the use of the new 802.3 Ethernet format with a length field, while values of 1536 decimal (0600 hexadecimal) and greater indicated the use of the original DIX or Ethernet II frame format with an EtherType sub-protocol identifier."
so I have to make sure my ethertype is >= 0x0600.
According to http://standards.ieee.org/regauth/ethertype/eth.txt use of 0x88b5 and 0x88b6 is "available for public use for prototype and vendor-specific protocol development." So this is what I am going to use as an ethertype. I shouldn't need any further filtering as the kernel should make sure to only pick up ethernet frames with the right destination MAC address and using that protocol.
I've worked around this problem in the past by using a packet filter.
Hand Waving (untested pseudocode)
struct bpf_insn my_filter[] = {
...
}
s = socket(PF_PACKET, SOCK_DGRAM, htons(protocol));
struct sock_fprog pf;
pf.filter = my_filter;
pf.len = my_filter_len;
setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &pf, sizeof(pf));
sll.sll_family = PF_PACKET;
sll.sll_protocol = htons(protocol);
sll.sll_ifindex = if_nametoindex("eth0");
bind(s, &sll, sizeof(sll));
Error checking and getting the packet filter right is left as an exercise for the reader...
Depending on your application, an alternative that may be easier to get working is libpcap.

getaddrinfo() returning only ::1 as IPV6 address,

I'm using getaddrinfo() to return all assigned IP addresses (both IPv4 and
IPv6) for my local machine. I see that on XP, getaddrinfo() only returns ::1
( I installed the IPV6 stack on 2 XP machine and configured the IPV6 address and pinged the both peers. they are working fine. I check the Ipconfig its all working fine.
)
I believe that Gonzalo is on the right track. ::1 is a shorthand for localhost from what I understand . . . In just about every case the IPV6 localhost ::1 shows up first when iterating through the returned list . . .
Well, in the ::1 address (or, rather, in any address, that has a double colon in it) double colon expands into the number of zero-bits, neccessary to pad the address to full length, so the expanded version looks like 0000:0000:0000:0000:0000:0000:0000:0001.
In ipv6 this is the only address, that is specifically defined as a loopback address (unlike ipv4, where you get 127.0.0.0/8 for those purposes).
(Are you sure, that you are actually iterating over the result and not just checking the first element of the linked list?)
I'd advise to stay away from the dual stack configurations on Windows XP and 2003. The stacks just don't play nice with each other. If you want IPv6 use Windows 2008 R2 or Windows 7.