Android Iptables 客制化方法及基本使用

news2024/11/24 7:38:27

Android Iptables 客制化方法及基本使用

    • Android netd 的自定义链
      • NetdConstants.cpp 的 execIptablesRestore 方法
      • IptablesRestoreController 的 execute 方法
      • 使用 oem-iptables-init.sh 添加自定义的防火墙规则
      • oem-iptables-init.sh 示例文件
    • 基本概念
      • Iptables 链
      • Iptables 表
    • 表、链规则检查顺序
    • 规则
      • 匹配条件
      • 处理动作
    • iptables 规则管理
      • 信息查询
      • 规则添加
      • 规则删除
      • 规则修改
      • 规则匹配
      • 自定义链
    • Iptables 保存规则
      • 命令
      • 示例
      • 导入规则
    • 参考

iptables 是一个在Linux内核集成的IP信息包过滤系统,用于控制IP信息包过滤和防火墙配置,通过存储在数据包过滤表中的规则,在不同的链中做出数据包过滤决定。
Android 是基于 Linux 的操作系统,支持 Iptables。执行 Iptables 命令需要 root 权限。

Android netd 的自定义链

以 INPUT 链为例子,Andorid 会自定义 bw_INPUT、fw_INPUT 等链,链到 INPUT 链上

# iptables -L 

Chain INPUT (policy ACCEPT)
target     prot opt source               destination                  
bw_INPUT   all  --  anywhere             anywhere            
fw_INPUT   all  --  anywhere             anywhere    

Controllers 执行 init() 方法时会先执行 initChildChains 方法, 在 INPUT 链下创造子链

void Controllers::initChildChains() {
    /*
     * This is the only time we touch top-level chains in iptables; controllers
     * should only mutate rules inside of their children chains, as created by
     * the constants above.
     *
     * Modules should never ACCEPT packets (except in well-justified cases);
     * they should instead defer to any remaining modules using RETURN, or
     * otherwise DROP/REJECT.
     */

    // Create chains for child modules.
    createChildChains(V4V6, "filter", "INPUT", FILTER_INPUT, true);
    createChildChains(V4V6, "filter", "FORWARD", FILTER_FORWARD, true);
    createChildChains(V4V6, "raw", "PREROUTING", RAW_PREROUTING, true);
    createChildChains(V4V6, "mangle", "FORWARD", MANGLE_FORWARD, true);
    createChildChains(V4V6, "mangle", "INPUT", MANGLE_INPUT, true);
    createChildChains(V4, "nat", "PREROUTING", NAT_PREROUTING, true);
    createChildChains(V4, "nat", "POSTROUTING", NAT_POSTROUTING, true);

    createChildChains(V4, "filter", "OUTPUT", FILTER_OUTPUT, false);
    createChildChains(V6, "filter", "OUTPUT", FILTER_OUTPUT, false);
    createChildChains(V4, "mangle", "POSTROUTING", MANGLE_POSTROUTING, false);
    createChildChains(V6, "mangle", "POSTROUTING", MANGLE_POSTROUTING, false);
}

FILTER_INPUT 是子链的集合,定义顺序,会从上往下添加,先添加的子链具有较高的优先级,以 FILTER_INPUT 为例,FILTER_INPUT 定义为:

/**
 * List of module chains to be created, along with explicit ordering. ORDERING
 * IS CRITICAL, AND SHOULD BE TRIPLE-CHECKED WITH EACH CHANGE.
 */
static const std::vector<const char*> FILTER_INPUT = {
        // Bandwidth should always be early in input chain, to make sure we
        // correctly count incoming traffic against data plan.
        BandwidthController::LOCAL_INPUT,
        FirewallController::LOCAL_INPUT,
};

createChildChains 方法定义如下

void Controllers::createChildChains(IptablesTarget target, const char* table,
                                    const char* parentChain,
                                    const std::vector<const char*>& childChains,
                                    bool exclusive) {
    std::string command = StringPrintf("*%s\n", table);

    // We cannot just clear all the chains we create because vendor code modifies filter OUTPUT and
    // mangle POSTROUTING directly. So:
    //
    // - If we're the exclusive owner of this chain, simply clear it entirely.
    // - If not, then list the chain's current contents to ensure that if we restart after a crash,
    //   we leave the existing rules alone in the positions they currently occupy. This is faster
    //   than blindly deleting our rules and recreating them, because deleting a rule that doesn't
    //   exists causes iptables-restore to quit, which takes ~30ms per delete. It's also more
    //   correct, because if we delete rules and re-add them, they'll be in the wrong position with
    //   regards to the vendor rules.
    //
    // TODO: Make all chains exclusive once vendor code uses the oem_* rules.
    std::set<std::string> existingChildChains;
    if (exclusive) {
        // Just running ":chain -" flushes user-defined chains, but not built-in chains like INPUT.
        // Since at this point we don't know if parentChain is a built-in chain, do both.
        StringAppendF(&command, ":%s -\n", parentChain);
        StringAppendF(&command, "-F %s\n", parentChain);
    } else {
        existingChildChains = findExistingChildChains(target, table, parentChain);
    }

    for (const auto& childChain : childChains) {
        // Always clear the child chain.
        StringAppendF(&command, ":%s -\n", childChain);
        // But only add it to the parent chain if it's not already there.
        if (existingChildChains.find(childChain) == existingChildChains.end()) {
            StringAppendF(&command, CHILD_CHAIN_TEMPLATE, parentChain, childChain);
        }
    }
    command += "COMMIT\n";
    execIptablesRestore(target, command);
}

该方法的核心思路就是拼 command 字符串,语法与 iptables-save 生成文件的语法类似,最后调用 execIptablesRestore 执行 command,向 iptables 发送命令
此处生成的 command 为

04-03 15:31:55.872  1106  1106 E Netd    :  *filter
04-03 15:31:55.872  1106  1106 E Netd    : :INPUT -
04-03 15:31:55.872  1106  1106 E Netd    : -F INPUT
04-03 15:31:55.872  1106  1106 E Netd    : :bw_INPUT -
04-03 15:31:55.872  1106  1106 E Netd    : -A INPUT -j bw_INPUT
04-03 15:31:55.872  1106  1106 E Netd    : :fw_INPUT -
04-03 15:31:55.872  1106  1106 E Netd    : -A INPUT -j fw_INPUT
04-03 15:31:55.872  1106  1106 E Netd    : COMMIT

NetdConstants.cpp 的 execIptablesRestore 方法

execIptablesRestore 方法的实现定义在 NetdConstants.cpp

int execIptablesRestoreWithOutput(IptablesTarget target, const std::string& commands,
                                  std::string *output) {
    return android::net::gCtls->iptablesRestoreCtrl.execute(target, commands, output);
}

int execIptablesRestore(IptablesTarget target, const std::string& commands) {
    return execIptablesRestoreWithOutput(target, commands, nullptr);
}

本质上是调用 IptablesRestoreController 的 execute 方法

IptablesRestoreController 的 execute 方法

int IptablesRestoreController::execute(const IptablesTarget target, const std::string& command,
                                       std::string *output) {
    std::lock_guard lock(mLock);

    std::string buffer;
    if (output == nullptr) {
        output = &buffer;
    } else {
        output->clear();
    }

    int res = 0;
    if (target == V4 || target == V4V6) {
        res |= sendCommand(IPTABLES_PROCESS, command, output);
    }
    if (target == V6 || target == V4V6) {
        res |= sendCommand(IP6TABLES_PROCESS, command, output);
    }
    return res;
}

sendCommand 方法定义如下:

int IptablesRestoreController::sendCommand(const IptablesProcessType type,
                                           const std::string& command,
                                           std::string *output) {
   std::unique_ptr<IptablesProcess> *process =
           (type == IPTABLES_PROCESS) ? &mIpRestore : &mIp6Restore;


    // We might need to fork a new process if we haven't forked one yet, or
    // if the forked process terminated.
    //
    // NOTE: For a given command, this is the last point at which we try to
    // recover from a child death. If the child dies at some later point during
    // the execution of this method, we will receive an EPIPE and return an
    // error. The command will then need to be retried at a higher level.
    IptablesProcess *existingProcess = process->get();
    if (existingProcess != nullptr && !existingProcess->outputReady()) {
        existingProcess->stop();
        existingProcess = nullptr;
    }

    if (existingProcess == nullptr) {
        // Fork a new iptables[6]-restore process.
        IptablesProcess *newProcess = IptablesRestoreController::forkAndExec(type);
        if (newProcess == nullptr) {
            LOG(ERROR) << "Unable to fork ip[6]tables-restore, type: " << type;
            return -1;
        }

        process->reset(newProcess);
    }

    if (!android::base::WriteFully((*process)->stdIn, command.data(), command.length())) {
        ALOGE("Unable to send command: %s", strerror(errno));
        return -1;
    }

    if (!android::base::WriteFully((*process)->stdIn, PING, PING_SIZE)) {
        ALOGE("Unable to send ping command: %s", strerror(errno));
        return -1;
    }

    if (!drainAndWaitForAck(*process, command, output)) {
        // drainAndWaitForAck has already logged an error.
        return -1;
    }

    return 0;
}

这里 process 其实就是 /system/bin/iptables-restore 的进程,WriteFully 方法所在位置实际就是把 command 数据重定向输入到 /system/bin/iptables-restore ,与下面命令等价

iptables-restore < command

使用 oem-iptables-init.sh 添加自定义的防火墙规则

/system/netd/server/oem_iptables_hook.cpp
在这里插入图片描述

/system/netd/server/NetdConstants.cpp
在这里插入图片描述
在 Controllers::initIptablesRules 先通过 initChildChains() 初始化 INPUT OUTPUT 等链,再通过 setupOemIptablesHook 加载 ODM 规则,随后在加载系统防火墙规则

void Controllers::initIptablesRules() {
    Stopwatch s;
    initChildChains(); // 初始化 INPUT OUTPUT 等链
    ALOGI("Creating child chains: %.1fms", s.getTimeAndReset());

    // Let each module setup their child chains
    setupOemIptablesHook(); // 加载 OEM 规则
    ALOGI("Setting up OEM hooks: %.1fms", s.getTimeAndReset());

    /* When enabled, DROPs all packets except those matching rules. */
    firewallCtrl.setupIptablesHooks(); // 加载防火墙规则
    ALOGI("Setting up FirewallController hooks: %.1fms", s.getTimeAndReset());

    /* Does DROPs in FORWARD by default */
    tetherCtrl.setupIptablesHooks();
    ALOGI("Setting up TetherController hooks: %.1fms", s.getTimeAndReset());

    /*
     * Does REJECT in INPUT, OUTPUT. Does counting also.
     * No DROP/REJECT allowed later in netfilter-flow hook order.
     */
    bandwidthCtrl.setupIptablesHooks();
    ALOGI("Setting up BandwidthController hooks: %.1fms", s.getTimeAndReset());

    /*
     * Counts in nat: PREROUTING, POSTROUTING.
     * No DROP/REJECT allowed later in netfilter-flow hook order.
     */
    idletimerCtrl.setupIptablesHooks();
    ALOGI("Setting up IdletimerController hooks: %.1fms", s.getTimeAndReset());

    /*
     * Add rules for detecting IPv6/IPv4 TCP/UDP connections with TLS/DTLS header
     */
    strictCtrl.setupIptablesHooks();
    ALOGI("Setting up StrictController hooks: %.1fms", s.getTimeAndReset());
}

oem-iptables-init.sh 示例文件

#!/bin/bash
/system/bin/iptables -A INPUT -p tcp --destination-port 5555 -s 127.0.0.1 -j ACCEPT
/system/bin/iptables -A INPUT -p tcp --destination-port 5555 -j DROP

基本概念

Iptables 链

  • INPUT:处理入站数据包
  • OUTPUT:处理出站数据包
  • FORWARD:处理转发数据包
  • POSTROUTING:在进行路由选择后处理数据包(对数据链进行源地址修改转换)
  • PREROUTING:在进行路由选择前处理数据包(做目标地址转换)

Iptables 表

  1. raw表:确定是否对该数据包进行状态跟踪以及处理异常,表内包含两个链:OUTPUT、PREROUTING
  2. mangle表:为数据包的 TOS(服务类型)、TTL(生命周期)值,或者为数据包设置 Mark 标记,以实现流量整形、策略路由等高级应用。其对应 iptable_mangle,表内包含五个链:PREROUTING、POSTROUTING、INPUT、OUTPUT、FORWARD
  3. nat表:修改数据包中的源、目标IP地址或端口;其对应的模块为 iptable_nat,表内包括三个链:PREROUTING、POSTROUTING、OUTPUT(centos7中还有 INPUT,centos6 中没有)
  4. filter表:确定是否放行该数据包(过滤);其对应的内核模块为 iptable_filter,表内包含三个链:INPUT、FORWARD、OUTPUT。
    在这里插入图片描述

表、链规则检查顺序

在这里插入图片描述
按顺序依次检查,匹配即停止(LOG策略例外),若找不到相匹配的规则,则按该链的默认策略处理

规则

匹配条件

  1. 源地址 Source IP,目标地址 Destination IP
  2. 源端口 Source Port, 目标端口 Destination Port
  3. …等等

处理动作

处理动作在 iptables 中被称为 target,此处列出一些常用的动作:
ACCEPT:允许数据包通过
DROP:直接丢弃数据包,不给任何回应信息,客户端不会收到任何回应
REJECT:拒绝数据包通过,必要时会给数据发送端一个响应的信息,客户端刚请求就会收到拒绝的信息
SNAT:源地址转换,解决内网用户用同一个公网地址上网的问题
MASQUERADE:是 SNAT 的一种特殊形式,适用于动态的、临时会变的 ip 上
DNAT:目标地址转换
REDIRECT:在本机做端口映射

iptables 规则管理

信息查询

# filter负责过滤功能,比如允许哪些IP地址访问,拒绝哪些IP地址访问,允许访问哪些端口,禁止访问哪些端口
# filter表会根据我们定义的规则进行过滤,filter表应该是我们最常用到的表了
# 下面两种都可以
# 默认不加-t就是指的filter表
iptables -t filter --list
iptables -t raw -L
iptables -t mangle -L
iptables -t nat -L

# -v是显示详细的信息,列出INPUT链的详细信息
iptables -vL INPUT
# 不让IP进行反解
iptables -nvL INPUT
# 显示规则的序号,--line-numbers选项表示显示规则的序号,注意,此选项为长选项,不能与其他短选项合并,不过此选项可以简写为--line
iptables --line-numbers -t 表名 -L
# 表示查看表中的所有规则,并且显示更详细的信息(-v选项),不过,计数器中的信息显示为精确的计数值,而不是显示为经过可读优化的计数值,-x选项表示显示计数器的精确值
iptables -t 表名 -v -x -L
# 可以合起来,不过-L在最后
iptables --line -t filter -nvxL INPUT

# -----------------------显示界面解释-----------------------
# Chain INPUT (policy ACCEPT 170M packets, 33G bytes)
# policy表示当前链的默认策略,policy ACCEPT表示INPUT的链的默认动作为ACCEPT,换句话说就是,默认接受通过INPUT关卡的所有请求,所以我们在配置INPUT链的具体规则时,应该将需要拒绝的请求配置到规则中
# 说白了就是”黑名单”机制,默认所有人都能通过,只有指定的人不能通过,当我们把INPUT链默认动作设置为接受(ACCEPT),就表示所有人都能通过这个关卡,此时就应该在具体的规则中指定需要拒绝的请求,就表示只有指定的人不能通过这个关卡,这就是黑名单机制
# packets表示当前链(上例为INPUT链)默认策略匹配到的包的数量,0 packets表示默认策略匹配到0个包。
# bytes表示当前链默认策略匹配到的所有包的大小总和。
# 其实,我们可以把packets与bytes称作”计数器”,上图中的计数器记录了默认策略匹配到的报文数量与总大小,”计数器”只会在使用-v选项时,才会显示出来

# pkts:对应规则匹配到的报文的个数。
# bytes:对应匹配到的报文包的大小总和。
# target:规则对应的target,往往表示规则对应的”动作”,即规则匹配成功后需要采取的措施。
# prot:表示规则对应的协议,是否只针对某些协议应用此规则。
# opt:表示规则对应的选项。
# in:表示数据包由哪个接口(网卡)流入,即从哪个网卡来。
# out:表示数据包将由哪个接口(网卡)流出,即到哪个网卡去。
# source:表示规则对应的源头地址,可以是一个IP,也可以是一个网段。
# destination:表示规则对应的目标地址。可以是一个IP,也可以是一个网段

规则添加

注意:添加规则时,规则的顺序非常重要,哪个先匹配就执行哪个,后面就算有一模一样的也不会执行

# 在指定表的指定链的尾部添加一条规则,-A选项表示在对应链的末尾添加规则,省略-t选项时,表示默认操作filter表中的规则
iptables -t 表名 -A 链名 匹配条件 -j 动作
# 举例,表示丢弃来自192.168.1.146的数据包
# 使用-s选项,指明”匹配条件”中的”源地址”,即如果报文的源地址属于-s对应的地址,那么报文则满足匹配条件,-s为source之意,表示源地址
iptables -t filter -A INPUT -s 192.168.1.146 -j DROP

# 在指定表的指定链的首部添加一条规则,-I选型表示在对应链的开头添加规则
iptables -t 表名 -I 链名 匹配条件 -j 动作
# 举例
iptables -t filter -I INPUT -s 192.168.1.146 -j ACCEPT

# 在指定表的指定链的指定位置添加一条规则
iptables -t 表名 -I 链名 规则序号 匹配条件 -j 动作
iptables -t filter -I INPUT 5 -s 192.168.1.146 -j REJECT

# 设置指定表的指定链的默认策略(默认动作),并非添加规则。
iptables -t 表名 -P 链名 动作
# 表示将filter表中FORWARD链的默认策略设置为ACCEPT
iptables -t filter -P FORWARD ACCEPT

规则删除

# 按照规则序号删除规则,删除指定表的指定链的指定规则,-D选项表示删除对应链中的规则
iptables -nvL --line-numbers
iptables -t 表名 -D 链名 规则序号
# 表示删除filter表中INPUT链中序号为3的规则
iptables -t filter -D INPUT 3

# 按照具体的匹配条件与动作删除规则,删除指定表的指定链的指定规则
iptables -t 表名 -D 链名 匹配条件 -j 动作
# 表示删除filter表中INPUT链中源地址为192.168.1.146并且动作为DROP的规则
iptables -t filter -D INPUT -s 192.168.1.146 -j DROP

# 删除指定表的指定链中的所有规则,-F选项表示清空对应链中的规则,执行时需三思
iptables -t 表名 -F 链名
iptables -t filter -F INPUT

# 删除指定表中的所有规则,执行时需三思
iptables -t 表名 -F
iptables -t filter -F

规则修改

# 修改指定表中指定链的指定规则,-R选项表示修改对应链中的规则,使用-R选项时要同时指定对应的链以及规则对应的序号,并且规则中原本的匹配条件不可省略
iptables -t 表名 -R 链名 规则序号 规则原本的匹配条件 -j 动作
# 修改filter表中INPUT链的第3条规则,将这条规则的动作修改为ACCEPT, -s 192.168.1.146为这条规则中原本的匹配条件,如果省略此匹配条件,修改后的规则中的源地址可能会变为0.0.0.0/0
iptables -t filter -R INPUT 3 -s 192.168.1.146 -j ACCEPT
# 其他修改规则的方法:先通过编号删除规则,再在原编号位置添加一条规则

# 修改指定表的指定链的默认策略(默认动作),并非修改规则,可以使用如下命令
iptables -t 表名 -P 链名 动作
# 将filter表中FORWARD链的默认策略修改为ACCEPT
iptables -t filter -P FORWARD ACCEPT

规则匹配

当规则中同时存在多个匹配条件时,多个条件之间默认存在"与"的关系,即报文必须同时满足所有条件,才能被规则匹配

# --------------------匹配条件:目标IP地址
# -s用于匹配报文的源地址,可以同时指定多个源地址,每个IP之间用逗号隔开,也可以指定为一个网段
# 逗号两侧均不能包含空格,多个IP之间必须与逗号相连
iptables -t filter -I INPUT -s 192.168.1.111,192.168.1.118 -j DROP
iptables -t filter -I INPUT -s 192.168.1.0/24 -j ACCEPT
# 只要发往本机的报文的源地址不是192.168.1.146,就接受报文
iptables -t filter -I INPUT ! -s 192.168.1.0/24 -j ACCEPT

# -d用于匹配报文的目标地址,可以同时指定多个目标地址,每个IP之间用逗号隔开,也可以指定为一个网段
# 所有IP发送往111,118的报文都将被丢弃
iptables -t filter -I OUTPUT -d 192.168.1.111,192.168.1.118 -j DROP
iptables -t filter -I INPUT -d 192.168.1.0/24 -j ACCEPT
# 不管是-s选项还是-d选项,取反操作与同时指定多个IP的操作不能同时使用
iptables -t filter -I INPUT ! -d 192.168.1.0/24 -j ACCEPT

# -------------------匹配条件:协议类型
# -p用于匹配报文的协议类型,可以匹配的协议类型tcp、udp、udplite、icmp、esp、ah、sctp等(centos7中还支持icmpv6、mh)
iptables -t filter -I INPUT -p tcp -s 192.168.1.146 -j ACCEPT
iptables -t filter -I INPUT ! -p udp -s 192.168.1.146 -j ACCEPT

# ---------------匹配条件:网卡接口
# -i用于匹配报文是从哪个网卡接口流入本机的,由于匹配条件只是用于匹配报文流入的网卡
# 所以在OUTPUT链与POSTROUTING链中不能使用此选项
# 拒绝由网卡eth4流入的ping请求报文
iptables -t filter -I INPUT -p icmp -i eth4 -j DROP
iptables -t filter -I INPUT -p icmp ! -i eth4 -j DROP

# -o用于匹配报文将要从哪个网卡接口流出本机,于匹配条件只是用于匹配报文流出的网卡,所以在INPUT链与PREROUTING链中不能使用此选项。
iptables -t filter -I OUTPUT -p icmp -o eth4 -j DROP
iptables -t filter -I OUTPUT -p icmp ! -o eth4 -j DROP

自定义链

# 创建自定义链
#示例:在filter表中创建IN_WEB自定义链,省略-t选项时,缺省操作的就是filter表
iptables -t filter -N IN_WEB
# 可以看到,这条自定义链的引用计数为0 (0 references),就是说,这条自定义链还没有被任何默认链所引用
iptables -nvL

# 自定义链中配置规则,和其他一样
iptables -t filter -I IN_WEB -s 192.168.1.139 -j REJECT
iptables -I IN_WEB -s 192.168.1.188 -j REJECT
iptables -t filter --line -nvL IN_WEB


# 引用自定义链
#示例:在INPUT链中引用刚才创建的自定义链
iptables -t filter -I INPUT -p tcp --dport 80 -j IN_WEB

# 重命名自定义链
#示例:将IN_WEB自定义链重命名为WEB
iptables -E IN_WEB WEB

# 删除自定义链
# 删除自定义链需要满足两个条件:自定义链没有被引用;自定义链中没有任何规则
#示例:删除引用计数为0并且不包含任何规则的WEB链
iptables -D INPUT 1
iptables -t filter -F WEB
# 使用”-X”选项可以删除一个引用计数为0的、空的自定义链
iptables -X WEB

Iptables 保存规则

命令

iptables-save [-M modprobe] [-c] [-t table] [-f filename]

-M, --modprobe <modprobe_program>
        指定 modprobe 程序的路径。默认情况下,iptables-save 将检查 /proc/sys/kernel/modprobe 以确定可执行文件的路径。
-f, --file <filename>
        指定要记录输出到的文件名。如果没有指定,将输出到 STDOUT。
-c, --counters
        在输出中包含所有数据包和字节计数器的当前值。
-t, --table <tablename>
        将输出限制为一个表。如果内核配置了自动模块加载,如果该表不存在,则会尝试为该表加载相应的模块。

        如果未指定,输出所有可用的表。

示例

iptables-save  -t filter > filter.bak

储存结果

iptables-save -c
# Generated by iptables-save v1.4.21 on Sat Dec 17 17:05:12 2022
*filter
:INPUT ACCEPT [8397115:1774409253]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [8446458:1291918085]
COMMIT
# Completed on Sat Dec 17 17:05:12 2022

其中:

“#”号开头的表示注释;
“*filter”表示所在的表;
“:链名默认策略”表示相应的链及默认策略,具体的规则部分省略了命令名“iptables”;
在末尾处“COMMIT”表示提交前面的规则设置。

导入规则

iptables-restore 命令

iptables-restore < 文件名称

参考

https://blog.csdn.net/lemon_TT/article/details/130018867

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1680664.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenHarmony 实战开发——使用分布式菜单创建点餐神器

随着社会的进步与发展&#xff0c;科技手段的推陈出新&#xff0c;餐饮行业也在寻求新的突破与变革&#xff0c;手机扫描二维码点餐系统已经成为餐饮行业的未来趋势&#xff0c;发展空间巨大&#xff1b;扫码点餐&#xff0c;是“互联网餐饮”潮流的产物&#xff0c;可以有效地…

k8s-Helm包管理器

这里写目录标题 什么是Helmhelm架构重要概念组件Helm 客户端Helm 库 安装heml使用halm快速部署应用添加 helm 仓库 Helm 的常用命令chart 详解目录结构Redis chart 实践创建StorageClass制备器&#xff0c;配置NFS动态制备修改 helm 源搜索 redis chart修改配置安装查看安装情况…

力扣HOT100 - 152. 乘积最大子数组

解题思路&#xff1a; 方法一&#xff1a;暴力 class Solution {public int maxProduct(int[] nums) {int max Integer.MIN_VALUE;int s 1;for (int i 0; i < nums.length; i) {s 1;for (int j i ; j < nums.length; j) {s * nums[j];max Math.max(max, s);}}ret…

服务器之间实现免密码传输文件(scp免密传输)

问题&#xff1a;需要定时将本服务器的文件传输到指定服务器上作为备份 通过scp实现不同服务器之间的文件传输 正常使用scp传输文件 传输文件命令&#xff1a;scp /data/文件 root服务器地址&#xff1a;/指定目录 传输文件夹命令&#xff1a;scp -r /data/文件 root服务…

【回溯】1255. 得分最高的单词集合

本文涉及知识点 回溯 力扣难道&#xff1a;1881 LeetCode1255. 得分最高的单词集合 你将会得到一份单词表 words&#xff0c;一个字母表 letters &#xff08;可能会有重复字母&#xff09;&#xff0c;以及每个字母对应的得分情况表 score。 请你帮忙计算玩家在单词拼写游戏…

Google I/O盛会省流全内容总结:AI模型家族革新与前沿技术应用的广阔前景

昨晚的Google I/O 发布会主要聚焦于AI模型和应用的更新与扩展&#xff0c;特别在生成模型领域取得了显著进步。以下是模型层面内容的简要总结&#xff1a; DeepMind官网链接&#xff1a;https://deepmind.google/ Google Veo官方&#xff1a;https://deepmind.google/technol…

小程序框架是智能融媒体平台构建的最佳线路

过去5年&#xff0c;媒体行业一直都在进行着信息化建设向融媒体平台建设的转变。一些融媒体的建设演变总结如下&#xff1a; 新闻终端的端侧内容矩阵建设&#xff0c;如App新闻端&#xff0c;社交平台上的官方媒体等新闻本地生活双旗舰客户端&#xff0c;兼顾主流媒体核心宣传…

幻兽帕鲁Palworld服务器手动+docker部署方法+备份迁移

目录 帕鲁部署官方文档帕鲁手动安装法手动安装steamcmd通过steamcmd安装帕鲁后端 docker容器一键部署幻兽帕鲁绿联云NAS机器部署幻兽帕鲁客户端连接附录1&#xff1a;PalServer.sh的启动项附录2&#xff1a;配置文件游戏存档保存和迁移 关于阿里云计算巢 帕鲁部署官方文档 htt…

监控 Apache Web 服务器性能指标

Apache Web 服务器以其可靠性、灵活性和强大的功能而闻名&#xff0c;几十年来一直是互联网的支柱&#xff0c;从小型个人博客到大型电子商务平台&#xff0c;Apache 的多功能性使其能够轻松处理各种 Web 应用程序。 Apache 的 Web 服务器是如何工作的 尽管 Web 服务器涉及复…

Audio Hijack for Mac 激活版:音频录制与处理软件

Audio Hijack for Mac&#xff0c;让您的音频创作更加高效、便捷。它支持多种音频格式的录制和导出&#xff0c;包括MP3、AAC、WAV等&#xff0c;让您的音频作品具有更广泛的兼容性。同时&#xff0c;软件界面简洁明了&#xff0c;操作流畅自然&#xff0c;即使您是初学者也能快…

5.2 操作系统安装必备知识

目前操作系统安装方式接近于全自动化&#xff0c;用户无需做过多操作就能完成操作系统安装。但是操作系统安装也有其复杂的一面&#xff0c;例如固件及分区表的不同就会导致操作系统安装失败。本节主要介绍系统安装的一些必备知识。 5.2.1 BIOS 概述 BIOS(Basic Input/Output …

【运维自动化-配置平台】如何自动应用主机属性

主要用于配置主机属性的自动应用。当主机发生模块转移或模块新加入主机时&#xff0c;会根据目标模块配置的策略自动触发修改主机属性&#xff0c;比如主机负责人、主机状态。主机属性自动应用顾名思义是应用到主机上&#xff0c;而主机是必须在模块下的&#xff0c;所以有两种…

武汉星起航:中国卖家借力亚马逊跨境电商平台,拓展全球销售市场

随着互联网技术的飞速发展&#xff0c;跨境电商已成为连接全球消费者与卖家的重要桥梁。作为全球领先的跨境电商平台&#xff0c;亚马逊凭借其强大的品牌影响力、丰富的商品资源和高效的物流体系&#xff0c;为全球消费者提供了一个便捷、安全的购物环境。在这个平台上&#xf…

Google Chrome 设备工具栏原理

1.不同预览模式 2.计算出缩放比 3.固定滚动偏移 关键代码&#xff1a; overview&#xff1a; ratioW getChildRect().width / getParentRect().width ratioH getChildRect().height / getParentRect().height maxRatio max(ratioW, ratioH) if(maxRatio < 1) return 1 …

【UE5.1 角色练习】02-添加慢走、快速跑、蹲伏功能

目录 前言 步骤 一、慢走 二、快速跑 三、蹲伏 前言 在上一篇文章基础上&#xff08;【UE5.1 角色练习】01-使用小白人蓝图控制商城角色移动&#xff09;继续实现角色的慢走、快速跑以及蹲伏功能 步骤 一、慢走 1. 打开项目设置&#xff0c;添加一个操作映射&#x…

六西格玛管理培训对企业有哪些实际帮助?

当下&#xff0c;企业要想脱颖而出&#xff0c;不仅要有创新思维和敏锐的市场洞察力&#xff0c;更要有高效的管理体系和严谨的质量控制手段。而六西格玛管理培训正是这样一项能够帮助企业实现提质增效、提升竞争力的关键举措。那么&#xff0c;六西格玛管理培训对企业究竟有哪…

邦注科技 工业冷水机的风冷和水冷的区别介绍

工业冷水机在工业生产中扮演着重要角色&#xff0c;特别是在需要精确控制温度的应用中。风冷式冷水机和水冷式冷水机是两种常见的类型&#xff0c;它们之间存在一些显著的区别。 热交换的来源不同&#xff1a; 风冷式冷水机&#xff1a;热交换的来源是气体。它采用空气冷却方…

计算机网络 -- 序列化与反序列化

一 协议的重要性 我们都知道&#xff0c;在进行网络通信的过程中&#xff0c;通信的双方可以是不同的设备&#xff0c;不同的平台&#xff0c;不同的平台&#xff0c;比如说&#xff0c;手机用户和电脑用户进行通信&#xff0c;ios系统和安卓系统进行通信。 自己的数据&#xf…

从RTTR谈Reflection机制

虽然C11引入了RTTI、Metaprogramming 等技术&#xff0c;但C在Reflection编程方面依旧功能有限。在社区上&#xff0c;RTTR则提供了一套C编写的反射库&#xff0c;补充了C在Reflection方面的缺陷。 零、环境 操作系统Windows 11Visual StudioVisual Studio Community 2022 CMa…

Broad Learning System (BLS) 宽度学习系统

宽度学习&#xff08;Broad Learning System, BLS&#xff09;是一种有效的神经网络学习框架&#xff0c;旨在通过扩展网络的宽度而不是深度来提高学习能力和效率。与传统的深度学习相比&#xff0c;宽度学习通过堆叠多层特征节点和增强节点来构建网络&#xff0c;从而避免了深…