Function for obtaining all interfaces and IP addresses of a Local Machine
I have been busy with a program recently. To protect all the IP addresses on my machine, I need to get them.
Although there is no arp in IPv6, I always want to write code in a Protocol version, so I must be able to get the IPv6 address anyway.
As long as IPv4 is used, there is no need to write this article.
The first thing I want to consider is to use NETLINK for access, but I don't want to use it now, because I have finished writing the program in a few days, and I will re-write the old ioctl with NETLINK.
Think of the book (UNPv3) that there is a getifaddrs function in BSD, I just ran it, and my machine also had it (LINUX ). Read the manual page and ifaddrs. h
The file is confused again. Can such a function support IPv6. because the Members in the struct ifaddrs structure only have the struct sockaddr pointer, And the V4 can be installed. V6 address
OK?
I searched it up and found an egg (simple) code in a foreign email list. The struct sockaddr * ifa_addr member can be directly
The value of the sa_family member in it is directly converted to the corresponding type. I feel much better. It's okay to get it. If it doesn't work, I feel that so many addresses are returned. You can use the lo interface address
It's useless. Imagine that the actual code seldom cares about the return interface, and you can find another way to filter it out. Look at the struct ifaddrs, which is obviously the type of the linked list node (too self-righteous)
I thought it was a real linked list. I just needed to correct it.
So I was busy for a long time and wrote a small function. This is a snippet: (this is a little abnormal, and it is not guaranteed)
While (ifa ){
Tmp = ifa;
Tmp-> ifa_next = NULL;
If (ifa-> ifa_addr-> sa_family! = Family ){
If (family! = AF_UNSPEC & (ifa-> ifa_addr = NULL | (ifa-> ifa_flags & IFF_UP) = 0) |! Strcmp (ifa-> ifa_name, "lo "))){
Freeifaddrs (tmp );
If (flag ){
Ifaddrs = ifa-> ifa_next;/** never get here twice **/
Flag = 0;
}
}
Ifa = ifa-> ifa_next;
}
}
The address type is used as a parameter to obtain all the interface IP addresses of all activities except the return address of the specified type on the machine,
Then we found that there were always errors. Because the first return is the V4 return address, I want to release it. When running, the following message is always displayed:
Lo: 127.0.0.1
Eth0: 10.0.119.163
Lo: 1
Eth0: fe80: 203: dff: fe2f: 9149
* ** Glibc detected ** d: free (): invalid pointer: 0x0804a4d4 ***
======= Backtrace: ============
/Lib/libc. so.6 [0xb7eda911]
/Lib/libc. so.6 (_ libc_free + 0x84) [0xb7edbf84]
/Lib/libc. so.6 (freeifaddrs + 0x1d) [0xb7f512dd]
D [0x8048989]
D [0x80486a5]
/Lib/libc. so.6 (_ libc_start_main + 0xdc) [0xb7e8c87c]
D [0x8048491]
======= Memory map: ========
08048000-08049000 r-xp 00000000 48637/home/souldump/bin/d
08049000-0804a000 rw-p 00000000 07 48637/home/souldump/bin/d
0804a000-0806b000 rw-p 0804a000 00:00 0 [heap]
B7d00000-b7d21000 rw-p b7d00000 0
B7d21000-b7e00000 --- p b7d21000 00:00 0
B7e76000-b7e77000 rw-p b7e76000 0
B7e77000-b7f90000 r-xp 00000000 0:05 16184/lib/libc-2.4.so
B7f90000-b7f92000 r -- p 00118000 0:05 16184/lib/libc-2.4.so
B7f92000-b7f94000 rw-p 0011a000 0:05 16184/lib/libc-2.4.so
B7f94000-b7f98000 rw-p b7f94000 0
B7fab000-b7fb5000 r-xp 00000000 0:05 20108/lib/libgcc_s.so.1
B7fb5000-b7fb6000 rw-p 00009000 0:05 20108/lib/libgcc_s.so.1
B7fb6000-b7fb7000 rw-p b7fb6000 0
B7fb7000-b7fd1000 r-xp 00000000 0:05 16177/lib/ld-2.4.so
B7fd1000-b7fd3000 rw-p 00019000 0:05 16177/lib/ld-2.4.so
Bfb2b000-bfb41000 rw-p bfb2b000 0 [stack]
Ffffe000-fffff000 --- p 00000000 00:00 0 [vdso]
Abandoned
The pointer is invalid because it is not a real linked list.
So I turned to the idea of looking at the GLIBC code, and I did not know the answer to all the questions in the code.
Quickly open the previous installation of LFS glbc-2.3.6 source code.
Searched under glibc-2.3.6/glibc-2.3.6/sysdeps/unix/sysv/linux/ifaddrs. c
Found the implementation of getifaddrs, the first eye is depressed, still implemented using NETLINK. I did not bother to do this until I wrote it myself.
It defines such a structure to ensure the space of each interface. This is why IPv6 can be converted.
Struct ifaddrs_storage
{
Struct ifaddrs ifa;
Union
{
/* Save space for the biggest of the four used sockaddr types and
Avoid a lot of casts .*/
Struct sockaddr sa;
Struct sockaddr_ll sl;
Struct sockaddr_in s4;
Struct sockaddr_in6 s6;
} Addr, netmask, broadaddr;
Char name [IF_NAMESIZE + 1];
};
The general process is as follows: if NETLINK is supported, send a request. Otherwise, call fallback_getifaddrs (previously implemented by getifaddrs)
Function implementation only supports IPv4 (ioctl is used for one interface ). (I remember someone in the mail list asked this function from the version of glibc.
I started to support IPv6, but the Japanese did not seem to have said that I only wanted to check the next version .), Then, the first time the system quickly traverses and processes the returned data,
Determine the number of interfaces and addresses to allocate space. Next, traverse the data again and initialize the ifa_next of each struct ifaddrs structure.
Pointer (using map_newlink), the value of the corresponding item memcpy is passed according to each rtattr structure type.
If you are interested, just read the freeifaddrs function. Only free (ifa.
I finally figured out that since NETLINK is still used, it is not easy to do this time. With the experience of operating the route table last time and the glibc code
The library function is changed and encapsulated into a new function, which will be used later. The AF_INET, AF_INET6, AF_UNSPEC parameters are used,
This is the code:
Shell code
- # Include <stdio. h>
- # Include <stdlib. h>
- # Include <stdbool. h>
- # Include <string. h>
- # Include <unistd. h>
- # Include <sys/socket. h>
- # Include <net/if. h>
- # Include <netinet/in. h>
- # Include <sys/types. h>
- # Include <linux/netlink. h>
- # Include <linux/rtnetlink. h>
- # Include <assert. h>
- # Include <errno. h>
- # Include <ifaddrs. h>
- # Include <netpacket/packet. h>
- Void print_ip (struct ifaddrs * ifaddrs );
- /** NOTE! Caller must call freeifaddrs () after the use the pointer **/
- Int get_local_ip (struct ifaddrs ** ifap, int family );
- # Define IS_UNSPEC (family) (family = AF_UNSPEC)
- # Define NO_ADDRS (ifa) (ifa-> ifa_addr = NULL)
- # Define FAMILY_ OK (ifa) (ifa-> ifa_addr-> sa_family = AF_INET | \
- Ifa-> ifa_addr-> sa_family = AF_INET6)
- # Define IN_FAMILY (ifa, family) (ifa-> ifa_addr-> sa_family = family)
- # Define TEST_FLAG (ifa, flag) (ifa-> ifa_flags | flag)
- # Define IS_LOOPBACK (ifa) (strncmp (ifa-> ifa_name, "lo", 2) = 0)
- # Define move_ptr (ifa, tmp) do {ifa-> ifa_next = tmp-> ifa_next ;\
- Ifa-> ifa_name = tmp-> ifa_name ;\
- Ifa-> ifa_flags = tmp-> ifa_flags ;\
- Ifa-> ifa_addr = tmp-> ifa_addr ;\
- Ifa-> ifa_netmask = tmp-> ifa_netmask ;\
- If (TEST_FLAG (ifa, IFF_BROADCAST ))\
- Ifa-> ifa_broadaddr = tmp-> ifa_broadaddr ;\
- Else if (TEST_FLAG (ifa, IFF_POINTOPOINT ))\
- Ifa-> ifa_dstaddr = tmp-> ifa_dstaddr ;\
- } While (0)
- Int main ()
- {
- Struct ifaddrs * ifa, * ifaddrs;
- Struct ifaddrs * ifb, * ifc;
- Printf ("AF_INET \ n ");
- Get_local_ip (& ifa, AF_INET );
- Print_ip (ifa );
- Printf ("AF_INET6 \ n ");
- Get_local_ip (& ifb, AF_INET6 );
- Print_ip (ifb );
- Printf ("AF_UNSPEC \ n ");
- Get_local_ip (& ifc, AF_UNSPEC );
- Print_ip (ifc );
- }
- Void
- Print_ip (struct ifaddrs * ifaddrs)
- {
- Struct ifaddrs * ifa;
- Struct sockaddr_in * sin;
- Struct sockaddr_in6 * sin6;
- Char buf [INET6_ADDRSTRLEN];
- For (ifa = ifaddrs; ifa! = NULL; ifa = ifa-> ifa_next)
- {
- If (ifa-> ifa_addr = NULL) continue;
- If (ifa-> ifa_flags & IFF_UP) = 0) continue;
- If (ifa-> ifa_addr-> sa_family = AF_INET)
- {
- Sin = (struct sockaddr_in *) (ifa-> ifa_addr );
- If (inet_ntop (ifa-> ifa_addr-> sa_family, (void *) & (sin-> sin_addr), buf, sizeof (buf) = NULL)
- {
- Printf ("% s: inet_ntop failed! \ N ", ifa-> ifa_name );
- }
- Else
- {
- Printf ("% s: % s \ n", ifa-> ifa_name, buf );
- }
- }
- Else if (ifa-> ifa_addr-> sa_family = AF_INET6)
- {
- Sin6 = (struct sockaddr_in6 *) (ifa-> ifa_addr );
- If (inet_ntop (ifa-> ifa_addr-> sa_family, (void *) & (sin6-> sin6_addr), buf, sizeof (buf) = NULL)
- {
- Printf ("% s: inet_ntop failed! \ N ", ifa-> ifa_name );
- }
- Else
- {
- Printf ("% s: % s \ n", ifa-> ifa_name, buf );
- }
- }
- }
- }
- Int get_local_ip (struct ifaddrs ** ifap, int family)
- {
- Int n;
- Bool change = 0;
- Char * name;
- Struct ifaddrs * ifa;
- Struct ifaddrs * tmp;
- Struct sockaddr_in * sin;
- Struct sockaddr_in6 * sin6;
- Char buf [INET6_ADDRSTRLEN];
- N = getifaddrs (& ifa );
- If (n! = 0)
- Return-1;
- * Ifap = ifa;
- For (ifa; (tmp = ifa )! = NULL; ifa = ifa-> ifa_next ){
- While (tmp & (IS_LOOPBACK (tmp) |
- NO_ADDRS (tmp) | (! TEST_FLAG (tmp, IFF_UP) |
- (FAMILY_ OK (tmp )&&(! IN_FAMILY (tmp, family ))&&(! IS_UNSPEC (family) |
- ! FAMILY_ OK (tmp ))){
- Change = true;
- Tmp = tmp-> ifa_next;
- }
- If (change ){
- If (tmp)
- Move_ptr (ifa, tmp );
- Else
- Memset (ifa, 0, sizeof (struct ifaddrs ));
- /** An alternative way is use these instead:
- Ifa-> ifa_next = NULL;
- Ifa-> ifa_name = NULL;
- Ifa-> ifa_addr = NULL;
- Ifa-> ifa_netmask = NULL;
- If (TEST_FLAG (ifa, IFF_BROADCAST ))
- Ifa-> ifa_broadaddr = NULL;
- Else if (TEST_FLAG (ifa, IFF_POINTOPOINT ))
- Ifa-> ifa_dstaddr = NULL;
- **/
- Change = false;
- }
- }
- /** I think even * ifap now is NULL after check, we shoshould not free the space either.
- Since user who called this routine will call freeifaddrs () too, The space will be free safely .**/
- Return 0;
- }
From: http://www.linuxsir.org/bbs/thread284220.html