How to Detect Ip Address Change Programmatically in Linux

How to detect IP address change programmatically in Linux?

In C, to get the current IP I use:

    int s;
struct ifreq ifr = {};

s = socket(PF_INET, SOCK_DGRAM, 0);

strncpy(ifr.ifr_name, "eth0", sizeof(ifr.ifr_name));

if (ioctl(s, SIOCGIFADDR, &ifr) >= 0)
printf("%s\n",
inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr));

Replace "eth0" with the interface you're looking at. All you now need to do is poll for a change.

How to detect IP address change on OSX programmatically in C or C++

There are multiple ways to do this, from IOKit notifications on up, but the simplest is probably the SystemConfiguration framework.

The first step is to fire up scutil and play with it to figure out which key(s) you want notification on:

$ scutil
> list
...
> n.add State:/Network/Global/IPv4
> n.watch
... unplug your network cable (or disconnect from WiFi)
notification callback (store address = 0x10e80e3c0).
changed key [0] = State:/Network/Global/IPv4

Look at that, got it on first try. :) But if you want to watch a particular NIC, or use IPv6 instead of v4, etc., obviously you'll want a different key from the list. Note that you can use regex patterns (POSIX style, as defined by man 3 regex), so if you want to watch, say, any NIC for IPv4, you can use State:/Network/Interface/.*/IPv4, or if you want to say global IPv4 or IPv6, State:/Network/Global/IPv., etc.

Now you just call SCDynamicStoreSetNotificationKeys with the keys you want.

Note that SCDynamicStoreSetNotificationKeys can take regex patterns (POSIX style, as defined by man 3 regex)

Since it's a bit painful in C, I'll write it in Python:

#!/usr/bin/python

from Foundation import *
from SystemConfiguration import *

def callback(store, keys, info):
for key in keys:
print key, SCDynamicStoreCopyValue(store, key)

store = SCDynamicStoreCreate(None,
"global-network-watcher",
callback,
None)
SCDynamicStoreSetNotificationKeys(store,
None,
['State:/Network/Global/IPv4'])
CFRunLoopAddSource(CFRunLoopGetCurrent(),
SCDynamicStoreCreateRunLoopSource(None, store, 0),
kCFRunLoopCommonModes)
CFRunLoopRun()

The main reason this is more painful in C is that you need dozens of lines of boilerplate for things like creating an CFArray with a CFString in it, printing CFString values, managing the lifetimes of the objects, etc. From Jeremy Friesner's comment, there's C++ sample code available if you'd rather read 113 lines of C++ than 17 lines of Python. But really, there's only one line here that should be unfamiliar to someone who's never used Python:

def callback(store, keys, info):
for key in keys:
print key, SCDynamicStoreCopyValue(store, key)

… is the equivalent of the C definition:

void callback(SCDynamicStoreRef store, CFArrayRef keys, void *info) {
/* iterate over keys, printing something for each one */
}

Oddly, I can't find the actual reference or guide documentation on SystemConfiguration anymore; the only thing that comes up for SCDynamicStoreSetNotificationKeys or related functions is in the Navigating Firewalls section of CFNetwork Programming Guide. But the original technote TN1145: Living in a Dynamic TCP/IP Environment still exists, and it's got enough background and sample code to figure out how write this yourself (and how to detect the new IP address(es) when you get notified).

Obviously this requires you to know what exactly you're trying to watch for. If you don't know that, nobody can tell you how to watch for it. Your original question was how to "detect an IP address change".

What the code above will do is detect when your default address changes. That's the address that gets used when you connect a socket to an internet address without binding it, or bind a socket to '0.0.0.0' to act as an internet server. If you haven't written the server code you care about, nearly all network clients do the former, and most servers do the latter unless you configure them otherwise, so that's probably all you care about.

Now let's go through the examples in your comments one by one:

if i am trying to determine a network change, wifi to LAN

There is no such thing as changing from WiFi to LAN. When you connect to a LAN, the WiFi is still working. Of course you can manually disable it before or after connecting to the LAN, but you don't have to, and it's a separate step, with a separate notification.

Normally, adding a LAN will change your default address to the LAN's address, so /Network/Global will notify you. If the OS can tell the LAN is not actually connected to the internet, or you've changed some hidden settings to make it prefer WiFi to LAN, etc., it won't change the default address, and /Network/Global will not notify you, but you probably don't care.

If you do care about whether a particular interface gets, loses, or changes an address, you can watch that interface. On most Macs, the built-in Ethernet is en0, and the built-in WiFi is en1, but of course you may have a third-party USB WiFi connector, or you may be using a tethered cell phone, or you may be interested not so much in the actual IP address of the LAN as in the VPN address of the VPN the LAN is connected to, etc. If you're writing something special purpose, you probably know which interface you care about, so you can watch, e.g., State:/Network/Interface/en0/IPv4. If you want to be notified on any interface changing no matter what, just watch State:/Network/Interface/.*/IPv4.

or to hotspot or another wifi

Changing from one WiFi network to another (hotspot or otherwise) will change en1—or, if you're using a third-party WiFi adapter, some other interface. If your default address at the time comes from WiFi, it will also change Global, which means the code above will work as-is. If your default address is still your LAN, Global won't change, but you probably don't care. If you do care, watch Interface/en1 or Interface/.*, etc., as above.

what all network settings should I be watching for IPV4 and 6

Just replace IPv4 with IPv6, or use IPv.. But do you really care about IPv6?

what else

What else do you care about? If there's something you actually want notification of, you presumably know what that something is.

Beyond that, if the system tells you that the foo address on the bar interface has changed to "ZZ9 Plural Z Alpha", and you've never heard of the foo protocol, what could you usefully do with that information? But if you really want it anyway, again, you can just use a regex pattern to watch for anything under each interface.

How to get IP address programmatically on Debian based system?

See "netdevice", through man netdevice or on the web.

SIOCGIFCONF can then be used to get an enumeration of all transport layer addresses.

Edit (on manpages): man is a very useful command on Linux (or other UNIX-like systems as well). It shows a brief description of most commands, library functions, programs, etc. Open a shell prompt and type man ls or man netdevice, and you'll see what I mean.

Edit (on general retrieving of IP): The easiest way, if you think the C way is too messy, is a simple shell script like (just from the top of my head):

ifconfig|grep 'inet addr'|awk '{print $2}'|sed 's/addr://g'

Edit (on the Brain solution): What he does is using the if_nameindex() function for finding all network device names, and then the SIOCFIFCONF ioctl on each of these names for finding their IP. As he says, it only lists one IP per device.

Change IP settings programmatically in Linux with C/C++

In most cases, such kind of operations requires root access. Run your application with root rights and it'll make it work.

Get notified about network interface change on Linux

I believe the netlink (man 7 netlink) facility provides information about network interfaces via the NETLINK_ROUTE family (man 7 rtnetlink). You may be able to select() or poll() on a netlink socket to get the information you want. I'm not certain of this, though; I haven't used it myself.

At a higher level, if the system is running NetworkManager, that'll broadcast events via D-Bus when the system's network status changes. The Epiphany browser uses these events, for example, to automatically activate "Work Offline" mode when the system loses its network connection, and switch back to online mode when network connectivity resumes. There are D-Bus client libraries for a variety of languages, and it's less platform-specific than netlink, so this is what I'd recommend using.

How can I programmatically find the IP address/netmask/gateway configured for a specific network device in Linux?

There sure is using a struct of ifreq and ioctl() calls you can grab all interface information:

Man page is here Ifreq manpage

/* local interface info */
typedef struct{
char *iface;
struct ether_addr hwa;
struct in_addr ipa;
struct in_addr bcast;
struct in_addr nmask;
u_short mtu;
} ifcfg_t;
/*
* Grabs local network interface information and stores in a ifcfg_t
* defined in network.h, returns 0 on success -1 on failure
*/
int get_local_info(int rsock, ifcfg_t *ifcfg)
{
struct ifreq ifr;

memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifcfg->iface, IF_NAMESIZE);
if((ioctl(rsock, SIOCGIFHWADDR, &ifr)) == -1){
perror("ioctl():");
return -1;
}
memcpy(&(ifcfg->hwa), &ifr.ifr_hwaddr.sa_data, 6);

memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifcfg->iface, IF_NAMESIZE);
if((ioctl(rsock, SIOCGIFADDR, &ifr)) == -1){
perror("ioctl():");
return -1;
}
memcpy(&ifcfg->ipa, &(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr, 4);

memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifcfg->iface, IF_NAMESIZE);
if((ioctl(rsock, SIOCGIFBRDADDR, &ifr)) == -1){
perror("ioctl():");
return -1;
}
memcpy(&ifcfg->bcast, &(*(struct sockaddr_in *)&ifr.ifr_broadaddr).sin_addr, 4);

memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifcfg->iface, IF_NAMESIZE);
if((ioctl(rsock, SIOCGIFNETMASK, &ifr)) == -1){
perror("ioctl():");
return -1;
}
memcpy(&ifcfg->nmask.s_addr, &(*(struct sockaddr_in *)&ifr.ifr_netmask).sin_addr, 4);

memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifcfg->iface, IF_NAMESIZE);
if((ioctl(rsock, SIOCGIFMTU, &ifr)) == -1){
perror("ioctl():");
return -1;
}
ifcfg->mtu = ifr.ifr_mtu;

return 0;
}

Quick edit, this function requires that the interface has been assigned before it is called, like so:

strcpy(if_cfg->iface, iface)

Ensuring you have allocated the memory first, then call like so

if((get_local_info(sock, if_cfg)) != 0){
printf("Unable to get network device info\n");
return -1;
}


Related Topics



Leave a reply



Submit