Error getting WinPcap address information - c++

Hello i am having some problem getting network card address information from the WinPcap driver. I have noticed that whenever IPv6 is enabled for a particular NIC, the address information for it becomes 0.0.0.0. How can i fix this?
pcap_addr* address = GetDeviceAddress(1);
cout<<"IP address "<<iptos(((struct sockaddr_in *)address->addr)->sin_addr.s_addr)<<endl;
/////////////
pcap_addr* GetDeviceAddress(int index)//Gets the name of a device using the zero based index of its location in the list
{
int i=0;
if(pcap_findalldevs_ex(PCAP_SRC_IF_STRING,NULL,&AllDevices,errbuf) != -1)
for(Iterator=AllDevices;Iterator!=NULL;Iterator=Iterator->next)
{
if(i==index)
{
return Iterator->addresses;
}
i++;
}
return NULL;
}

What does your code that's looking at the address information look like? Perhaps it has a bug that causes it not to properly handle IPv6 addresses.
The address information may have more than one IP address - libpcap/WinPcap's pcap_findalldevs() call and WinPcap's pcap_findalldevs_ex() call supply a pointer to a list of pcap_if_t structures, one for each interface that it found, and each of those structures includes a list of address information items of type struct pcap_addr. Each of those address information items includes a network address, which is of type struct sockaddr; that structure includes a member named sa_family, which will have the value AF_INET for IPv4 and AF_INET6 for IPv6. If the member has the value AF_INET6, the address data will be 128 bits of IPv6 address, NOT 32 bits of IPv4 address.

Related

Where is the ip address in IP_ADAPTER_ADDRESSES_LH

Per this question:
How to get local IP address of Windows system?
#Remy Lebeau answered that GetAdaptersAddresses() was a way to get the IP address of the local machine in Windows using C++.
I compiled the example and the example does not print out the local IP address of the machine. I took a look at the struct that the function returns (IP_ADAPTER_ADDRESSES_LH) and I was surprised to find that I did not see any references to where the actual IP address is.
My question is, where is the IP address located in the IP_ADAPTER_ADDRESSES_LH struct?
I compiled the example and the example does not print out the local IP address of the machine.
GetAdaptersAddresses() has always worked fine for me.
where is the IP address located in the IP_ADAPTER_ADDRESSES_LH struct?
There are potentially many IP addresses in the struct, depending on what kind of IP address you are interested in - Unicast, Anycast, Multicast, or DnsServer. For local assigned IPs, you would typically use the Unicast addresses only:
The IP_ADAPTER_ADDRESSES_LH::FirstUnicastAddress field points to a linked list of IP_ADAPTER_UNICAST_ADDRESS_LH structs, one entry for each IP address. Use the IP_ADAPTER_UNICAST_ADDRESS_LH::Next field to loop through the list (the MSDN example shows such a loop, but it only counts the number of elements in the list, it does not print out the content of the list).
The IP_ADAPTER_UNICAST_ADDRESS_LH::Address field contains the actual IP address, in SOCKET_ADDRESS format.
the SOCKET_ADDRESS::lpSockaddr field is a SOCKADDR* pointer. You can pass it as-is to socket APIs like bind().
If you want to do something with the IP address (like display it), you have to type-cast the SOCKET_ADDRESS::lpSockAddr pointer to either sockaddr_in* or sockaddr_in6*, depending on whether the IP address is IPv4 or IPv6, respectively (use the SOCKADDR::sa_family field to determine the correct type - AF_INET for sockaddr_in, AF_INET6 for sockaddr_in6). Then you can access the sockaddr_in::sin_addr or sockaddr_in6::sin6_addr field as needed, which contain the actual bytes of the IP address.
For example:
PIP_ADAPTER_ADDRESSES pAddresses = ...; // allocate buffer as needed...
ULONG ulSize = ...; // size of allocated buffer...
if (GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME, NULL, pAddresses, &ulSize) == 0)
{
for (PIP_ADAPTER_UNICAST_ADDRESS_LH *pAddress = pAddresses->FirstUnicastAddress;
pAddress != NULL;
pAddress = pAddress->Next)
{
SOCKADDR *pSockAddr = pAddress->Address.lpSockaddr;
switch (pSockAddr->sa_family)
{
case AF_INET: {
sockaddr_in *pInAddr = (sockaddr_in*) pSockAddr;
// use pInAddr->sin_addr as needed...
break;
}
case AF_INET6: {
sockaddr_in6 *pIn6Addr = (sockaddr_in6*) pSockAddr;
// use pIn6Addr->sin6_addr as needed...
break;
}
}
}
}
// free pAddresses as needed ...

Unwanted side-effects when modifying IP address with ioctl

Question:
Is there a way of using ioctl to change only a desired interface component without affecting the other parts of the network interface?
Reasoning
I'm writing a C++ program that allows a user to change the IP address, Broadcast address, Netmask, and Default Gateway independently of one another on a Linux machine. I modified this code for the IP, Bcast and NMask solution. However, changing the IP address with ioctl is automatically modifying my Broadcast/Netmask, and clearing the Kernel IP Routing table.
Here's an example. Before running the code below, this is the result of ifconfig and route -n:
This is a functional version of the code that modifies the IP address:
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <string.h>
#include <iostream>
int main(int argc, const char *argv[]) {
struct ifreq ifr;
struct sockaddr_in* addr = (struct sockaddr_in*)&ifr.ifr_addr;
const char * name = "eth0";
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
strncpy(ifr.ifr_name, name, IFNAMSIZ);
ifr.ifr_addr.sa_family = AF_INET;
inet_pton(AF_INET, "10.10.2.59", &addr->sin_addr);
if(ioctl(fd, SIOCSIFADDR, &ifr) < 0)
{
std::cout << "Failed to set IP: " << strerror(errno) << std::endl;
return -1;
}
if(ioctl(fd, SIOCGIFFLAGS, &ifr) < 0)
{
std::cout << "Failed to get flags: " << strerror(errno) << std::endl;
return -2;
}
strncpy(ifr.ifr_name, name, IFNAMSIZ);
ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
if(ioctl(fd, SIOCSIFFLAGS, &ifr) < 0)
{
std::cout << "Failed to set flags: " << strerror(errno) << std::endl;
return -3;
}
return 0;
}
This is the resulting network card status after running the above program:
As you can see, the IP address was modified as desired, but the rest of the interface changed (which is not desirable).
I've been unable to find anything on the internet or the netdevice man page about preventing ioctl from automatically modifying other parts of the network interface.
I know I could set the values of struct ifreq so the Bcast and Mask components don't change, but I would prefer to be able to modify each component individually without worrying about the values of the other ones. I especially don't want to keep track of the default gateway and have to add it in every time I make a change to the IP address.
UPDATE
After doing more tests and research, I've found that this issue still occurs when running the system commands ifconfig or ip. For example, if instead of running the above code one ran ifconfig eth0 10.10.2.59 the result is the same.
Using the ip command is somewhat different, as changing the ip address requires running ip addr del 10.10.2.58/16 dev eth0 && ip addr add 10.10.2.59/16 dev eth0. Thus, you delete a known address/netmask combo and add another. Since the broadcast address was not specified, it is set to 0.0.0.0. However, this command still removes the default gateway from the routing table.
It seems (through experimentation) that there is some form of prioritized ordering of ioctl flags in how they affect the rest of the network interface:
SIOCSIFADDR forces a reset of the Netmask, Broadcast Address, and Routing Table entries for the given network interface
SIOCSIFNETMASK forces a reset of the Broadcast Address and Routing Table
SIOCSIFBRDADDR forces a reset of only the Routing Table
The implication of this is that the order in which you run ioctl calls matters - first you must set IP, then Subnet Mask, then Broadcast Address, then any Routes. Otherwise ioctl automatically overwrites changes you made previously.
Because of that, I ended up having to keep track of all sub-components that were affected when changing only part of the network interface.
For example, if the subnet mask is going to be changed I first read the old Broadcast Address (an ioctl call with the SIOCGIFBRDADDR flag) and Default Gateway (see here on reading that programatically) and store them. Then after changing the subnet mask with ioctl, I re-assigned the Broadcast Address and Default Gateway (in that order) and it appears to the user that only the subnet mask was changed.
This doesn't exactly answer the original question, but I couldn't find any other way of modifying only one component of a network interface without affecting the others. If anyone finds a better way of doing it I would be very happy to know.

How to know if a TCP connection is between two processes on the same machine?

Using socket programming APIs (e.g., socket(), connect(), accept() ...), how can I know if a TCP connection is between two processes on the same machine? Say, I have the socket file descriptor, and the remote ip. Can I simply inspect if the remote ip is 127.0.0.1?
There's no really reliable way to determine this - you can connect to local processes using a globally routed IP address (ie, local processes can use IPs other than 127.0.0.1). It's also possible for a process to run in a different virtual machine on the same physical hardware, if you're in a virtualized environment.
Note, however, that if the remote IP (via getpeername) or local IP (via getsockname) starts with 127 (including 127.0.0.1), then it is indeed a local connection; however, you can't rule out the possibility that it might be a local connection if it's a different pair of addresses.
Use getsockname() and getpeername() to retreive the two IPs associated with the connection, then use gethostname() and gethostbyname() (or other platform-specific APIs, like GetAdaptersInfo() and GetAdapterAddresses() on Windows) to determine the IPs that belong to the local machine, then you can compare the connection IPs to the local machine IPs to see if they both match. A machine can have multiple IPs assigned to it, and multiple IPs on the same machine can communicate with each other.
Here is the approach I have used. The idea is to attempt to bind a listener to that IP address and use the failure/success codes to decide whether the address is local.
I am not claiming this is particularly efficient, but it should be fairly reliable, and for my application it was appropriate.
#include <sys/socket.h>
#include <errno.h>
/* ...probably need some other headers I am forgetting... */
int
is_local(const struct sockaddr *addr, socklen_t addr_len)
{
const char *func = "is_local()";
int result = 0;
int tmp = socket(addr->sa_family, SOCK_STREAM, 0);
if (tmp < 0) {
printf("%s: socket(%d,SOCK_STREAM,0) failed, errno %d\n",
func, addr->sa_family);
goto out;
}
/* If bind() succeeds, or if it fails because the address is in
use, then the address must be local to this system. */
if (bind(tmp, addr, addr_len) < 0) {
if (errno == EADDRINUSE)
result = 1;
else if (errno == EADDRNOTAVAIL)
; /* do nothing; address is remote */
else
printf("%s: bind() unexpected error %d\n", func, errno);
}
else {
result = 1;
}
close(tmp);
out:
return result;
}
You call it like this:
struct sockaddr_storage client_addr;
socklen_t client_addr_len = sizeof(client_addr);
int fd = accept(listener, &client_addr, &client_addr_len);
if (is_local((struct sockaddr *)&client_addr, client_addr_len))
/* peer is local */
If you already have the remote ip address, you can check if it is the loopback address or if it is the ip address of the host, because, as cnicutar points out, it doesn't have to be over the loopback address to be a local connection.

C++ Linux getpeername and getsockname return only port

In my Linux C++ application I'm using getpeername and getsockname.
when IPv6 enabled on the OS, both getpeername and getsockname return only port!
code:
int GetSockAndPeer(int sock)
{
struct sockaddr_storage ss;
socklen_t salen = sizeof(ss);
struct sockaddr *sa;
struct addrinfo hints, *paddr, *paddrp;
sa = (struct sockaddr *)&ss;
if (getpeername(sock, sa, &salen) != 0)
{
return -1;
}
if (getsockname(sock, sa, &salen) != 0)
{
return -1;
}
}
sa variable hold after the systemcalls in sa_data only the sa_data[0] and sa_data[1] which means port. all the other bytes are 0;
Any help???
Related to RFC2553 you have to use the IN6_IS_ADDR_V4MAPPED and IN6_IS_ADDR_V4COMPAT macros to identify if there is any usable IPv4 information available within yours socket_storage, or to be exact the sockaddr_in6 structure:
struct sockaddr_in6 {
sa_family_t sin6_family; /* AF_INET6 */
in_port_t sin6_port; /* transport layer port # */
uint32_t sin6_flowinfo; /* IPv6 traffic class & flow info */
struct in6_addr sin6_addr; /* IPv6 address */
uint32_t sin6_scope_id; /* set of interfaces for a scope */
};
If both macros returns true, the IPv4 address is in sockaddr_in6.sin6_addr[12-15]:
printf("%u.%u.%u.%u\n", sockaddr_in6.sin6_addr[12], sockaddr_in6.sin6_addr[13], \
sockaddr_in6.sin6_addr[14], sockaddr_in6.sin6_addr[15])
It's important to remember that, unless a socket is connected (or, for a connectionless socket, has transferred data), there may not be any IP addresses, local or remote, associated with the socket.
Let's say the computer is multihomed and has both local and Internet IP addresses. Maybe even multiple local network IP addresses. If you choose to bind a socket to "any" local address (using an INADDR_ANY-type flag), or never call bind() in the first place, the socket API does not have a single local IP address associated with the socket, just a port number at the most. When you call connect() on a socket, the system chooses which local IP to use based on who you are connecting to. So if you connect to a machine over the Internet, your Internet IP is associated with the socket, and if you connect to a machine on the local network, your LAN IP address is used.
So may sure that you connect() to a remote computer or bind() to a specific local IP before you use getsockname(). I wonder if enabling IPv6 has caused your machine to see multiple potential local IPs to use. Obviously you much be connected to a machine to use getpeername().

How to get Current System IP in C under Windows XP [duplicate]

For a communication between two hosts, I need to send the IP address of my host to the other site. The problem is that if I request my IP address, it might be that I get back my local loopback IP addres (127.x.x.x) , not the network (ethernet) IP address.
I use the following code:
char myhostname[32];
gethostname(myhostname, 32);
hp = gethostbyname(myhostname);
unsigned my_ip = *(unsigned*)(hp->h_addr);
if( (my_ip % 256) == 127) {
/* Wrong IP adress as it's 127.x.x.x */
printf("Error, local IP address!");
return;
}
The only way to solve it is to make sure my hostname in /etc/hosts is behind the real network address, not the local loopback (the default for e.g. Ubuntu).
Is there a way to solve this without relying on the content of /etc/hosts?
Edit: I changed the above code so it makes use of getaddrinfo, but I still get back the loopback device's number (127.0,0,1) instead of the real IP address:
struct addrinfo hint = {0};
struct addrinfo *aip = NULL;
unsigned ip = 0;
struct sockaddr_in *sinp = NULL;
hint.ai_family = AF_INET; /* IPv4 */
hint.ai_socktype = SOCK_STREAM;
if(getaddrinfo(hostname, NULL, &hint, &aip) != 0) {
return 0;
}
sinp = (struct sockaddr_in *) aip->ai_addr;
ip = *(unsigned *) &sinp->sin_addr;
(I used to get back a list of 3 addrinfo's with the three SOCK_STREAM,SOCK_DGRAM and SOCK_RAW, but the hint prevents that)
So my question still stands...
There is POSIX function getaddrinfo() that returns linked list of addresses for given hostname, so you just need to go through that list and find non-loopback address.
See man getaddrinfo.
Not an answer, but a relevant comment: be aware that as soon as you start sending addressing information in the content of packets, you run the risk of making your application unable to work across NAT:ing routers and/or through firewalls.
These technologies rely on the information in IP packet headers to keep track of the traffic, and if applications exchange addressing information inside packets, where they remain invisible to this inspection, they might break.
Of course, this might be totally irrelevant to your application, but I thought it worth pointing out in this context.
The originating address will be included in the packet sent... there's no need to duplicate this information. It's obtained when accepting the communication from the remote host (see beej's guide to networking, specifically the part on accept())
I just ran into a situation where when only /etc/hosts has information in it and when I used getaddrinfo to get the IP address list, it returned 127.0.0.1 each time. As it turned out, the hostname was aliased to localhost...something often easy to overlook. Here's what happened:
The /etc/hosts file:
127.0.0.1 localhost.localdomain localhost foo
::1 localhost6.localdomain6 localhost6
172.16.1.248 foo
172.16.1.249 bie
172.16.1.250 bletch
So, now, when you call getaddrinfo with host="foo", it returns 127.0.0.1 3 times. The error here, is that foo appears both on the line with "127.0.0.1" and "172.16.1.248". Once I removed foo from the line with "127.0.0.1" things worked fine.
Hope this helps someone.
Look at this:
Discovering public IP programmatically
Note that in some cases a computer can have more than one non-loopback IP address, and in that case the answers to that question tell you how to get the one that is exposed to the internet.
Even if the computer has only one physical network interface (an assumption that may or may not hold, even netbooks have two - ethernet and WLAN), VPNs can add even more IP adresses. Anyway, the host on the other side should be able to determine the IP your host used to contact it.
Use getaddrinfo()
You're almost there. I'm not sure how you're getting my_ip from hp.
gethostbyname() returns a pointer to a hostent structure which has an h_addr_list field.
The h_addr_list field is a null-terminated list of all the ip addresses bound to that host.
I think you're getting the loopback address because it's the first entry in h_addr_list.
EDIT: It should work something like this:
gethostname(myhostname, 32);
hp = gethostbyname(myhostname);
unsigned my_ip = *(unsigned*)(hp->h_addr);
for (int i = 0; hp->h_addr_list[i] != 0; ++i) {
if (hp->h_addr_list[i] != INADDR_LOOPBACK) {
// hp->addr_list[i] is a non-loopback address
}
}
// no address found
If /etc/hosts is still there and still the same, looking for all entries of h_addr_list won't help.
Your new code hardwires the use of IPv4 (in the hint.ai_family field) which is a terrible idea.
Apart from that, you're close, you just should loop through the results of getaddrinfo. Your code just gets the first IP address but there is an aip->ai_next field to follow...
struct addrinfo {
...
struct addrinfo *ai_next; /* next structure in linked list */
};