Explicación detallada de la red de Android 9.0

1. proceso DHCP

  Antes de analizar netd, primero comprenda el proceso de adquisición automática de IP por parte de la red. Consulte el proceso a continuación para ver el código:

 (1) WIFI escanea la red disponible y se conecta. Ruta del código: \frameworks\opt\net\wifi\service\java\com\android\server\wifi\WifiStateMachine.java

Copiar código

                caso WifiMonitor.NETWORK_CONNECTION_EVENT:
                    if (mVerboseLoggingEnabled) log("Conexión de red establecida");
                    mLastNetworkId = mensaje.arg1;
                    mWifiConfigManager.clearRecentFailureReason(mLastNetworkId);
                    mLastBssid = (Cadena) mensaje.obj;
                    ReasonCode = mensaje.arg2;
                    // TODO: Esta comprobación no debería ser necesaria después de la refactorización de WifiStateMachinePrime.
                    // Actualmente, la última configuración de red conectada se deja en
                    // wpa_supplicant, esto puede resultar en que wpa_supplicant inicie la conexión
                    // a él después de recargar el almacén de configuración. Por lo tanto, es posible que las búsquedas de ID de red antiguas no
                    // funciona, así que desconecta la red y deja que el selector de red vuelva a seleccionar una nueva
                    // red.
                    config = getCurrentWifiConfiguration();
                    si (config! = nulo) {
                        mWifiInfo.setBSSID(mLastBssid);
                        mWifiInfo.setNetworkId(mLastNetworkId);
                        mWifiInfo.setMacAddress(mWifiNative.getMacAddress(mInterfaceName));

                        ScanDetailCache scanDetailCache =
                                mWifiConfigManager.getScanDetailCacheForNetwork(config.networkId);
                        if (scanDetailCache != nulo &&mLastBssid != nulo) {
                            ScanResult scanResult = scanDetailCache.getScanResult(mLastBssid);
                            if (scanResult! = nulo) {
                                mWifiInfo.setFrequency(scanResult.frequency);
                            }
                        }
                            } más {
                                config.enterpriseConfig.setAnonymousIdentity(anonymousIdentity);
                            if (identidad anónima! = nulo) {
                                    mWifiNative.getEapAnonymousIdentity(mInterfaceName);
                            Cadena identidad anónima =
                                        config.enterpriseConfig.getEapMethod())) {
                                && TelephonyUtil.isSimEapMethod(
                        si (config.enterpriseConfig! = nulo
                        // Necesitamos obtener el seudónimo actualizado del solicitante para EAP-SIM/AKA/AKA'
                                Log.d(TAG, "Error al obtener la identidad anónima actualizada"
                                        + " del solicitante, restablezcalo en WifiConfiguration.");
                                config.enterpriseConfig.setAnonymousIdentity(null);
                            }
                    }
                        enviarMensaje(CMD_DISCONNECT);
                                + ", desconectando...");
                        logw("Conectado a un ID de red desconocido " + mLastNetworkId
                    } más { TransitionTo(mObtainingIpState);
                        sendNetworkStateChangeBroadcast(mLastBssid);
                        }
                       

Copiar código

     Entre ellos, TransitionTo (mObtainingIpState) se llama de la siguiente manera y realiza una configuración de IP automática o estática según la información del archivo de configuración wifi actual:

Copiar código

    clase ObtainingIpState extiende Estado {
        @Anular
        entrada nula pública() {
            configuración Wifi final currentConfig = getCurrentWifiConfiguration();
            booleano final isUsingStaticIp =
                    (currentConfig.getIpAssignment() == IpConfiguration.IpAssignment.STATIC);
            si (mVerboseLoggingEnabled) {
                clave de cadena final = currentConfig.configKey();
                log("ingrese ObtainingIpState netId=" + Integer.toString(mLastNetworkId)
                        + " " + tecla + " "
                        + " deambular =" + mIsAutoRoaming
                        + " estático = " + isUsingStaticIp);
            }

            // Enviar evento a CM & transmisión de cambio de red
            setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);

            // Debemos borrar el BSSID de configuración, ya que el chipset wifi puede decidir moverse
            // a partir de este momento y teniendo el BSSID especificado en el bloque de red
            // provoca que falle la itinerancia y que el dispositivo se desconecte.
            clearTargetBssid("Obteniendo dirección IP");

            // Detener IpClient en caso de que cambiemos de DHCP a estático
            // configuración o viceversa.
            //
            // TODO: Solo ingresamos a este estado la primera vez que nos conectamos a
            // red, nunca al cambiar entre configuración estática y
            //DHCP. Cuando pasamos de la configuración estática a DHCP en
            // particular, debemos decirle a ConnectivityService que estamos
            // desconectado, porque DHCP puede tardar mucho tiempo durante el cual
            // las API de conectividad como getActiveNetworkInfo no deberían devolver
            // CONECTADO.
            stopIpClient();

            mIpClient.setHttpProxy(currentConfig.getHttpProxy());
            if (!TextUtils.isEmpty(mTcpBufferSizes)) {
                mIpClient.setTcpBufferSizes(mTcpBufferSizes);
            }
            final IpClient.ProvisioningConfiguration prov;
            si (!isUsingStaticIp) {
                prov = IpClient.buildProvisioningConfiguration()
                            .withPreDhcpAction()
                            .withApfCapabilities(mWifiNative.getApfCapabilities(mInterfaceName))
                            .withNetwork(getCurrentNetwork())
                            .withDisplayName(currentConfig.SSID)
            } más {
                            .withRandomMacAddress()
                            .build();
                StaticIpConfiguration staticIpConfig = currentConfig.getStaticIpConfiguration();
                prov = IpClient.buildProvisioningConfiguration()
                            .withStaticConfiguration(staticIpConfig)
                            .withApfCapabilities(mWifiNative.getApfCapabilities(mInterfaceName))
                            .withNetwork(getCurrentNetwork())
                            .withDisplayName(currentConfig.SSID)
                            .build();
            } mIpClient.startProvisioning(prov);
           // Obtener estadísticas de la capa de enlace para obtener nuevos contadores de paquetes de transmisión
            getWifiLinkLayerStats();
        }
        }
            }
                    devolver NOT_HANDLED;
                predeterminado:
                    romper;
                    aplazarMensaje(mensaje);
                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
                caso CMD_SET_HIGH_PERF_MODE:
                    devolver NOT_HANDLED;
                            WifiMetricsProto.ConnectionEvent.HLF_NONE);
                            WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION,
                    reportConnectionAttemptEnd(
                caso WifiMonitor.NETWORK_DISCONNECTION_EVENT:
                    descanso;
                    aplazarMensaje(mensaje);
                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
                caso WifiManager.SAVE_NETWORK:
                    romper;
                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
                caso CMD_START_ROAM:
                caso CMD_START_CONNECT:
            cambiar(mensaje.qué) {
            logStateAndMessage(mensaje, esto);
        mensaje de proceso booleano público (mensaje de mensaje) {
           

Copiar código

 Establezca el estado en DetailState.OBTAINING_IPADDR. Después de inicializar la configuración, se llamará a startProvisioning de IpClient.

Copiar código

    public void startProvisioning(Requisito de configuración de aprovisionamiento) {
        si (!req.isValid()) {
            doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
            volver;
        }
    }
        sendMessage(CMD_START, nueva configuración de aprovisionamiento(req));
        mCallback.setNeighborDiscoveryOffload(true);
        }
            devolver;
            doImmediateProvisioningFailure(IpManagerEvent.ERROR_INTERFACE_NOT_FOUND);
            logError("Error al encontrar InterfaceParams para " + mInterfaceName);
        if (mInterfaceParams == nulo) {
 
 

Copiar código

El trabajo de preparación de IPClient es enviar el mensaje CMD_START junto con la configuración y luego pasar al método RunningState. El método de entrada sigue diferentes procesos dependiendo de si está configurado ipv6 o ipv4. Ahora el valor predeterminado es generalmente ipv4.

Copiar código

    clase RunningState extiende el estado {
        ConnectivityPacketTracker privado mPacketTracker;
        mDhcpActionInFlight booleano privado;

        @Anular
        entrada nula pública() {
            ApfFilter.ApfConfiguration apfConfig = nuevo ApfFilter.ApfConfiguration();
            apfConfig.apfCapabilities = mConfiguration.mApfCapabilities;
            apfConfig.multicastFilter = mMulticastFiltering;
            // Obtener la configuración de ApfFilter desde Context
            apfConfig.ieee802_3Filter =
                    mContext.getResources().getBoolean(R.bool.config_apfDrop802_3Frames);
            apfConfig.ethTypeBlackList =
                    mContext.getResources().getIntArray(R.array.config_apfEthTypeBlackList);
            mApfFilter = ApfFilter.maybeCreate(mContext, apfConfig, mInterfaceParams, mCallback);
            // TODO: investigar los efectos de cualquier filtrado de multidifusión acelerado/interferiendo con el
            // resto del inicio de esta configuración IP.
            si (mApfFilter == nulo) {
                mCallback.setFallbackMulticastFilter(mMulticastFiltering);
            }

            mPacketTracker = crearPacketTracker();
            if (mPacketTracker != null) mPacketTracker.start(mConfiguration.mDisplayName);

            if (mConfiguration.mEnableIPv6 && !startIPv6()) {
                doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6);
                transiciónA(mStoppingState);
                devolver;
            }

            if (mConfiguration.mEnableIPv4 && !startIPv4()) {
                doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV4);
                transiciónA(mStoppingState);
                devolver;
            }

            configuración inicial de configuración final = mConfiguration.mInitialConfig;
            if ((config != null) && !applyInitialConfig(config)) {
                // TODO introduce una nueva constante IpManagerEvent para distinguir este caso de error.
                doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
                transiciónA(mStoppingState);
                volver;
            }
            }
                mMultinetworkPolicyTracker.start();
                        mContext, getHandler(),
                mMultinetworkPolicyTracker = nuevo MultinetworkPolicyTracker(

                        () -> { mLog.log("OBSERVADO EviteBadWifi cambiado"); });

            if (mConfiguration.mUsingIpReachabilityMonitor && !startIpReachabilityMonitor()) {
                doImmediateProvisioningFailure(
                        IpManagerEvent.ERROR_STARTING_IPREACHABILITYMONITOR);
                transiciónA(mStoppingState);
                devolver;
            }
        }

Copiar código

Mire la lógica startIPv4:

Copiar código

    startIPv4 booleano privado() {
        // Si tenemos una StaticIpConfiguration intentamos aplicarla y
        // maneja el resultado en consecuencia.
        si (mConfiguration.mStaticIpConfig! = nulo) {
            if (mInterfaceCtrl.setIPv4Address(mConfiguration.mStaticIpConfig.ipAddress)) {
                handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig));
            } más {
                devolver falso;
            }
    }
        }            mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);
            mDhcpClient.registerForPreDhcpNotification();
            mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpClient.this, mInterfaceParams);
            // Inicie DHCPv4.

Copiar código

DhcpClient también maneja el cambio de estado de inicio CMD_START_DHCP. Después de un tiempo, el cambio de estado volverá a mDhcpInitState.

Copiar código

    clase StoppedState extiende el estado {
        @Anular
        mensaje de proceso booleano público (mensaje de mensaje) {
            cambiar (mensaje.qué) {
                caso CMD_START_DHCP:
                    si (mRegisteredForPreDhcpNotification) {
                        transiciónA(mWaitBeforeStartState);
                    } más {
                        transiciónA(mDhcpInitState);
                    }
        }
            }
                    devolver NOT_HANDLED;
                predeterminado:
                    devolver MANEJADO;
                    transiciónA(mOtroEstado);
                caso CMD_PRE_DHCP_ACTION_COMPLETE:
            cambiar (mensaje.qué) {
            super.processMessage(mensaje);
        mensaje de proceso booleano público (mensaje de mensaje) {
        @Anular
        }
            mController.sendMessage(CMD_PRE_DHCP_ACTION);
            super.enter();
        entrada nula pública() {
        @Anular
        Estado protegido Estado madre;
    la clase abstracta WaitBeforeOtherState extiende LoggingState {
    // CMD_PRE_DHCP_ACTION_COMPLETE y luego pasa a mOtherState.
    // Envía CMD_PRE_DHCP_ACTION al controlador, espera a que el controlador responda con
    Estado privado mWaitBeforeStartState = nuevo WaitBeforeStartState(mDhcpInitState);
            }
                    devolver NOT_HANDLED;
                predeterminado:
 
 
 
 
 

Copiar código

IPClient maneja CMD_CONFIGURE_LINKADDRESS enviado por DhcpClient

Copiar código

                caso DhcpClient.CMD_CONFIGURE_LINKADDRESS: {
                    dirección de enlace final dirección ip = (dirección de enlace) msg.obj;
                    if (mInterfaceCtrl.setIPv4Address(ipAddress)) {
                        mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED);
                    } más {
                        logError("Error al establecer la dirección IPv4");
                        despachoCallback(ProvisioningChange.LOST_PROVISIONING,
                                nuevas LinkProperties(mLinkProperties));
                        transiciónA(mStoppingState);
                    }
                }

Copiar código

DhcpClient cambia a mWaitBeforeStartState. Esto se debe a la llamada anterior a mDhcpClient.registerForPreDhcpNotification(); por lo que el estado aquí está esperando a que se completen otros estados. De hecho, algunos preparativos para dhcp deben completarse en WifiStateMachine, por lo que el proceso aquí necesita esperar Una vez completado el proceso, naturalmente cambiará a DhcpInitState.

Copiar código

    clase DhcpInitState extiende PacketRetransmittingState {
        público DhcpInitState() {
            súper();
        }
        }
            }
                transiciónA(mDhcpRequestingState);
                Log.d(TAG, "Tiene arrendamiento pendiente: " + mOffer);
            if (mOferta != nulo) {
            mOferta = paquete.toDhcpResults();
            if (!(instancia de paquete de DhcpOfferPacket)) return;
            si (!isValidPacket(paquete)) regresa;
        }
            devolver sendDiscoverPacket();
        }
            mLastInitEnterTime = SystemClock.elapsedRealtime();
            iniciarNuevaTransacción();
            super.enter();
        entrada nula pública() {
 
 
 

Copiar código

DHCPREQUEST recibe la respuesta y envía un mensaje CMD_POST_DHCP_ACTION a IpClient.

Copiar código

    notifySuccess anulado privado() {
        mController.sendMessage(
                CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, nuevo DhcpResults(mDhcpLease));
    }
        }
            }
                transiciónA(mDhcpInitState);
                mOferta = nula;
                Log.d(TAG, "NAK recibido, regresando a INIT");
                // TODO: Espere un momento antes de regresar al estado INIT.
                }
                    transiciónA(mConfiguringInterfaceState);
                    aceptarDhcpResults(resultados, "Confirmado");
                    setDhcpLeaseExpiry(paquete);
                si (resultados! = nulo) {
                Resultados de DhcpResults = paquete.toDhcpResults();
            if ((instancia de paquete de DhcpAckPacket)) {
            si (!isValidPacket(paquete)) regresa;
        paquete de recepción vacío protegido (paquete DhcpPacket) {
        }
                    INADDR_BROADCAST); // dirección de destino del paquete
                    (Inet4Address) mOffer.serverAddress, // DHCP_SERVER_IDENTIFIER
                    (Inet4Address) mOffer.ipAddress.getAddress(), // DHCP_REQUESTED_IP
                    INADDR_ANY, // ciaddr
            devolver enviarRequestPacket(
        }
            mTimeout = DHCP_TIMEOUT_MS / 2;
        público DhcpRequestingState() {
    }
        notificarSuccess();
        Log.d(TAG, mensaje + " arrendamiento: " + mDhcpLease);
        mOferta = nula;
        mDhcpLease = resultados;
 
 
 
 
 

Copiar código

IpClient maneja:

Copiar código

                caso DhcpClient.CMD_POST_DHCP_ACTION:
                    detenerDhcpAction();
 
                    cambiar (msg.arg1) {
                        caso DhcpClient.DHCP_SUCCESS:
                            handleIPv4Success((DhcpResults) msg.obj);
                            romper;
                        caso DhcpClient.DHCP_FAILURE:
                            handleIPv4Failure();
                            romper;
                        predeterminado:
                            logError("Estado CMD_POST_DHCP_ACTION desconocido: %s", msg.arg1);
                    }

Copiar código

Devolución de llamada a WifiStateMachine:

        @Anular
        public void onProvisioningSuccess(LinkProperties newLp) {
            mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL);
            enviarMessage(CMD_UPDATE_LINKPROPERTIES, newLp);
            enviarMensaje(CMD_IP_CONFIGURATION_SUCCESSFUL);
        }

Después de que la configuración de IP sea exitosa, configure el estado de la red en conectado y envíe una notificación de transmisión, y finalmente el estado cambia a ConnectedState.

2. Explicación detallada del principio de funcionamiento de netd.

  NETD es un proceso Daemon del sistema Android que se especializa en administrar enlaces de red, políticas de enrutamiento/ancho de banda/firewall e iptables. Se carga cuando se inicia el sistema Anroid:

Copiar código

servicio netd /system/bin/netd
    clase principal
    socket netd stream 0660 sistema raíz
    socket dnsproxyd flujo 0660 raíz inet
    sistema raíz socket mdns stream 0660
    socket fwmarkd flujo 0660 raíz inet
    onrestart reiniciar cigoto
    onrestart reiniciar zygote_secundario

Copiar código

Cuando se inicia netd, se crearán cuatro sockets para que otros procesos se comuniquen con netd:

  • netd: interactúa principalmente con FrameworkNetworkManagementService, utilizado para controlar el estado del puerto de red y la tabla de enrutamiento
  • dnsproxyd: Control y configuración del proxy DNS, utilizado para el reenvío de solicitudes de DNS privado (DNS Over TLS)
  • mdns: DNS de multidifusión (DNS de multidifusión, consulte RFCRFC 6762 - DNS de multidifusión), utilizado para el descubrimiento de servicios basado en una conexión WIFI (NSD , descubrimiento de servicios de red)
  • fwmarkd: configuración del enrutamiento de políticas de iptables (fwmark) (enrutamiento de políticas, como configurar permisos de red, conectar etiquetas, etc.

En general, el proceso netd establece un puente de comunicación entre los servicios de capa intermedia de AndroidNetworkManagementService y el kernel.

1.Inicio e inicialización de Netd

Cuando comienza el proceso netd, hace principalmente las siguientes cosas:

  • Crear unNetlinkManager para administrar la conexión netlink para comunicarse con el kernel
  • Inicializar clases de control de red, como control de enrutamientoRouteController, control de ancho de bandaBandwidthController
  • Iniciar varias clases de escucha de eventos: DnsProxyListenerEscuchar el proxy DNS; CommandListenerEscuchar comandos de NetworkManagement
  • Inicie NetdHwService para proporcionar una interfaz para la capa HAL

Copiar código

int principal() {
    usando android::net::gCtls;
    Cronómetro s;

    ALOGI("Netd 1.0 iniciando");
    remove_pid_file();

    bloqueSigpipe();

    // Antes de hacer cualquier cosa que pueda bifurcarse, marque CLOEXEC los sockets UNIX que obtenemos de init.
    // FrameworkListener también hace esto en la inicialización, pero solo inicializamos estos
    // componentes después de haber inicializado otros subsistemas que pueden bifurcarse.
    para (const auto& sock: { CommandListener::SOCKET_NAME,
                              DnsProxyListener::SOCKET_NAME,
                              FwmarkServer::SOCKET_NAME,
                              MDnsSdListener::SOCKET_NAME }) {
        setCloseOnExec(calcetín);
    }
    FwmarkServer fwmarkServer(&gCtls->netCtrl, &gCtls->eventReporter,&gCtls->trafficCtrl);
    }
        salida(1);
        ALOGE("No se puede iniciar MDnsSdListener (%s)", strerror(errno));
    si (mdnsl.startListener()) {
    }
        salida(1);
        ALOGE("No se puede iniciar DnsProxyListener (%s)", strerror(errno));
    si (dpl.startListener()) {
    DnsProxyListener dpl(&gCtls->netCtrl, &gCtls->eventReporter);
    setenv("ANDROID_DNS_MODE", "local", 1);
    // volver a este servicio, recursivamente.
    }
        }
            // Aún podemos continuar sin el registro de paquetes de activación.
            ALOGE("No se puede iniciar WakeupController: %s", toString(resultado).c_str());
        si (!isOk(resultado)) {
        estado automático = gCtls->wakeupCtrl.init(logListener.get());
        }
            salida(1);
            ALOGE("No se puede crear NFLogListener: %s", toString(resultado).c_str());
        si (!isOk(resultado)) {
    {
    }
        salida(1);
        ALOGE("No se puede iniciar NetlinkManager (%s)", strerror(errno));
    si (nm->start()) {
    nm->setBroadcaster((SocketListener *) &cl);
    CommandListener cl;
    gCtls->init();
    gCtls = nuevo android::net::Controllers();
    };
        salida(1);
        ALOGE("No se puede crear NetlinkManager");
    si (nm == nullptr) {








    si (fwmarkServer.startListener()) {
        ALOGE("No se puede iniciar FwmarkServer (%s)", strerror(errno));
        salida(1);
    }
}
    salida(0);
    remove_pid_file();
    ALOGI("Netd saliendo");
    IPCThreadState::self()->joinThreadPool();
    ALOGI("Netd comenzó en %dms", static_cast<int>(s.timeTaken()));
    ALOGI("Registro de NetdHwService: %.1fms", subTime.getTimeAndReset());
    }
        salida(1);
        ALOGE("No se puede iniciar NetdHwService: %d", ret);
    if ((ret = mHwSvc.start()) != android::OK) {
    NetdHwService mHwSvc;
    // disponibilidad para clientes HAL.
    // Ahora que netd está listo para procesar comandos, anuncia el servicio
    escribir_pid_file();
    ALOGI("Iniciando CommandListener: %.1fms", subTime.getTimeAndReset());
    }
        salida(1);
        ALOGE("No se puede iniciar CommandListener (%s)", strerror(errno));
     */
     * NetworkManagementService que estamos en funcionamiento y que nuestra interfaz de carpeta está lista.
    /*
    }
        salida(1);
        ALOGE("No se puede iniciar NetdNativeService: %d", ret);
    if ((ret = NetdNativeService::start()) != android::OK) {
    status_t ret;








Copiar código

  CommandListener se utiliza para recibir y procesar instrucciones de la capa superiorNetworkManagementService. Cuando se inicia netd, escuchará el socket < a i=3> y permite el procesamiento de hasta 4 solicitudes de clientes. Una vez iniciado netd, puede procesar solicitudes de instrucciones desde la capa intermedia e interactuar con el kernel. netd

2.Interacción entre netd y NetworkManagerService

  SystemServerCuando se inicia el proceso , se crea NetworkManagementService (en adelante denominado (NMS)). En este momento, NMS se comunicará activamente con netdEstablecer enlace de socket:

Copiar código

// SystemServer.java
       si (!disableNetwork) {
           traceBeginAndSlog("StartNetworkManagementService");
           prueba {
               networkManagement = NetworkManagementService.create(contexto);
               ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement);
           } captura (tirable e) {
               reportWtf("iniciando el servicio de administración de red", e);
           }
       }

Copiar código

Al crear NMS, inicie un nuevo hilo para comunicarse con netd,

Copiar código

 creación estática de NetworkManagementService (contexto de contexto, socket de cadena)
        lanza una excepción interrumpida {
    servicio NetworkManagementService final = nuevo NetworkManagementService(contexto, socket);
    final CountDownLatch connectSignal = service.mConnectedSignal;
    if (DBG) Slog.d(TAG, "Creando servicio de administración de red");
    servicio.mThread.start();
    if (DBG) Slog.d(TAG, "Esperando conexión de socket");
    connectSignal.await();
    service.connectNativeNetdService();

    servicio de devolución;
    }
    

       Private NetworkManagementService (contexto de contexto, conector de cadena) {
    mContext = contexto;

    // asegúrate de que esté en el mismo looper que nuestro NativeDaemonConnector para fines de sincronización
    mFgHandler = nuevo controlador (FgThread.get().getLooper());

    // No necesitamos este bloqueo de activación, ya que ahora tenemos una marca de tiempo para cuando
    // la red en realidad quedó inactiva. (Sería bueno seguir haciendo esto,
    // pero no quiero hacerlo a través del administrador de energía porque eso contamina el
    // historial de estadísticas de batería con ruido inútil.)
    //PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
    PowerManager.WakeLock wl = nulo; //pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, NETD_TAG);

    mConnector = nuevo NativeDaemonConnector(
        nuevo NetdCallbackReceiver(), socket, 10, NETD_TAG, 160, wl,
        FgThread.get().getLooper());
    mThread = nuevo hilo (mConnector, NETD_TAG);

    mDaemonHandler = nuevo controlador (FgThread.get().getLooper());

    // Agregarnos a los monitores Watchdog.
    Watchdog.getInstance().addMonitor(this);

    LocalServices.addService(NetworkManagementInternal.class, nuevo LocalService());

    sincronizado (mTetheringStatsProviders) {
        mTetheringStatsProviders.put(new NetdTetheringStatsProvider(), "netd");
    }

Copiar código

NMS establece comunicación de socket con a través de NativeDaemonConnector, hace principalmente dos cosas: netdNativeDaemonConnector

  • Establece un enlace de datos connetd
  • lee continuamente el flujo de datos en el socket: uno es el comando informado activamente por netd y el otro es el comando enviado por NMS a netd. Respuesta

    Copiar código

    @Anular
    ejecución pública vacía() {
        mCallbackHandler = nuevo controlador (mLooper, esto);
    
        mientras (verdadero) {
            prueba {
                escuchaToSocket();
            } captura (Excepción e) {
                loge("Error en NativeDaemonConnector: " + e);
                SystemClock.sleep(5000);
            }
                            } más {
                                }
                                    liberaciónWl = falso;
                                if (mCallbackHandler.sendMessage(msg)) {
                                        event.getCode(), uptimeMillisInt(), 0, event.getRawEvent());
                                Mensaje mensaje = mCallbackHandler.obtainMessage(
                            if (event.isClassUnsolicited()) {
                            log("RCV <- {" + evento + "}");
                                    NativeDaemonEvent.parseRawEvent(rawEvent, fdList);
                            evento final NativeDaemonEvent =
                        prueba {
                        liberación booleanaWl = false;
                                buffer, inicio, i - inicio, StandardCharsets.UTF_8);
                        cadena final rawEvent = nueva cadena (
                        // datos confidenciales
                        // Nota: no registre este mensaje sin formato ya que puede contener
                    if (búfer[i] == 0) {
                para (int i = 0; i < count; i++) {
                inicio = 0;
                contar += inicio;
                // Agrega nuestro punto de partida al conteo y restablece el inicio.
                }
                    romper;
                    loge("obtuve " + recuento + " lectura con inicio = " + inicio);
                si (cuenta < 0) {
                int count = inputStream.read(buffer, inicio, BUFFER_SIZE - inicio);
            mientras (verdadero) {
            int inicio = 0;
            byte[] buffer = nuevo byte[BUFFER_SIZE];
            FileDescriptor[] fdList = nulo;
            }
                mOutputStream = socket.getOutputStream();
            sincronizado (mDaemonLock) {
            InputStream inputStream = socket.getInputStream();
            socket.connect(dirección);
            Dirección LocalSocketAddress = determinarSocketAddress();
            socket = nuevo LocalSocket();
        prueba {
        Conector LocalSocket = nulo;
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
                                mResponseQueue.add(event.getCmdNumber(), evento);
                            }
    }
        }
            }
                loge("Error al cerrar el socket: " + ex);
            } captura (IOException ex) {
                }
                    socket.close();
                si (socket! = nulo) {
            }
                }
                    }
                        loge("Error al cerrar el flujo de salida: " + e);
                    } captura (IOException e) {
                        mOutputStream.close();
                        loge("cierre de flujo para " + mSocket);
                    prueba {
                si (mOutputStream! = nulo) {
            sincronizado (mDaemonLock) {
        } finalmente {
            tirar ex;
            loge("Error de comunicaciones: " + ex);
            }
                }
                    inicio = 0;
                } más {
                    inicio = restante;
                    System.arraycopy(búfer, inicio, búfer, 0, restante);
                    int final restante = BUFFER_SIZE - inicio;
                if (iniciar!= contar) {
                // buffer y leer de nuevo.
                }
                    }
                            }
                                mWakeLock.release();
                            si (liberarWl) {
                        } finalmente {
                            log("Mensaje de análisis de problema " + e);
    
    
    

    Copiar código

    Una vez establecido el enlace del socket , NMS y netd pueden comunicarse entre sí y enviar instrucciones y datos. NMS ejecuta las instrucciones correspondientes a través de NativeDaemonConnector, como como NMS Establezca la configuración de la interfaz de red (abrir/cerrar el puerto de red):

Copiar código

@Anular
public void setInterfaceConfig(String iface, InterfaceConfiguration cfg) {
    mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    Dirección de enlace linkAddr = cfg.getLinkAddress();
    if (linkAddr == nulo || linkAddr.getAddress() == nulo) {
        throw new IllegalStateException("Dirección de enlace nula dada");
    }
    }
        lanzar e.rethrowAsParcelableException();
    } captura (NativeDaemonConnectorException e) {
        mConnector.execute(cmd);
    }
        cmd.appendArg(bandera);
    for (bandera de cadena: cfg.getFlags()) {
            linkAddr.getPrefixLength());
            linkAddr.getAddress().getHostAddress(),

Copiar código

NativeDaemonConnector asignará una secuencia única a cada comando y pondrá su respuesta en una cola de bloqueo, esperando que netd

En la primera parte, se mencionó queSocketListener después de recibir las instrucciones de la capa superior, las distribuirá a las clases de instrucción correspondientes para su procesamiento (ver<). a i= 2>Subclases):SocketListenerFrameworkListener

3.NETD interactúa con el kernel

NETD intercambia mensajes con el kernel a través de netlink eventos. Como se vio en la primera parte, cuando se inicia netd, el socket se configurará para comunicarse con el núcleo. :

  • evento netlinkNETLINK_KOBJECT_UEVENT: se utiliza para que el kernel envíe mensajes anetd, como cambios en el estado del puerto de red;
  • evento netlinkNETLINK_ROUTE: se utiliza para recibir información de enrutamiento, como actualizaciones y eliminaciones de la tabla de enrutamiento;
  • evento de enlace de redNETLINK_NFLOG: se utiliza para recibir mensajes de cuota de uso de tráfico de datos, como si el uso de datos excede el límite;
  • evento netlinkNETLINK_NETFILTER se utiliza para recibir mensajes de filtrado de paquetes (netfilter);

    Copiar código

    int NetlinkManager::start() {
        if ((mUeventHandler = setupSocket(&mUeventSock, NETLINK_KOBJECT_UEVENT,
         0xffffffff, NetlinkListener::NETLINK_FORMAT_ASCII, falso)) == NULL) {
        devolver -1;
        }
    }
        devolver 0;
        }
        // TODO: devuelve -1 una vez que el emulador obtiene un nuevo kernel.
        ALOGE("No se puede abrir el socket estricto");
            0, NetlinkListener::NETLINK_FORMAT_BINARY_UNICAST, verdadero)) == NULL) {
        if ((mStrictHandler = setupSocket(&mStrictSock, NETLINK_NETFILTER,
        }
        // TODO: devuelve -1 una vez que el emulador obtiene un nuevo kernel.
        ALOGW("No se puede abrir el socket de cuota de qlog, verifique si xt_quota2 puede enviar a través de UeventHandler");
            NFLOG_QUOTA_GROUP, NetlinkListener::NETLINK_FORMAT_BINARY, falso)) == NULL) {
        }
        devolver -1;
         NetlinkListener::NETLINK_FORMAT_BINARY, falso)) == NULL) {
                                     (1 << (RTNLGRP_ND_USEROPT - 1)),
                                     RTMGRP_IPV6_ROUTE |
                                     RTMGRP_IPV6_IFADDR |
                                     RTMGRP_IPV4_IFADDR |
                                     RTMGRP_LINK |
    
    
    
    

    Copiar código

    Cada socket de netlink creará uno nuevoNetlinkHandler, que se utiliza para procesar mensajes del kernel y transmitir los mensajes a la capa superior:

    Copiar código

    void NetlinkHandler::onEvent(NetlinkEvent *evt) {
        const char *subsys = evt->getSubsystem();
        si (!subsys) {
        ALOGW("No se encontró ningún subsistema en el evento netlink");
        devolver;
        }
    
        if (!strcmp(subsys, "net")) {
        NetlinkEvent::Acción acción = evt->getAction();
        const char *iface = evt->findParam("INTERFACE");
    
        if (acción == NetlinkEvent::Acción::kAdd) {
            notifyInterfaceAdded(iface);
        } else if (action == NetlinkEvent::Action::kRemove) {
            notifyInterfaceRemoved(iface);
        } else if (acción == NetlinkEvent::Action::kChange) {
            evt->dump();
            notifyInterfaceChanged("nana", verdadero);
        } else if (acción == NetlinkEvent::Action::kLinkUp) {
            notifyInterfaceLinkChanged(iface, true);
        } else if (acción == NetlinkEvent::Action::kLinkDown) {
            notifyInterfaceLinkChanged(iface, false);
        } else if (action == NetlinkEvent::Action::kAddressUpdated ||
                   acción == NetlinkEvent::Acción::kAddressRemoved) {
            const char *dirección = evt->findParam("DIRECCIÓN");
            const char *flags = evt->findParam("BANDERAS");
            const char *alcance = evt->findParam("SCOPE");
            if (acción == NetlinkEvent::Action::kAddressRemoved && iface && dirección) {
                // Nota: si esta interfaz fue eliminada, iface es "" y no notificamos.
                SockDiag sd;
                si (sd.open()) {
                    char addrstr[INET6_ADDRSTRLEN];
                    strncpy(addrstr, dirección, tamaño de(addrstr));
                    char *barra = strchr(addrstr, '/');
                    si (barra oblicua) {
                        *barra diagonal = '\0';
                    }
                notifyInterfaceDnsServers(iface, duración, servidores);
            if (de por vida y servidores) {
            const char *servidores = evt->findParam("SERVIDORES");
            const char *lifetime = evt->findParam("LIFETIME");
            }
                notifyAddressChanged(acción, dirección, iface, banderas, alcance);
            if (iface && iface[0] && dirección && banderas && alcance) {
                }
                    ALOGE("Error al abrir el socket NETLINK_SOCK_DIAG: %s", strerror(errno));
                    }
                        ALOGE("Error al destruir sockets: %s", strerror(ret));
                    si (ret < 0) {
    
            }
    }
        }
                                         marca de tiempo, uid);
            notifyInterfaceClassActivity(etiqueta, !strcmp("activo", estado),
        si (estado)
        const char *uid = evt->findParam("UID");
        const char *marca de tiempo = evt->findParam("TIME_NS");
        const char *estado = evt->findParam("ESTADO");
        const char *label = evt->findParam("INTERFACE");
        } else if (!strcmp(subsys, "xt_idletimer")) {
        notifyStrictCleartext(uid, hexadecimal);
        const char *hex = evt->findParam("HEX");
        const char *uid = evt->findParam("UID");
        } else if (!strcmp(subsys, "estricto")) {
        notifyQuotaLimitReached(alertName, iface);
        const char *iface = evt->findParam("INTERFACE");
        const char *alertName = evt->findParam("ALERT_NAME");
            }
                notifyRouteChange(acción, ruta, puerta de enlace, iface);
            if (ruta && (puerta de enlace || iface)) {
            const char *iface = evt->findParam("INTERFACE");
            const char *gateway = evt->findParam("GATEWAY");
            const char *ruta = evt->findParam("RUTA");
                   acción == NetlinkEvent::Acción::kRouteRemoved) {
    
    
    

    Copiar código

    Supervise el estado de la red del kernel y vuelva a llamar:

    Copiar código

        private void listeningToSocket() lanza IOException {
            Conector LocalSocket = nulo;
    
            prueba {
                socket = nuevo LocalSocket();
                Dirección LocalSocketAddress = determinarSocketAddress();
    
                socket.connect(dirección);
    
                InputStream inputStream = socket.getInputStream();
                sincronizado (mDaemonLock) {
                    mOutputStream = socket.getOutputStream();
                }
                                    if (mCallbackHandler.sendMessage(msg)) {
                                            event.getCode(), uptimeMillisInt(), 0, event.getRawEvent());
                                    }
                                        liberaciónWl = verdadero;
                                        mWakeLock.acquire();
                                            && mWakeLock != nulo) {
                                    if (mCallbacks.onCheckHoldWakeLock(event.getCode())
                                    // TODO: migrar para enviar instancias de NativeDaemonEvent
                                if (event.isClassUnsolicited()) {
                                log("RCV <- {" + evento + "}");
                                        NativeDaemonEvent.parseRawEvent(rawEvent, fdList);
                                evento final NativeDaemonEvent =
                            prueba {
                            liberación booleanaWl = false;
                                    buffer, inicio, i - inicio, StandardCharsets.UTF_8);
                            cadena final rawEvent = nueva cadena (
                            // datos confidenciales
                            // Nota: no registre este mensaje sin formato ya que puede contener
                        if (búfer[i] == 0) {
                    para (int i = 0; i < count; i++) {
                    inicio = 0;
                    contar += inicio;
                    // Agrega nuestro punto de partida al conteo y restablece el inicio.
                    }
                        romper;
                        loge("obtuve " + recuento + " lectura con inicio = " + inicio);
                    si (cuenta < 0) {
                    int count = inputStream.read(buffer, inicio, BUFFER_SIZE - inicio);
                mientras (verdadero) {
                int inicio = 0;
                byte[] buffer = nuevo byte[BUFFER_SIZE];
                FileDescriptor[] fdList = nulo;
    
    
    
    
    
    
    
    
                                        liberaciónWl = falso;
                                    }
                }
                    loge("Error al cerrar el socket: " + ex);
                } captura (IOException ex) {
                    }
                        socket.close();
                    si (socket! = nulo) {
                prueba {
                    }
                        }
                            loge("Error al cerrar el flujo de salida: " + e);
                        } captura (IOException e) {
                            mOutputStream.close();
                            loge("cierre de flujo para " + mSocket);
                        prueba {
                    si (mOutputStream! = nulo) {
                sincronizado (mDaemonLock) {
            } finalmente {
                tirar ex;
                loge("Error de comunicaciones: " + ex);
                }
                    }
                        inicio = 0;
                    } más {
                        inicio = restante;
                        System.arraycopy(búfer, inicio, búfer, 0, restante);
                        int final restante = BUFFER_SIZE - inicio;
                    if (iniciar!= contar) {
                    // buffer y leer de nuevo.
                    // Deberíamos terminar en la cantidad que leímos. Si no, compacta entonces
                    }
                        log("RCV incompleto");
                        }
                            }
                                }
                                    mWakeLock.release();
                                si (liberarWl) {
                            } finalmente {
                                log("Mensaje de análisis de problema " + e);
                                }
                                    mResponseQueue.add(event.getCmdNumber(), evento);
    
    
    
    

    Copiar código

    Vuelva a llamar al método onEvent en NetworkManagementService.java y notifique a los observadores creados anteriormente.

    Copiar código

            @Anular
            onEvent público booleano (código int, cadena sin procesar, cadena [] cocida) {
                String errorMessage = String.format("Evento no válido del demonio (%s)", sin formato);
                cambiar (código) {
                caso NetdResponseCode.InterfaceChange:
                        /*
                        /*
                caso NetdResponseCode.InterfaceClassActivity:
                        // romper;
                        lanzar nueva IllegalStateException(errorMessage);
                        }
                            devolver verdadero;
                            notificarLimitReached(cocido[3],cocinado[4]);
                        }
                            lanzar nueva IllegalStateException(errorMessage);
                         */
                         * Formato: "alerta de límite NNN <alertName> <ifaceName>"
                        /*
                caso NetdResponseCode.BandwidthControl:
                        // romper;
                        }
                            devolver verdadero;
                            notifyInterfaceLinkStateChanged(cocido[3], cocido[4].equals("arriba"));
                        } else if (cocido[2].equals("estado de enlace") && cocinado.longitud == 5) {
                            devolver verdadero;
                            notifyInterfaceStatusChanged(cocido[3], cocido[4].equals("arriba"));
                        } else if (cocido[2].equals("cambiado") && cocinado.longitud == 5) {
                            devolver verdadero;
                            notifyInterfaceRemoved(cooked[3]);
                        } else if (cocido[2].equals("eliminado")) {
                            devolver verdadero;
                            notifyInterfaceAdded(cooked[3]);
                        }
                            lanzar nueva IllegalStateException(errorMessage);
                         */
                         * "NNN Estado del enlace de Iface <nombre> <arriba/abajo>"
                         * "NNN Iface cambió <nombre> <arriba/abajo>"
                         * "NNN Iface eliminado <nombre>"
                         * Formato: "NNN Iface agregó <nombre>"
                         * El estado de una clase de interfaz de red cambió (activo/inactivo)
                         * Formato: "NNN IfaceClass <activo/inactivo> <etiqueta>"
                         */
                        if (cooked.length < 4 || !cooked[1].equals("IfaceClass")) {
                            lanzar nueva IllegalStateException(errorMessage);
                        }
                        marca de tiempo largaNanos = 0;
                        int ProcessUid = -1;
                        if (longitud.cocinada >= 5) {
                            prueba {
                                marca de tiempoNanos = Long.parseLong(cocido[4]);
                                if (cocido.longitud == 6) {
                                    ProcessUid = Integer.parseInt(cocido[5]);
                                }
                        } catch(IllegalArgumentException e) { // Dirección IP mal formada/no válida.
                            lanzar una nueva IllegalStateException (errorMessage,mi);
                        } catch(NumberFormatException e) { // Duración o alcance no numérico.
                            dirección = nueva dirección de enlace (cocinada [3], banderas, alcance);
                            int alcance = Integer.parseInt(cocido[6]);
                            int banderas = Integer.parseInt(cocido[5]);
                        prueba {
                        Dirección de dirección de enlace;
                        }
                            lanzar nueva IllegalStateException(errorMessage);
                         */
                         * "Dirección NNN eliminada <addr> <iface> <banderas> <alcance>"
                         * Formato: "Dirección NNN actualizada <addr> <iface> <banderas> <alcance>"
                        /*
                caso NetdResponseCode.InterfaceAddressChange:
                        // romper;
                        devolver verdadero;
                                marca de tiempoNanos, ProcessUid, false);
                                : DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
                                está activo ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH
                        notifyInterfaceClassActivity(Integer.parseInt(cocido[3]),
                        booleano isActive = cocinado[2].equals("activo");
                        }
                            marca de tiempoNanos = SystemClock.elapsedRealtimeNanos();
                        } más {
    
                            lanzar nueva IllegalStateException(errorMessage, e);
                        }
                                } más {
                                    vía = cocinado[i+1];
                                    válido = falso; // Interfaz duplicada.
                                } más {
                                    dev = cocinado[i+1];
                                si (dev == nulo) {
                            if (cocido[i].equals("dev")) {
                        for (int i = 4; (i + 1) < longitud.cocinada && válido; i += 2) {
                        booleano válido = verdadero;
                        Cadena de desarrollo = nulo;
                        }
                            lanzar nueva IllegalStateException(errorMessage);
                         */
                         * Formato: "Ruta NNN <actualizado|eliminado> <dst> [a través de <gateway] [dev <iface>]"
                        /*
                caso NetdResponseCode.RouteChange:
                        // romper;
                        }
                            notifyInterfaceDnsServerInfo(cocido[3], duración, servidores);
                            }
                                lanzar nueva IllegalStateException(errorMessage);
                            } captura (NumberFormatException e) {
                                vida útil = Long.parseLong(cocido[4]);
                            prueba {
                            cocinado[2].equals("servidores")) {
                            cocinado[1].equals("DnsInfo") &&
                        if (cocido.longitud == 6 &&
                        larga vida útil; // En realidad, un entero sin signo de 32 bits.
                         */
                         * Formato: "NNN DnsInfo servidores <interfaz> <vida útil> <servidores>"
                        /*
                caso NetdResponseCode.InterfaceDnsServerInfo:
                        // romper;
                        devolver verdadero;
                        }
                            notifyAddressRemoved(iface, dirección);
                        } más {
                            notifyAddressUpdated(iface, dirección);
    
    
    
                                si (vía == null) {
                                    válido = falso; // Puerta de enlace duplicada.
                                }
            }
                }
                predeterminado: romper;
                    romper;
                    }
                    } captura (RemoteException ignorada) {
                        ActivityManager.getService().notifyCleartextNetwork(uid, primerpaquete);
                    prueba {
                    byte final[] primerPaquete = HexDump.hexStringToByteArray(cocido[2]);
                    final int uid = Integer.parseInt(cocido[1]);
                caso NetdResponseCode.StrictCleartext:
                        // romper;
                        }
                            } captura (IllegalArgumentException e) {}
                                devolver verdadero;
                                notifyRouteChange(cocido[2], ruta);
                                Ruta RouteInfo = nueva RouteInfo(new IpPrefix(cooked[3]), gateway, dev);
                                if (vía != null) gateway = InetAddress.parseNumericAddress(via);
                                Puerta de enlace InetAddress = nulo;
                                // InetAddress.parseNumericAddress(null) devuelve inexplicablemente ::1.
                            prueba {
                        }
                            }
                                válido = falso; // Sintaxis desconocida.

    Copiar código

3.Herramienta de prueba Netd ndc

El principio de ndc es en realidad conectarse a netd a través de un socket para interactuar. Esta parte se puede reflejar en el código fuente:

ndc.c

Copiar código

int principal(int argc, char **argv) {
    //argv[1]nombre del socket.
    if ((calcetín = socket_local_client(argv[1],
                                     ANDROID_SOCKET_NAMESPACE_RESERVED,
                                     SOCK_STREAM)) < 0) {
} 
    exit(do_cmd(sock, argc-cmdOffset, &(argv[cmdOffset])));
        }
            salida(4);
                                         SOCK_STREAM)) < 0) {
                                         ANDROID_SOCKET_NAMESPACE_RESERVED,
        if ((sock = socket_local_client("netd",

Copiar código

Copiar código

static int do_cmd(int sock, int argc, char **argv) {
   //Los parámetros del comando finalmente se envían al proceso del servicio netd a través del socket para su procesamiento
   if (escribir(calcetín, final_cmd, strlen(final_cmd) + 1) < 0) {
        int res = errno;
        perror("escribir");
        libre(final_cmd);
        devolver resolución;
    }

Copiar código

monitor:

Consulte la lista de comandos disponibles:

Copiar código

consola:/ # lista de interfaces ndc
110 0 ficticio0
110 0 eth0
110 0 ip6_vti0
110 0 ip6tnl0
110 0 ip_vti0
110 0 lo
200 0 Lista de interfaces completada

Copiar código

  Ejemplo: $ lista de interfaces ndc de shell de $ adb

 

interfaz lista
contador de lectura| contador de lectura
acelerar<iface><”rx|tx”>
establecer acelerador<iface><rx_kbps|tx_kbps>
controlador<iface><cmd><args>
ruta<agregar|eliminar> <iface> <”predeterminado|secundario”><dst> <prefijo> <puerta de enlace>
lista_ttys
ipfwd estado
habilitar|deshabilitar
atar estado
inicio-reversa|parada-reversa
detener<
inicio<addr_1 addr_2 addr_3 addr_4 [addr_2n]>
interfaz<añadir|eliminar|lista>
lista dns
dnsset <addr_1> < dirección_2>
nat <activar|desactivar><iface><extface><addrcnt><nated-ipaddr/prelength>
ppd adjuntar<tty> <addr_local> <add_remote> <dns_1><dns_2>
separar<tty>
suave inicio|parada
descargar descarga<iface> <AP|P2P>
clientela
estado
establecer<iface> <SSID> <wpa-psk|wpa2-psk|abierto> [<clave><canal> <preámbulo><SCB máximo>]
resolver setdefaultif<iface>
setifdns<iface><dns_1><dns_2>
rubor predeterminado si
fluxif<iface>
banda ancha habilitar|deshabilitar
eliminar cuota|rq
obtener cuota|gq
getquota|giq<iface>
setquota|sq<bytes> <iface>
eliminarquota|rqs<iface>
eliminariiquota|riq<iface>
setiquota|sq<interfaz><bytes>
addnaughtyapps|ana<appUid>
eliminarnaughtyapps|rna<appUid>
setgolbalalert|sga<bytes>
debugsettetherglobalalert|dstga<iface0><iface1>
setsharedalert|ssa<bytes>
eliminar alerta compartida|rsa
setinterfacealert|sia<iface><bytes>
eliminar alerta de interfaz|ria<iface>
gettetherstats|gts<iface0><iface1>
temporizador inactivo habilitar|deshabilitar
agregar|eliminar<iface><timeout><classLabel>
cortafuegos habilitar|deshabilitar|está_enabled
set_interface_rule<rmnet0><permitir|denegar>
set_egress_source_rule<ip_addr><allow|deny>
set_egress_dest_rule<ip_addr><port><allow|deny>
set_uid_rule<uid><permitir|denegar>
clatido detener|estado|iniciar<iface>
 

Supongo que te gusta

Origin blog.csdn.net/yangzex/article/details/132719655
Recomendado
Clasificación