/* * ni-client.c * This file is part of NetworkInador * * Copyright (C) 2021 - Gatuno * * NetworkInador is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * NetworkInador is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with NetworkInador; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include "ni-client.h" #include "ni-interface.h" #include "ni-marshal.h" #include "ni-route.h" #include "../src/network-inador-manager.h" #define COMMAND_SOCKET_PATH "/tmp/network-inador.socket" /* Privados desde el ni-interface */ void ni_interface_consume_values (NIInterface *ni_interface, char *name, gint arp_type, guint master_index, guint mtu, guint flags); /* Privados locales */ static void ni_client_dispose (GObject *obj); static void ni_client_finalize (GObject *obj); struct _NIClientPrivate { int client_socket; gchar *socket_path; GHashTable *interfaces; GList *routes_v4, *routes_v6; guint source; guint events; }; enum { PROP_SOCKET_PATH = 1, N_PROPERTIES }; static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, }; enum { DATA_READ, NEW_INTERFACE, DELETE_INTERFACE, NEW_ROUTE, DELETE_ROUTE, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; G_DEFINE_TYPE_WITH_PRIVATE (NIClient, ni_client, G_TYPE_OBJECT) static gboolean ni_client_data_read (NIClient *ni_client, gpointer data, guint size); static void ni_client_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { NIClient *ni_client = NI_CLIENT (object); g_return_if_fail (NI_IS_CLIENT (object)); switch (prop_id) { case PROP_SOCKET_PATH: if (ni_client->priv->socket_path != NULL) { g_free (ni_client->priv->socket_path); } ni_client->priv->socket_path = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void ni_client_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { NIClient *ni_client = NI_CLIENT (object); g_return_if_fail (NI_IS_CLIENT (object)); switch (prop_id) { case PROP_SOCKET_PATH: g_value_set_string (value, ni_client->priv->socket_path); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void ni_client_class_init (NIClientClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->set_property = ni_client_set_property; object_class->get_property = ni_client_get_property; object_class->dispose = ni_client_dispose; object_class->finalize = ni_client_finalize; obj_properties[PROP_SOCKET_PATH] = g_param_spec_string ( "socket-path", "Socket Path", "The path of the unix socket.", COMMAND_SOCKET_PATH, G_PARAM_CONSTRUCT | G_PARAM_READWRITE); g_object_class_install_properties (object_class, N_PROPERTIES, obj_properties); signals[DATA_READ] = g_signal_new ("packet-read", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (NIClientClass, data_response), g_signal_accumulator_true_handled, NULL, _ni_marshal_BOOLEAN__POINTER_UINT, G_TYPE_BOOLEAN, 2, G_TYPE_POINTER | G_SIGNAL_TYPE_STATIC_SCOPE, G_TYPE_UINT); signals[NEW_INTERFACE] = g_signal_new ("new-interface", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (NIClientClass, new_interface), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, NI_TYPE_INTERFACE); signals[DELETE_INTERFACE] = g_signal_new ("delete-interface", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (NIClientClass, delete_interface), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, NI_TYPE_INTERFACE); signals[NEW_ROUTE] = g_signal_new ("new-route", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (NIClientClass, new_route), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, NI_TYPE_ROUTE); signals[DELETE_ROUTE] = g_signal_new ("delete-route", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (NIClientClass, delete_route), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, NI_TYPE_ROUTE); klass->data_response = ni_client_data_read; klass->new_interface = NULL; klass->delete_interface = NULL; klass->new_route = NULL; klass->delete_route = NULL; } static void ni_client_process_interface_response (NIClient *ni_client, gpointer data, guint size) { NIInterface *ni_interface; unsigned char *buffer = (unsigned char *) data; uint32_t index, master_index, mtu, type; uint16_t flags; uint8_t bits; guint name_size; char iface_name[128]; memcpy (&index, &buffer[2], 4); memcpy (&type, &buffer[6], 4); memcpy (&master_index, &buffer[10], 4); memcpy (&mtu, &buffer[14], 4); memcpy (&flags, &buffer[18], 2); bits = buffer[20]; name_size = buffer[21]; memcpy (iface_name, &buffer[22], name_size); iface_name[name_size] = 0; /* Insertar y crear */ ni_interface = g_object_new (NI_TYPE_INTERFACE, "ni-client", ni_client, "index", index, "wireless", (bits == 0 ? FALSE : TRUE), "type", type, "name", iface_name, "mtu", mtu, "flags", flags, "master-index", master_index, NULL); g_hash_table_insert (ni_client->priv->interfaces, GINT_TO_POINTER(index), ni_interface); g_signal_emit (ni_client, signals[NEW_INTERFACE], 0, ni_interface); } static NIRoute *_ni_client_search_route (GList *list_routes, sa_family_t family, uint8_t type, uint32_t table, void *dest, uint32_t prefix, uint32_t priority) { GList *g; NIRoute *ni_route; int family_size = 0; if (family == AF_INET) { family_size = sizeof (struct in_addr); } else if (family == AF_INET6) { family_size = sizeof (struct in6_addr); } for (g = list_routes; g != NULL; g = g->next) { ni_route = (NIRoute *) g->data; if (ni_route_get_family(ni_route) != family) continue; if (ni_route_get_route_type(ni_route) != type) continue; if (ni_route_get_table(ni_route) != table) continue; if (ni_route_get_priority(ni_route) != priority) continue; if (memcmp (ni_route_get_dest (ni_route), dest, family_size) == 0 && ni_route_get_prefix(ni_route) == prefix) { return ni_route; } } return NULL; } static void ni_client_process_route_response (NIClient *ni_client, gpointer data, guint size) { unsigned char *buffer = (unsigned char *) data; uint8_t family, family_size; uint8_t type, protocol, tos, scope, prefix; uint32_t tabla, prioridad; uint8_t num_nexthops; gboolean has_prefsrc; int pos, g; NIRouteNH nexthops[16]; NIRoute *ni_route; struct_addr dest, prefsrc; memset (nexthops, 0, sizeof (nexthops)); family = buffer[2]; if (family == AF_INET) { family_size = sizeof (struct in_addr); } else if (family == AF_INET6) { family_size = sizeof (struct in6_addr); } type = buffer[3]; memcpy (&tabla, &buffer[4], 4); prefix = buffer[8]; protocol = buffer[9]; tos = buffer[10]; scope = buffer[11]; has_prefsrc = FALSE; if (buffer[12] & 0x01) { has_prefsrc = TRUE; } num_nexthops = buffer[13]; memcpy (&prioridad, &buffer[14], 4); memcpy (&dest, &buffer[18], family_size); pos = 18 + family_size; if (has_prefsrc) { memcpy (&prefsrc, &buffer[pos], family_size); pos += family_size; } if (num_nexthops > 16) { num_nexthops = 16; } for (g = 0; g < num_nexthops; g++) { nexthops[g].has_gw = FALSE; if (buffer[pos] & 0x01) { /* Tiene GW */ nexthops[g].has_gw = TRUE; } pos++; nexthops[g].flags = buffer[pos]; pos++; nexthops[g].weight = buffer[pos]; pos++; pos++; /* El byte ignorado */ memcpy (&nexthops[g].out_index, &buffer[pos], 4); pos +=4; if (nexthops[g].has_gw) { memcpy (&nexthops[g].gw, &buffer[pos], family_size); pos += family_size; } } ni_route = ni_route_new (ni_client, family, type, tabla, &dest, prefix, protocol, tos, scope, (has_prefsrc ? &prefsrc : NULL), prioridad, num_nexthops, nexthops); if (family == AF_INET) { ni_client->priv->routes_v4 = g_list_append (ni_client->priv->routes_v4, ni_route); } else if (family == AF_INET6) { ni_client->priv->routes_v6 = g_list_append (ni_client->priv->routes_v6, ni_route); } g_signal_emit (ni_client, signals[NEW_ROUTE], 0, ni_route); } static void ni_client_process_route_del_response (NIClient *ni_client, gpointer data, guint size) { unsigned char *buffer = (unsigned char *) data; uint8_t family, family_size; uint8_t type, prefix; uint32_t tabla, prioridad; NIRoute *ni_route; struct_addr dest; GList *list_routes; family = buffer[2]; if (family == AF_INET) { family_size = sizeof (struct in_addr); list_routes = ni_client->priv->routes_v4; } else if (family == AF_INET6) { family_size = sizeof (struct in6_addr); list_routes = ni_client->priv->routes_v6; } type = buffer[3]; memcpy (&tabla, &buffer[4], 4); prefix = buffer[8]; memcpy (&prioridad, &buffer[9], 4); memcpy (&dest, &buffer[13], family_size); ni_route = _ni_client_search_route (list_routes, family, type, tabla, &dest, prefix, prioridad); if (ni_route == NULL) { /* ¿No hay ruta? Raro */ return; } /* Desligar la ruta */ if (family == AF_INET) { ni_client->priv->routes_v4 = g_list_remove (ni_client->priv->routes_v4, ni_route); } else if (family == AF_INET6) { ni_client->priv->routes_v6 = g_list_remove (ni_client->priv->routes_v6, ni_route); } g_signal_emit (ni_client, signals[DELETE_ROUTE], 0, ni_route); /* Liberar el objeto */ g_object_unref (ni_route); } static gboolean ni_client_data_read (NIClient *ni_client, gpointer data, guint size) { unsigned char *buffer = (unsigned char *) data; uint32_t index; NIInterface *ni_interface; if (buffer[0] == NET_INADOR_TYPE_EVENT) { switch (buffer[1]) { case NET_INADOR_EVENT_IFACE_ADDED: memcpy (&index, &buffer[2], 4); /* Si la interfaz no existe, proccesarla yo, para que se cree el objeto, si la interfaz ya existe, dejar pasar el mensaje */ ni_interface = (NIInterface *) g_hash_table_lookup (ni_client->priv->interfaces, GINT_TO_POINTER(index)); if (ni_interface == NULL) { ni_client_process_interface_response (ni_client, data, size); return TRUE; } else { return FALSE; } break; case NET_INADOR_EVENT_IFACE_REMOVED: memcpy (&index, &buffer[2], 4); /* La interfaz fué eliminada, nosotros procesamos este evento */ ni_interface = (NIInterface *) g_hash_table_lookup (ni_client->priv->interfaces, GINT_TO_POINTER(index)); if (ni_interface == NULL) { /* ¿Evento de una interfaz eliminada? Ignorar */ } else { g_hash_table_remove (ni_client->priv->interfaces, GINT_TO_POINTER(index)); g_signal_emit (ni_client, signals[DELETE_INTERFACE], 0, ni_interface); g_object_unref (ni_interface); } return TRUE; break; case NET_INADOR_EVENT_DHCP_STATUS: memcpy (&index, &buffer[2], 4); /* Si la interfaz no existe, proccesarla yo, para que se cree el objeto, si la interfaz ya existe, dejar pasar el mensaje */ ni_interface = (NIInterface *) g_hash_table_lookup (ni_client->priv->interfaces, GINT_TO_POINTER(index)); if (ni_interface == NULL) { /* Esta es una condición de error interesante. No debería ocurrir. Absorber el evento */ return TRUE; } /* Dejar pasar el evento para que la propia interfaz lo procese */ return FALSE; break; case NET_INADOR_EVENT_ROUTE_REMOVED: /* Eliminar una ruta es ligeramente mas complejo porque necesitamos que haga match con varios valores para eliminarla */ ni_client_process_route_del_response (ni_client, data, size); return TRUE; break; case NET_INADOR_EVENT_ROUTE_ADDED: /* TODO: Revisar si la ruta existe, entonces, en teoría, es una actualización */ ni_client_process_route_response (ni_client, data, size); return FALSE; break; } } else if (buffer[0] == NET_INADOR_TYPE_RESPONSE) { switch (buffer[1]) { case NET_INADOR_RESPONSE_IFACE: memcpy (&index, &buffer[2], 4); /* Si la interfaz no existe, proccesarla yo, para que se cree el objeto, si la interfaz ya existe, dejar pasar el mensaje */ ni_interface = (NIInterface *) g_hash_table_lookup (ni_client->priv->interfaces, GINT_TO_POINTER(index)); if (ni_interface == NULL) { ni_client_process_interface_response (ni_client, data, size); return TRUE; } else { return FALSE; } break; case NET_INADOR_RESPONSE_DHCP_STATUS: memcpy (&index, &buffer[2], 4); ni_interface = (NIInterface *) g_hash_table_lookup (ni_client->priv->interfaces, GINT_TO_POINTER(index)); if (ni_interface == NULL) { /* Esta es una condición de error interesante. No debería ocurrir. Absorber el evento */ return TRUE; } /* Dejar pasar el evento para que la propia interfaz lo procese */ return FALSE; break; case NET_INADOR_RESPONSE_ROUTE: /* Cuando hacemos listado, no hay problemas de actualización */ ni_client_process_route_response (ni_client, data, size); return FALSE; break; } } /* Procesar los datos aquí */ return TRUE; } static gboolean ni_client_read_from_socket (GIOChannel *source, GIOCondition condition, gpointer data) { NIClient *ni_client = NI_CLIENT (data); unsigned char buffer[8192]; int res; gboolean stop; int g; res = recv (ni_client->priv->client_socket, buffer, sizeof (buffer), 0); if (res <= 0) { /* El socket se cerró */ close (ni_client->priv->client_socket); ni_client->priv->client_socket = -1; /* Eliminar el source */ if (ni_client->priv->source > 0) { g_source_remove (ni_client->priv->source); ni_client->priv->source = 0; } /* TODO: Disparar aquí una señal de desconexión al network-inador */ return FALSE; } printf ("Data arrival: %i\n", res); for (g = 0; g < res; g++) { printf ("%02hhx ", buffer[g]); } printf ("\n"); /* Tenemos datos, hacer una validación básica */ if (buffer[0] != NET_INADOR_TYPE_RESPONSE && buffer[0] != NET_INADOR_TYPE_RESPONSE_ERROR && buffer[0] != NET_INADOR_TYPE_EVENT && buffer[0] != NET_INADOR_TYPE_RESPONSE_LISTING_END) { /* Tipo de paquete inválido */ return TRUE; } g_signal_emit (ni_client, signals[DATA_READ], 0, (void *) buffer, res, &stop); return TRUE; } static gboolean ni_client_foreach_remove_every_interface (gpointer key, gpointer value, gpointer user_data) { NIInterface *ni_interface = NI_INTERFACE (value); g_object_unref (ni_interface); return TRUE; } static void ni_client_dispose (GObject *obj) { NIClient *ni_client; GObjectClass *parent_class = g_type_class_peek (G_TYPE_OBJECT); ni_client = NI_CLIENT (obj); /* Recorrer todos los objetos interface para quitarles una referencia y liberarlos */ g_hash_table_foreach_remove (ni_client->priv->interfaces, ni_client_foreach_remove_every_interface, NULL); if (ni_client->priv->source > 0) { g_source_remove (ni_client->priv->source); ni_client->priv->source = 0; } parent_class->dispose (obj); } static void ni_client_finalize (GObject *obj) { NIClient *ni_client; GObjectClass *parent_class = g_type_class_peek (G_TYPE_OBJECT); ni_client = NI_CLIENT (obj); g_hash_table_destroy (ni_client->priv->interfaces); if (ni_client->priv->client_socket >= 0) { close (ni_client->priv->client_socket); ni_client->priv->client_socket = 0; } if (ni_client->priv->socket_path != NULL) { g_free (ni_client->priv->socket_path); ni_client->priv->socket_path = NULL; } parent_class->finalize (obj); } static void ni_client_init (NIClient *ni_client) { NIClientPrivate *priv = ni_client_get_instance_private (ni_client); ni_client->priv = priv; priv->client_socket = -1; priv->socket_path = NULL; priv->routes_v4 = NULL; priv->routes_v6 = NULL; priv->source = 0; priv->events = NET_INADOR_EVENT_MASK_INTERFACES | NET_INADOR_EVENT_MASK_IP | NET_INADOR_EVENT_MASK_DHCP_STATUS | NET_INADOR_EVENT_MASK_ROUTES; priv->interfaces = g_hash_table_new (g_direct_hash, g_direct_equal); } void ni_client_update_event_mask (NIClient *ni_client) { unsigned char buffer[8]; if (ni_client->priv->client_socket < 0) { return; } buffer[0] = NET_INADOR_TYPE_COMMAND; buffer[1] = NET_INADOR_COMMAND_SET_EVENT_MASK; memcpy (&buffer[2], &ni_client->priv->events, 4); send (ni_client->priv->client_socket, buffer, 6, 0); } void ni_client_ask_interfaces (NIClient *ni_client) { unsigned char buffer[8]; if (ni_client->priv->client_socket < 0) { return; } buffer[0] = NET_INADOR_TYPE_COMMAND; buffer[1] = NET_INADOR_COMMAND_LIST_IFACES; send (ni_client->priv->client_socket, buffer, 2, 0); } void ni_client_ask_routes (NIClient *ni_client) { unsigned char buffer[8]; if (ni_client->priv->client_socket < 0) { return; } buffer[0] = NET_INADOR_TYPE_COMMAND; buffer[1] = NET_INADOR_COMMAND_LIST_ROUTES; memset (&buffer[2], 0, 4); buffer[6] = AF_UNSPEC; send (ni_client->priv->client_socket, buffer, 7, 0); } void ni_client_ask_ip_new (NIClient *ni_client, NIInterface *ni_interface, int family, int prefix, struct_addr *addr, uint32_t flags, unsigned char scope, int has_local, struct_addr *local_addr, int has_brd, struct_addr *brd_addr, uint32_t *cacheinfo) { unsigned char buffer[80]; uint32_t index; int family_size; int pos; if (ni_client->priv->client_socket < 0) { return; } if (family == AF_INET) { family_size = sizeof (struct in_addr); } else if (family == AF_INET6) { family_size = sizeof (struct in6_addr); } index = ni_interface_get_index (ni_interface); buffer[0] = NET_INADOR_TYPE_COMMAND; buffer[1] = NET_INADOR_COMMAND_ADD_IP; memcpy (&buffer[2], &index, 4); buffer[6] = family; buffer[7] = prefix; buffer[8] = 0; if (has_local) { buffer[8] |= 0x01; } if (has_brd) { buffer[8] |= 0x02; } buffer[9] = scope; memcpy (&buffer[10], &flags, 4); /* Copiar los timestamp solicitados */ memcpy (&buffer[14], cacheinfo, 8); memcpy (&buffer[22], addr, family_size); pos = 22 + family_size; if (has_local) { memcpy (&buffer[pos], local_addr, family_size); pos += family_size; } if (has_brd) { memcpy (&buffer[pos], brd_addr, family_size); pos += family_size; } send (ni_client->priv->client_socket, buffer, pos, 0); } void ni_client_ask_ip_delete (NIClient *ni_client, NIInterface *ni_interface, NIIP *ni_ip) { unsigned char buffer[32]; uint32_t index; int family_size; int pos; const struct_addr *addr, *local_addr; int family = ni_ip_get_family (ni_ip); if (ni_client->priv->client_socket < 0) { return; } if (family == AF_INET) { family_size = sizeof (struct in_addr); } else if (family == AF_INET6) { family_size = sizeof (struct in6_addr); } index = ni_interface_get_index (ni_interface); buffer[0] = NET_INADOR_TYPE_COMMAND; buffer[1] = NET_INADOR_COMMAND_REMOVE_IP; memcpy (&buffer[2], &index, 4); buffer[6] = family; buffer[7] = ni_ip_get_prefix (ni_ip); buffer[8] = 0; if (ni_ip_has_local (ni_ip)) { buffer[8] |= 0x01; } addr = ni_ip_get_addr (ni_ip); memcpy (&buffer[9], addr, family_size); pos = 9 + family_size; if (ni_ip_has_local (ni_ip)) { local_addr = ni_ip_get_local_addr (ni_ip); memcpy (&buffer[9 + family_size], local_addr, family_size); pos += family_size; } send (ni_client->priv->client_socket, buffer, pos, 0); } void ni_client_ask_change_iface_name (NIClient *ni_client, NIInterface *ni_interface, const gchar *new_name) { unsigned char buffer[128]; uint32_t index; int n_len; if (ni_client->priv->client_socket < 0) { return; } if (new_name[0] == 0) return; index = ni_interface_get_index (ni_interface); buffer[0] = NET_INADOR_TYPE_COMMAND; buffer[1] = NET_INADOR_COMMAND_IFACE_CHANGE_NAME; memcpy (&buffer[2], &index, 4); n_len = strlen (new_name); buffer[6] = n_len; memcpy (&buffer[7], new_name, buffer[6]); send (ni_client->priv->client_socket, buffer, 7 + n_len, 0); } void ni_client_ask_change_iface_mtu (NIClient *ni_client, NIInterface *ni_interface, guint new_mtu) { unsigned char buffer[128]; uint32_t index, mtu; if (ni_client->priv->client_socket < 0) { return; } index = ni_interface_get_index (ni_interface); buffer[0] = NET_INADOR_TYPE_COMMAND; buffer[1] = NET_INADOR_COMMAND_IFACE_CHANGE_MTU; memcpy (&buffer[2], &index, 4); //mtu = htonl (new_mtu); mtu = new_mtu; memcpy (&buffer[6], &mtu, 4); send (ni_client->priv->client_socket, buffer, 10, 0); } void ni_client_ask_ip_interface (NIClient *ni_client, NIInterface *ni_interface, int family) { unsigned char buffer[8]; uint32_t index; if (ni_client->priv->client_socket < 0) { return; } index = ni_interface_get_index (ni_interface); buffer[0] = NET_INADOR_TYPE_COMMAND; buffer[1] = NET_INADOR_COMMAND_LIST_IP; memcpy (&buffer[2], &index, 4); buffer[6] = family; send (ni_client->priv->client_socket, buffer, 7, 0); } void ni_client_ask_up_down_interface (NIClient *ni_client, NIInterface *ni_interface, gboolean is_up) { unsigned char buffer[8]; uint32_t index; if (ni_client->priv->client_socket < 0) { return; } index = ni_interface_get_index (ni_interface); buffer[0] = NET_INADOR_TYPE_COMMAND; buffer[1] = (is_up ? NET_INADOR_COMMAND_IFACE_UP : NET_INADOR_COMMAND_IFACE_DOWN); memcpy (&buffer[2], &index, 4); send (ni_client->priv->client_socket, buffer, 6, 0); } void ni_client_ask_dhcp_run_interface (NIClient *ni_client, NIInterface *ni_interface, int type) { unsigned char buffer[16]; uint32_t index; uint32_t flags = 0x1; if (ni_client->priv->client_socket < 0) { return; } if (type < 1 || type > 2) { /* Solo se permite tipo 1 o tipo 2 */ return; } index = ni_interface_get_index (ni_interface); buffer[0] = NET_INADOR_TYPE_COMMAND; buffer[1] = NET_INADOR_COMMAND_RUN_DHCP; memcpy (&buffer[2], &index, 4); buffer[6] = AF_INET; buffer[7] = type; memcpy (&buffer[8], &flags, 4); send (ni_client->priv->client_socket, buffer, 12, 0); } void ni_client_ask_dhcp_stop_interface (NIClient *ni_client, NIInterface *ni_interface) { unsigned char buffer[8]; uint32_t index; if (ni_client->priv->client_socket < 0) { return; } index = ni_interface_get_index (ni_interface); buffer[0] = NET_INADOR_TYPE_COMMAND; buffer[1] = NET_INADOR_COMMAND_STOP_DHCP; memcpy (&buffer[2], &index, 4); buffer[6] = AF_INET; send (ni_client->priv->client_socket, buffer, 7, 0); } void ni_client_ask_create_bridge (NIClient *ni_client, const gchar *name) { unsigned char buffer[128]; int n_len; if (ni_client->priv->client_socket < 0) { return; } if (name[0] == 0) return; buffer[0] = NET_INADOR_TYPE_COMMAND; buffer[1] = NET_INADOR_COMMAND_CREATE_BRIDGE; n_len = strlen (name); buffer[2] = n_len; memcpy (&buffer[3], name, buffer[2]); send (ni_client->priv->client_socket, buffer, 3 + n_len, 0); } void ni_client_ask_interface_clear_master (NIClient *ni_client, NIInterface *ni_interface) { unsigned char buffer[128]; uint32_t index; if (ni_client->priv->client_socket < 0) { return; } buffer[0] = NET_INADOR_TYPE_COMMAND; buffer[1] = NET_INADOR_COMMAND_CLEAR_MASTER; index = ni_interface_get_index (ni_interface); memcpy (&buffer[2], &index, 4); send (ni_client->priv->client_socket, buffer, 6, 0); } void ni_client_ask_interface_set_master (NIClient *ni_client, NIInterface *ni_interface, NIInterface *master) { unsigned char buffer[128]; uint32_t index; if (ni_client->priv->client_socket < 0) { return; } buffer[0] = NET_INADOR_TYPE_COMMAND; buffer[1] = NET_INADOR_COMMAND_SET_MASTER; index = ni_interface_get_index (ni_interface); memcpy (&buffer[2], &index, 4); index = ni_interface_get_index (master); memcpy (&buffer[6], &index, 4); send (ni_client->priv->client_socket, buffer, 10, 0); } void ni_client_ask_interface_dhcp_status (NIClient *ni_client, NIInterface *ni_interface) { unsigned char buffer[8]; uint32_t index; if (ni_client->priv->client_socket < 0) { return; } index = ni_interface_get_index (ni_interface); buffer[0] = NET_INADOR_TYPE_COMMAND; buffer[1] = NET_INADOR_COMMAND_GET_DHCP_STATUS; memcpy (&buffer[2], &index, 4); buffer[6] = AF_INET; send (ni_client->priv->client_socket, buffer, 7, 0); } void ni_client_ask_route_new (NIClient *ni_client, int family, int prefix, struct_addr *dest, int type, uint32_t tabla, int protocol, int tos, int scope, int has_prefsrc, struct_addr *prefsrc, uint32_t priority, GList *gws) { unsigned char buffer[80]; int family_size; int pos, c; GList *g; NIRouteNH *nexthop; if (ni_client->priv->client_socket < 0) { return; } if (family == AF_INET) { family_size = sizeof (struct in_addr); } else if (family == AF_INET6) { family_size = sizeof (struct in6_addr); } buffer[0] = NET_INADOR_TYPE_COMMAND; buffer[1] = NET_INADOR_COMMAND_ADD_ROUTE; buffer[2] = family; buffer[3] = type; memcpy (&buffer[4], &tabla, 4); buffer[8] = prefix; buffer[9] = protocol; buffer[10] = tos; buffer[11] = scope; buffer[12] = 0; if (has_prefsrc) { buffer[12] |= 0x01; } /* La cantidad de gateways que voy a enviar con esta ruta */ g = gws; c = 0; while (g != NULL) { c++; g = g->next; } buffer[13] = c; memcpy (&buffer[14], &priority, 4); memcpy (&buffer[18], dest, family_size); pos = 18 + family_size; if (has_prefsrc) { memcpy (&buffer[pos], prefsrc, family_size); pos += family_size; } g = gws; while (g != NULL) { nexthop = (NIRouteNH *) g->data; buffer[pos] = 0; if (nexthop->has_gw) { buffer[pos] |= 0x01; } buffer[pos + 1] = nexthop->flags; buffer[pos + 2] = nexthop->weight; buffer[pos + 3] = 0; memcpy (&buffer[pos + 4], &nexthop->out_index, 4); if (nexthop->has_gw) { memcpy (&buffer[pos + 8], &nexthop->gw, family_size); pos += (8 + family_size); } else { pos += 8; } g = g->next; } send (ni_client->priv->client_socket, buffer, pos, 0); } static void ni_client_foreach_has_table (gpointer key, gpointer value, gpointer data) { GList **lista = (GList **) data; *lista = g_list_append (*lista, value); } GList *ni_client_get_list_interfaces (NIClient *ni_client) { g_return_val_if_fail (NI_IS_CLIENT (ni_client), NULL); GList *all_ifaces = NULL; g_hash_table_foreach (ni_client->priv->interfaces, ni_client_foreach_has_table, &all_ifaces); return all_ifaces; } NIInterface *ni_client_get_interface_by_index (NIClient *ni_client, guint index) { g_return_val_if_fail (NI_IS_CLIENT (ni_client), NULL); return g_hash_table_lookup (ni_client->priv->interfaces, GINT_TO_POINTER(index)); } GList *ni_client_get_routes_v4 (NIClient *ni_client) { g_return_val_if_fail (NI_IS_CLIENT (ni_client), NULL); return ni_client->priv->routes_v4; } GList *ni_client_get_routes_v6 (NIClient *ni_client) { g_return_val_if_fail (NI_IS_CLIENT (ni_client), NULL); return ni_client->priv->routes_v6; } gboolean ni_client_connect (NIClient *ni_client) { int s, ret; struct sockaddr_un path_dest; GIOChannel *channel; if (ni_client->priv->client_socket >= 0) { return FALSE; } s = socket (AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0); if (s < 0) { return FALSE; } path_dest.sun_family = AF_UNIX; strncpy (path_dest.sun_path, ni_client->priv->socket_path, sizeof (path_dest.sun_path)); ret = connect (s, (struct sockaddr *) &path_dest, sizeof (path_dest)); if (ret < 0) { perror ("Connect"); close (s); return FALSE; } /* Tengo socket, instalar vigilante */ ni_client->priv->client_socket = s; channel = g_io_channel_unix_new (s); ni_client->priv->source = g_io_add_watch (channel, G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP, ni_client_read_from_socket, ni_client); g_io_channel_unref (channel); /* Pedir eventos */ ni_client_update_event_mask (ni_client); /* Pedir un listado de interfaces */ ni_client_ask_interfaces (ni_client); /* Pedir un listado de rutas */ ni_client_ask_routes (ni_client); } void ni_client_disconnect (NIClient *ni_client) { /* TODO: Escribir esto */ } NIClient *ni_client_new (void) { return g_object_new (NI_TYPE_CLIENT, NULL); } NIClient *ni_client_new_with_path (const char *path) { return g_object_new (NI_TYPE_CLIENT, "socket-path", path, NULL); }