Question Comment détecter le changement d'adresse IP sous OSX par programmation en C ou C ++


Je dois pouvoir détecter un changement d'adresse IP pour mon client Mac. Je dois effectuer une action chaque fois que j'en reçois un nouveau, quand je vais du wifi au câble ...

Quelqu'un a fait quelque chose de similaire? J'interroge actuellement chaque minute et je dois changer cela pour être plus axé sur les événements.


16
2017-07-17 23:12


origine


Réponses:


Il y a plusieurs façons de le faire, depuis les notifications IOKit, mais le plus simple est probablement le framework SystemConfiguration.

La première étape consiste à lancer scutil et à jouer avec lui pour déterminer les clés sur lesquelles vous souhaitez recevoir une notification:

$ 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

Regardez ça, vous l'avez eu au premier essai. :) Mais si vous voulez regarder une carte réseau particulière, ou utiliser IPv6 au lieu de v4, etc., vous voudrez évidemment une clé différente de la liste. Notez que vous pouvez utiliser des modèles de regex (style POSIX, définis par man 3 regex), donc si vous voulez regarder, disons, une carte réseau pour IPv4, vous pouvez utiliser State:/Network/Interface/.*/IPv4, ou si vous voulez dire IPv4 ou IPv6 global, State:/Network/Global/IPv., etc.

Maintenant, il vous suffit d'appeler SCDynamicStoreSetNotificationKeys avec les clés souhaitées.

Notez que SCDynamicStoreSetNotificationKeys peut prendre des motifs d’expression rationnelle (style POSIX, défini par regex man 3)

Comme c'est un peu pénible en C, je l'écrirai en 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()

La raison principale pour laquelle cela est plus douloureux en C est que vous avez besoin de dizaines de lignes de raccourcis pour créer des CFArray avec un CFString, imprimer des valeurs CFString, gérer les durées de vie des objets, etc. Exemple de code C ++ disponible si vous préférez lire 113 lignes de C ++ que 17 lignes de Python. Mais vraiment, il n'y a qu'une seule ligne ici qui devrait être peu familière à quelqu'un qui n'a jamais utilisé Python:

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

... est l'équivalent de la définition C:

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

Bizarrement, je ne trouve plus la documentation de référence ou de guide sur SystemConfiguration; la seule chose qui se présente pour SCDynamicStoreSetNotificationKeys ou des fonctions connexes se trouve dans la section Navigating Firewalls de Guide de programmation CFNetwork. Mais la note technique originale TN1145: Vivre dans un environnement TCP / IP dynamique existe toujours, et il a assez de code d'arrière-plan et de code pour savoir comment l'écrire vous-même (et comment détecter la ou les nouvelles adresses IP lorsque vous êtes averti).

Évidemment, cela vous oblige à savoir exactement ce que vous essayez de surveiller. Si vous ne le savez pas, personne ne peut vous dire comment le surveiller. Votre question initiale était de savoir comment "détecter un changement d'adresse IP".

Ce que le code ci-dessus va faire est de détecter quand votre adresse par défaut change. C'est l'adresse qui est utilisée lorsque vous connectez une socket à une adresse Internet sans la lier, ou que vous liez une socket à «0.0.0.0» pour agir en tant que serveur Internet. Si vous n'avez pas écrit le code de serveur qui vous intéresse, presque tous les clients réseau le font, et la plupart des serveurs le font, sauf si vous les configurez autrement.

Passons maintenant en revue les exemples de vos commentaires un par un:

si j'essaie de déterminer un changement de réseau, le wifi au réseau local

Le passage du WiFi au LAN n’existe pas. Lorsque vous vous connectez à un réseau local, le WiFi continue de fonctionner. Bien sûr, vous pouvez le désactiver manuellement avant ou après la connexion au réseau local, mais vous n'êtes pas obligé, et il s'agit d'une étape distincte, avec une notification distincte.

Normalement, l’ajout d’un réseau local changera votre adresse par défaut en adresse du réseau local. /Network/Global vous avertira Si le système d’exploitation peut dire que le réseau local n’est pas réellement connecté à Internet, ou si vous avez modifié certains paramètres cachés pour qu’il préfère le WiFi au réseau local, etc., cela ne changera pas l’adresse par défaut. /Network/Global ne vous avertira pas, mais vous ne vous en souciez probablement pas.

Si vous vous souciez de savoir si une interface particulière obtient, perd ou modifie une adresse, vous pouvez regarder cette interface. Sur la plupart des Mac, l’Ethernet intégré est en0, et le WiFi intégré est en1, mais vous pouvez bien sûr avoir un connecteur USB WiFi tiers, ou vous pouvez utiliser un téléphone portable, ou cela peut vous intéresser. Pas tellement dans l'adresse IP réelle du réseau local que dans l'adresse VPN du réseau privé virtuel auquel le réseau local est connecté, etc. Si vous écrivez quelque chose à des fins spéciales, vous savez probablement quelle interface vous intéresse, vous pouvez regarder, par exemple, State:/Network/Interface/en0/IPv4. Si vous voulez être averti sur n'importe quelle interface qui change, regardez juste State:/Network/Interface/.*/IPv4.

ou à hotspot ou un autre wifi

Passer d'un réseau WiFi à un autre (hotspot ou autre) modifiera en1 ou, si vous utilisez un adaptateur WiFi tiers, une autre interface. Si votre adresse par défaut provient du WiFi, elle changera également Global, ce qui signifie que le code ci-dessus fonctionnera tel quel. Si votre adresse par défaut est toujours votre réseau local, Global ne changera pas, mais vous ne vous en souciez probablement pas. Si tu t'en soucie, regarde Interface/en1 ou Interface/.*, etc., comme ci-dessus.

Que dois-je surveiller tous les paramètres réseau pour IPV4 et 6?

Remplacez simplement IPv4 par IPv6 ou utilisez IPv.. Mais vous souciez-vous vraiment d’IPv6?

quoi d'autre

Qu'est-ce qui vous préoccupe? S'il y a quelque chose que vous voulez vraiment savoir, vous savez probablement ce que c'est.

Au-delà de cela, si le système vous indique que l'adresse foo sur l'interface de la barre est passée à "ZZ9 Plural Z Alpha", et que vous n'avez jamais entendu parler du protocole foo, que pourriez-vous faire utilement avec cette information? Mais si vous le voulez vraiment de toute façon, encore une fois, vous pouvez simplement utiliser un modèle de regex pour surveiller tout ce qui se trouve sous chaque interface.


23
2017-07-17 23:53