Re: Wireless Connection Help



Hello all,

Thanks Thomas for the help thus far.  I reworked the program so that it is only using libnm now.  The program will get all the connection profiles from a nm_client_get_connections call and then go through each wifi device found in a nm_client_get_devices call.  It will then go through each Wifi device and filter the connections profile list for that device.  It will get all the access points for the device and filter the connection profile list again looking for the ones that match that access point.  It stores the information it finds on each access point in an unordered_map with the UUID as the key.  From this I am able to produce this list:

*** Begin Wireless Network List ***
======================================================
SSID            :Network1
BSSID           :xx:xx:xx:xx:xx:xx
UUID            :c928dd61-694c-4a5a-aa96-a97f589a855b
WPA Flags: none         WPA2 Flags: none
Device Path     :/org/freedesktop/NetworkManager/Devices/6
Connection Path :/org/freedesktop/NetworkManager/Settings/3
AP Path         :/org/freedesktop/NetworkManager/AccessPoint/130
Active          :0
======================================================
SSID            :Network2
BSSID           :yy:yy:yy:yy:yy:yy
UUID            :50586589-152a-463b-b579-2439c8ec829d
WPA Flags: none         WPA2 Flags: pair_ccmp group_ccmp psk
Device Path     :/org/freedesktop/NetworkManager/Devices/6
Connection Path :/org/freedesktop/NetworkManager/Settings/2
AP Path         :/org/freedesktop/NetworkManager/AccessPoint/636
Active          :0
======================================================
SSID            :Network3
BSSID           :zz:zz:zz:zz:zz:zz
UUID            :4adb7b72-4eba-4310-b202-caf993dc50ea
WPA Flags: pair_tkip pair_ccmp group_tkip psk           WPA2 Flags: pair_tkip pair_ccmp group_tkip psk
Device Path     :/org/freedesktop/NetworkManager/Devices/6
Connection Path :/org/freedesktop/NetworkManager/Settings/1
AP Path         :/org/freedesktop/NetworkManager/AccessPoint/778
Active          :0
*** End Wireless Network List ***

When I attempt to activate an open connection using the nm_client_activate_connection_async call the system will connect to the access point and get an IP.  If I attempt to connect to a secure access point where the password was already entered and saved using nmtui the system will also connect to the network.  If I attempt to connect to a network where the password needs to be set the system will not connect to the access point.  In the function I attempt to set the PSK using nm_connection_add_setting(...) but I think it is not actually editing the settings or maybe the settings are not getting "committed".  Any help would be appreciated.  The following is the code for the function that checks to add the PSK to the connection profile.

void NetworkSettings::CheckAP(NMAccessPoint *apNMDevice *deviceconst GPtrArray *deviceConnections)
{
   guint32 flags, wpa_flags, rsn_flags, freq, bitrate;
   guint8 strength;
   const GPtrArray *apConnections;
   GBytes *ssid;
   const char *hwaddr;
   NM80211Mode mode;
   const char *uuid;
   const char *ssid_str;
   char *freq_str, *bitrate_str, *strength_str, *wpa_flags_str, *rsn_flags_str;
   GString *security_str;

   ssid_str = NULL;
   // Get AP properties 
   flags = nm_access_point_get_flags(ap);
   wpa_flags = nm_access_point_get_wpa_flags(ap);
   rsn_flags = nm_access_point_get_rsn_flags(ap);
   
   ssid = nm_access_point_get_ssid(ap);
   hwaddr = nm_access_point_get_bssid(ap);
   // Not using these right now
   /*
   freq = nm_access_point_get_frequency(ap);
   mode = nm_access_point_get_mode(ap);
   bitrate = nm_access_point_get_max_bitrate(ap);
   strength = nm_access_point_get_strength(ap);
   */ 

   // Convert to strings 
   try
   {
      if(ssid)
         ssid_str = nm_utils_ssid_to_utf8((guint8 *)g_bytes_get_data(ssid, NULL), g_bytes_get_size(ssid));
      else
         ssid_str = NULL;;
   }
   catch(const std::exception& e)
   {
      std::cout << e.what() << std::endl << std::flush;
      ssid_str = NULL;
   }
   freq_str = g_strdup_printf("%u MHz", freq);
   bitrate_str = g_strdup_printf("%u MB/s", bitrate/1000);
   strength_str = g_strdup_printf("%u", strength);
   wpa_flags_str = NetworkSettings::ap_wpa_rsn_flags_to_string(wpa_flags);
   rsn_flags_str = NetworkSettings::ap_wpa_rsn_flags_to_string(rsn_flags);

   security_str = g_string_new(NULL);
   if (!(flags & NM_802_11_AP_FLAGS_PRIVACY)
       && (wpa_flags != NM_802_11_AP_SEC_NONE)
       && (rsn_flags != NM_802_11_AP_SEC_NONE))
      g_string_append(security_str, "Encrypted: ");

   if((flags & NM_802_11_AP_FLAGS_PRIVACY)
       && (wpa_flags == NM_802_11_AP_SEC_NONE)
       && (rsn_flags == NM_802_11_AP_SEC_NONE))
      g_string_append(security_str, "WEP ");
   if(wpa_flags != NM_802_11_AP_SEC_NONE)
      g_string_append(security_str, "WPA ");
   if(rsn_flags != NM_802_11_AP_SEC_NONE)
      g_string_append(security_str, "WPA2 ");
   if((wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)
       || (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X))
      g_string_append(security_str, "Enterprise ");

   if (security_str->len > 0)
      g_string_truncate(security_str, security_str->len-1);  /* Chop off last space */

   if(ssid_str != NULL)
   {
      apConnections = nm_access_point_filter_connections(ap, deviceConnections);
      if(apConnections && apConnections->len > 0)
         std::cout << "Filter Profile Count: " << apConnections->len << std::endl << std::flush;

      for (int i = 0; apConnections && (i < apConnections->len); i++)
      {
         NMConnection *connectionProfile = reinterpret_cast<NMConnection *>(g_ptr_array_index(apConnections, i));
         NMSettingConnection *s_con;
         s_con = nm_connection_get_setting_connection(connectionProfile);
         if(s_con) 
         {
            guint64 timestamp;
            char *timestamp_str;
            char timestamp_real_str[64];
            const char *val1, *val2, *val3, *val4, *val5;
            // Get various info from NMSettingConnection and show it 
            timestamp = nm_setting_connection_get_timestamp(s_con);
            timestamp_str = g_strdup_printf("%" G_GUINT64_FORMAT, timestamp);
            strftime(timestamp_real_str, sizeof(timestamp_real_str), "%c"localtime((time_t *) &timestamp));

            val1 = nm_setting_connection_get_id(s_con);
            uuid = nm_setting_connection_get_uuid(s_con);
            val3 = nm_setting_connection_get_connection_type(s_con);
            val4 = nm_connection_get_path(connectionProfile);
            val5 = timestamp ? timestamp_real_str : "never";         
            if(WirelessList.find(uuid) == WirelessList.end())
            {
               wirelessInfo *newEntry = new wirelessInfo();
               WirelessList.emplace(uuid, newEntry);
            }     
            WirelessList[uuid]->UUID = uuid;
            WirelessList[uuid]->lastTime = val5;
            WirelessList[uuid]->SSID = val1;
            WirelessList[uuid]->BSSID = hwaddr;
            WirelessList[uuid]->Security = security_str->str;
            WirelessList[uuid]->APPath = nm_object_get_path(NM_OBJECT(ap));
            WirelessList[uuid]->flags = flags;
            WirelessList[uuid]->wpa_flags = wpa_flags;
            WirelessList[uuid]->rsn_flags = rsn_flags;
            WirelessList[uuid]->freq = freq;
            WirelessList[uuid]->bitrate = bitrate;
            WirelessList[uuid]->DevicePath = nm_object_get_path((NMObject *)device);         
            WirelessList[uuid]->ConnectionPath = nm_connection_get_path(connectionProfile);
            if(WirelessList[uuid]->Requested == true)
            {
               std::cout << "Check Connection Setup: " << uuid << std::endl << std::flush;

               if(WirelessList[uuid]->Setup != true)
               {
                  std::cout << "Attempt to setup connection: " << val1 << std::endl << std::flush;
                  // Check to see if we need to setup the PSK
                  if(WirelessList[uuid]->PSK.length() > 0)
                  {
                     NMSettingWirelessSecurity *settingSecurity;
                     settingSecurity = (NMSettingWirelessSecurity *)nm_setting_wireless_security_new();
                     std::cout << "Attempt to update security proto." << std::endl << std::flush;
                     nm_setting_wireless_security_add_proto(settingSecurity, "rsn");
                     std::cout << "Attempt to update security settings." << std::endl << std::flush;
                     g_object_set(settingSecurity,
//                      NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open",
//                      NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk",
                        NM_SETTING_WIRELESS_SECURITY_PSK, WirelessList[uuid]->PSK.c_str(),
//                      NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS, NM_SETTING_SECRET_FLAG_NOT_SAVED,                
                        NULL);      
                     std::cout << "Attempt to add our security settings." << std::endl << std::flush;
                     nm_connection_add_setting(connectionProfile, NM_SETTING(settingSecurity));
                     // Now see if the PSK was stored in the settings
                     std::cout << "Check Security Key." << std::endl << std::flush;
                     const char* afterVal = nm_setting_wireless_security_get_psk(settingSecurity);
                     std::cout << "After Setting PSK: " << afterVal << std::endl << std::flush;       
                  }
                  // Try an start it up
                  nm_client_activate_connection_async(client, connectionProfile, device, WirelessList[uuid]->APPath.c_str(),
                     NULLNetworkSettings::ConnectionActivationStarted, NULL);
                  WirelessList[uuid]->Setup = true;
               }
               else
               {
                  NMSettingWirelessSecurity *settingSecurity;
                  settingSecurity = nm_connection_get_setting_wireless_security(connectionProfile);
                  const char* afterVal = NULL;
                  afterVal = nm_setting_wireless_security_get_psk(settingSecurity);
                  if(afterVal != NULL)
                     std::cout << "PSK Check: " << afterVal << std::endl << std::flush;                     
               }     
            }     

         }
      }
      g_ptr_array_unref((GPtrArray *)apConnections);
      //g_slist_free(apConnections);
   }

   if(ssid_str != NULL)
      g_free((gpointer)(ssid_str));
   g_free(freq_str);
   g_free(bitrate_str);
   g_free(strength_str);
   g_free(wpa_flags_str);
   g_free(rsn_flags_str);
   g_string_free(security_str, TRUE);
}


On Fri, Apr 10, 2020 at 2:14 AM Thomas Haller <thaller redhat com> wrote:
On Wed, 2020-04-08 at 09:25 -0500, Romano Pauli wrote:
> Hello all,
> I am looking for some help with using NetworkManager in a c++
> application.
>
> I am able to use network manager to scan the list of ap's and also go
> through the list of connections.  I think if the network is already
> listed in the connection list I need to activate it instead of trying
> to add it. 

Your example uses libnm (which is probably a good choice). Note that
libnm is basically a wrapper around D-Bus API, so the libnm API usually
directly corresponds to a D-Bus call.

Then libnm also contains NMConnection and NMSetting* classes, those aim
to simplify creating a connection profile. A connection profile is a
bunch of settings, and thus NMConnection is merely a dictionary of
values. That doesn't directly map to D-Bus, although with
nm_connection_to_dbus() and _nm_simple_connection_new_from_dbus(), you
can convert a NMConnection to a GVariant that you then may send via D-
Bus, as an argument for D-Bus methods like "AddConnection2".


The "connection list" oyu mention is the list of connection profiles. A
profile is just a bunch of settings (with configuration values for a
network).

There is a difference between the connection profiles and the Wi-Fi
scan list (access points). I would agree to call a Wi-Fi access point a
"network", but calling a connection profile a "network" seems confusing
to me (of course, a connection profile are the settings for configuring
a "network"). So, no, I wouldn't say that "the network is listed in the
connection list", but probably you meant the right thing.

You can activate an existing connection profile of your choice.
Alternatively, you can create a new profile with "AddConnection2"
(nm_client_add_connection_async) (and then activate that profile next).

Note that doing anything to a profile (modifying it or adding it),
often triggers an autoconnect right away (if the profile is configured
to autoconnect, and autoconnect is currently possible). So, in those
cases you may not need an explicit acitvation after adding the profile.
See also the flags NM_SETTINGS_ADD_CONNECTION2_FLAG_BLOCK_AUTOCONNECT
and NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT).

There is also AddAndActivateConnection2
(nm_client_add_and_activate_connection2()). That is what happens with
`nmcli device connect $IFNAME` and `nmcli device wifi connect`. It's
not only "AddConnection2 + ActivateConnection" in one step. It allows
you to provide an incomplete connection (e.g. only specify the SSID,
but omit the WPA settings), then NetworkManager will try to complete
the profile depending on the Wi-Fi scan list. With AddConnection2, you
always need to provide the full profile.

Possibly, if you have an Access Point but not profile for it, you would
create one by calling AddAndActivate2. See what happens during `nmcli
device wifi connect`:

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/1ef894f489ce0fc6e689c6671e50851763321f06/clients/cli/devices.c#L3297


> However what I am missing is how do I activate the wireless
> connection and how do I set the WPA2 key (or other key depending on
> the security used) I have not found any code examples on how to use
> ActivateConnection or AddAndActivateConnection2 methods. 
>
> Below was an attempt I made at setting up the connection (borrowed
> heavily from
> https://github.com/lcp/NetworkManager/blob/master/examples/C/glib/add-connection-libnm-glib.c )

I think you need to add a NMSettingWirelessSecurity() and set
NM_SETTING_WIRELESS_SECURITY_PSK. You may also want to set
NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS accordingly.

See `man nm-settings` for the meaning of the properties.


You may also omit the PSK, and let NetworkManager prompt for it. But
that requires a secret-agent running that can answer the request. If
you have nm-applet (or similar) running, it will prompt for a password.
You could also implement the secret agent in your own application...
Of coures, you can avoid all that, by specifying the secret during
nm_client_add_connection*() or nm_remote_connection_update2().


>    // Ask the settings service to add the new connection; we'll quit
> the
>    // mainloop and exit when the callback is called.
>    success = nm_remote_settings_add_connection(settings, connection,
> NetworkSettings::added_cb, loop);

nm_remote_settings_add_connection() is from libnm-glib. That library is
deprecated for many years. Don't use it. Use instead libnm. The
corresponding function there is called nm_client_add_connection().

Also, if you took this from an example code, then don't base it on
examples that are years old.

Possibly also refer to the recent API documentation:
https://developer.gnome.org/NetworkManager/stable/




best,
Thomas


--
Romano Pauli
Software Director
Holu Hou Energy, Inc



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]