news 2026/4/23 15:20:55

VPP中ACL实战配置指南与VPP的API使用初探

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
VPP中ACL实战配置指南与VPP的API使用初探

重要提示:路径命名规范

⚠️ 强烈建议:所有文件路径和目录名使用纯ASCII字符(英文字母、数字、下划线、连字符),不要使用中文或其他非ASCII字符!

原因

  • VPP的CLI API(cli_inband)只支持ASCII编码的字符串
  • 包含非ASCII字符的路径会导致UnicodeEncodeError: 'ascii' codec can't encode characters
  • 虽然可以通过手动执行CLI命令绕过,但自动化脚本会失败

正确示例

  • ✅ ./vpp/learning/acl_rules/www/eno1np0`
  • learning/acl_rules/vpp_acl_setup.py

错误示例

  • ❌ ./vpp/learning/acl规则/www/eno1np0` (包含中文字符)
  • learning/ACL配置/scripts/(包含中文字符)

前言

本文详细介绍如何在VMware虚拟机环境中配置VPP的ACL(访问控制列表)插件,实现基于IP地址和端口的访问控制。通过实际的操作步骤和原理说明,帮助读者理解ACL的工作机制和配置方法。

实验目标

  • 配置两个网卡:
    • eno1np0:VPP虚拟机主网卡,IP地址 10.0.0.213,采用主机模式
    • eno2np1:Linux测试机器网卡,IP地址 10.0.0.100,采用主机模式(充当其他主机)
  • 在VMware配置主机模式,网段为:10.0.0.0/24
  • Windows主机VMware网卡IP:10.0.0.1(默认)
  • ACL规则配置:
    • 10.0.0.1(Windows主机) 可以HTTP访问 10.0.0.213:80
    • 其他地址不能HTTP访问 10.0.0.213:80
    • 其他地址可以ICMP访问 10.0.0.213
    • 10.0.0.1(Windows主机) 不能ICMP访问 10.0.0.213
  • 启动HTTP静态页面服务
  • 通过Windows主机(10.0.0.1)和Linux测试机器(10.0.0.100)测试验证ACL规则是否生效

一、环境准备

1.1 VMware网络配置

网口1:eno1np0(VPP虚拟机主网卡)
配置: - 模式:主机模式(Host-Only) - 网段:10.0.0.0/24 - IP地址:10.0.0.213/24 - 目的:VPP虚拟机的主网卡,用于提供HTTP服务
网口2:eno2np1(Linux测试机器网卡)
配置: - 模式:主机模式(Host-Only) - 网段:10.0.0.0/24 - IP地址:10.0.0.100/24 - 目的:Linux测试机器网卡,充当"其他主机"用于测试ACL规则

网络拓扑

┌─────────────────────────────────────────────────────────┐ │ VMware主机模式网络 10.0.0.0/24 │ │ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ Windows主机 │ │ VPP虚拟机 │ │ │ │ 10.0.0.1 │◄───────►│ │ │ │ │(VMware网卡) │ │ eno1np0: │ │ │ └──────────────┘ │ 10.0.0.213 │ │ │ └──────────────┘ │ │ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ Linux测试机 │ │ VPP虚拟机 │ │ │ │ 10.0.0.100 │◄───────►│ │ │ │ │(eno2np1) │ │ eno1np0: │ │ │ └──────────────┘ │ 10.0.0.213 │ │ │ └──────────────┘ │ └──────────────────────────────────────────────────────────┘

vmware开启一个主机模式的虚拟网络

开启两个网卡

1.2 Windows主机网络配置

设置Windows主机的VMware网卡(VMnet)的IP地址:10.0.0.1(手动配置)

配置步骤

  1. 打开"网络和共享中心" → “更改适配器设置”
  2. 找到VMware Virtual Ethernet Adapter(VMnet)
  3. 右键 → “属性” → “Internet协议版本4(TCP/IPv4)”
  4. 选择"使用下面的IP地址":
    • IP地址:10.0.0.1
    • 子网掩码:255.255.255.0
    • 默认网关:留空
  5. 点击"确定"保存

1.3 Linux测试机器网卡配置

在Linux虚拟机中配置第二个网卡(eno2np1)的IP地址:

# 配置eno2np1的IP地址(不交给VPP管理,由Linux内核管理)sudoipaddradd10.0.0.100/24 dev eno2np1sudoiplinkseteno2np1 up# 验证配置ipaddr show eno2np1

注意:eno2np1不需要在vpp1.conf中配置,它由Linux内核管理,用于测试。

二、VPP基础配置

2.1 准备工作

# 1. 在Linux虚拟机中关闭网卡eno1np0(交给VPP/DPDK管理,具体网卡名用ifconfig查看)sudoiplinkseteno1np0 down# 2. 配置eno2np1(Linux测试机器网卡,不交给VPP,具体网卡名用ifconfig查看)sudoipaddradd10.0.0.100/24 dev eno2np1sudoiplinkseteno2np1 up# 3. 加载VFIO模块(用于DPDK)sudomodprobe vfio-pci

2.2 配置vpp1.conf

创建或编辑vpp1.conf配置文件:

unix { nodaemon # 前台运行,方便查看日志 cli-listen /run/vpp/cli.sock } api-trace { on } # 启用 API 跟踪 dpdk { dev 0000:0b:00.0 { name eno1np0 } # 绑定第一个网卡(根据实际PCI地址修改) no-multi-seg # 关闭多段包(mlx5 推荐) } cpu { main-core 0 # 主核心 corelist-workers 1 # Worker 核心 } buffers { buffers-per-numa 131072 # 每个 NUMA 节点的缓冲区数量 default>2.3 启动VPP
sudovpp -c ./vpp1.conf

启动成功后,VPP会显示启动信息,并进入CLI模式。


三、使用自动化脚本配置

3.1 自动生成环境文件

我提供了一个Shell脚本setup_acl_environment.sh,可以自动创建所有需要的文件和目录结构。

3.1.1 运行环境生成脚本

复制3.1.2章节内容,并在VPP根目录生成脚本setup_acl_environment.sh下执行:

./setup_acl_environment.sh

脚本会自动创建以下内容(生成后最好chmod 755 ./learning一下,因为vpp调用html网页时需要有读写权限,否则会崩溃):

  • 目录结构:learning/acl_rules/www/eno1np0/
  • 配置文件:interfaces.jsonacl_bindings.json
  • 占位文件:vpp_acl_setup.pyindex.htmlacl_rules.json(需要手动填充内容)
3.1.2 环境生成脚本内容
#!/bin/bash## VPP ACL实战配置环境自动生成脚本# 功能:自动创建所有需要的文件和目录结构# 使用方法:在VPP根目录下执行此脚本#set-e# 遇到错误立即退出# 获取脚本所在目录(VPP根目录)VPP_ROOT="$(cd "$(dirname"${BASH_SOURCE[0]}")"&&pwd)" ACL_DIR="${VPP_ROOT}/learning/acl_rules" echo "==========================================" echo "VPP ACL实战配置环境生成脚本" echo "==========================================" echo "VPP根目录:${VPP_ROOT}" echo "ACL配置目录:${ACL_DIR}" # 1. 创建目录结构 echo "步骤1: 创建目录结构..." mkdir -p "${ACL_DIR}/www/eno1np0" echo "✓ 创建目录:${ACL_DIR}" echo "✓ 创建目录:${ACL_DIR}/www/eno1np0" # 2. 生成vpp_acl_setup.py脚本(占位符) echo ""echo"步骤2: 生成vpp_acl_setup.py脚本..."cat>"${ACL_DIR}/vpp_acl_setup.py"<<'VPP_ACL_SETUP_EOF' #!/usr/bin/env python3 """ VPP ACL自动化配置脚本 功能: 1. 配置网卡IP地址和状态 2. 导入ACL规则 3. 绑定ACL到接口 4. 启动HTTP静态页面服务 基于VPP测试框架的正确实现方式 """ import sys import os import json import time from ipaddress import IPv4Network, IPv6Network, ip_network # 添加VPP Python API路径 script_dir = os.path.dirname(os.path.abspath(__file__)) vpp_root = os.path.dirname(os.path.dirname(script_dir)) sys.path.insert(0, os.path.join(vpp_root, 'src', 'vpp-api', 'python')) try: from vpp_papi import VPPApiClient except ImportError: print("Error: Cannot import vpp_papi. Please ensure VPP Python API is installed.") sys.exit(1) class VppAclSetup: """VPP ACL自动化配置类""" def __init__(self, server_address="/run/vpp/api.sock"): """初始化""" self.server_address = server_address self.vpp = None self.connected = False def connect(self): """连接到VPP""" import traceback if not os.path.exists(self.server_address): print(f"Error: API socket not found: {self.server_address}") print("Please ensure VPP is running: sudo vpp -c vpp1.conf") return False try: # 查找API目录 api_dirs = [] possible_dirs = [ '/usr/share/vpp/api', '/usr/local/share/vpp/api', os.path.join(vpp_root, 'build-root', 'install-vpp_debug-native', 'vpp', 'share', 'vpp', 'api'), os.path.join(vpp_root, 'build-root', 'install-vpp-native', 'vpp', 'share', 'vpp', 'api'), ] for api_dir in possible_dirs: if os.path.isdir(api_dir): for root, dirs, files in os.walk(api_dir): if any(f.endswith('.api.json') for f in files): api_dirs.append(api_dir) break if api_dirs: break print(f"Connecting to VPP at {self.server_address}...") if api_dirs: self.vpp = VPPApiClient( apidir=api_dirs, server_address=self.server_address, use_socket=True ) else: self.vpp = VPPApiClient(server_address=self.server_address) self.vpp.connect("acl-setup") self.connected = True print("✓ Connected to VPP") return True except Exception as e: print(f"Failed to connect to VPP: {e}") traceback.print_exc() return False def disconnect(self): """断开连接""" if self.vpp and self.connected: try: self.vpp.disconnect() self.connected = False print("✓ Disconnected from VPP") except: pass def get_interface_index(self, interface_name): """获取接口索引""" try: # 使用sw_interface_dump获取接口信息 interfaces = self.vpp.api.sw_interface_dump() for iface in interfaces: # 处理接口名称(可能是bytes或str) try: iface_name = iface.interface_name if isinstance(iface_name, bytes): iface_name = iface_name.decode('utf-8').rstrip('\x00') else: iface_name = str(iface_name).rstrip('\x00') if iface_name == interface_name: return iface.sw_if_index except Exception as e: # 如果单个接口处理失败,继续处理下一个 continue # 如果没找到,打印所有接口名称用于调试 print(f" 可用接口列表:") for iface in interfaces: try: name = iface.interface_name if isinstance(name, bytes): name = name.decode('utf-8').rstrip('\x00') print(f" - {name} (sw_if_index: {iface.sw_if_index})") except: print(f" - <无法解析> (sw_if_index: {iface.sw_if_index})") return None except Exception as e: print(f"Error getting interface index: {e}") import traceback traceback.print_exc() return None def configure_interface(self, interface_name, ip_address, prefix_len=24): """配置接口IP地址和状态""" print(f"\n配置接口 {interface_name}...") sw_if_index = self.get_interface_index(interface_name) if sw_if_index is None: print(f"✗ 接口 {interface_name} 不存在") return False try: # 使用字符串格式的prefix,API会自动处理 prefix_str = f"{ip_address}/{prefix_len}" # 设置IP地址 - 参考vpp_interface.py的实现 self.vpp.api.sw_interface_add_del_address( sw_if_index=sw_if_index, prefix=prefix_str, # 字符串格式,如"10.0.0.213/24" is_add=True ) print(f" ✓ 设置IP地址: {prefix_str}") # 启用接口 - 参考vpp_memif.py的实现 self.vpp.api.sw_interface_set_flags( sw_if_index=sw_if_index, flags=1 # ADMIN_UP = 1 ) print(f" ✓ 启用接口") return True except Exception as e: print(f" ✗ 配置失败: {e}") import traceback traceback.print_exc() return False def add_acl(self, tag, rules): """添加ACL规则 - 参考vpp_acl.py的实现""" print(f"\n添加ACL规则: {tag}...") try: # 转换规则格式 - 参考AclRule.encode()的实现 vpp_rules = [] for rule in rules: # 解析源和目标前缀 - 使用IPv4Network/IPv6Network对象 src_prefix_str = rule.get("src_prefix", "0.0.0.0/0") dst_prefix_str = rule.get("dst_prefix", "0.0.0.0/0") src_prefix = IPv4Network(src_prefix_str) if '/' in src_prefix_str else IPv4Network(f"{src_prefix_str}/32") dst_prefix = IPv4Network(dst_prefix_str) if '/' in dst_prefix_str else IPv4Network(f"{dst_prefix_str}/32") vpp_rule = { "is_permit": rule.get("is_permit", 1), "proto": rule.get("proto", 0), "srcport_or_icmptype_first": rule.get("srcport_or_icmptype_first", 0), "srcport_or_icmptype_last": rule.get("srcport_or_icmptype_last", 65535), "dstport_or_icmpcode_first": rule.get("dstport_or_icmpcode_first", 0), "dstport_or_icmpcode_last": rule.get("dstport_or_icmpcode_last", 65535), "tcp_flags_mask": rule.get("tcp_flags_mask", 0), "tcp_flags_value": rule.get("tcp_flags_value", 0), "src_prefix": src_prefix, # IPv4Network对象,API会自动处理 "dst_prefix": dst_prefix, # IPv4Network对象,API会自动处理 } vpp_rules.append(vpp_rule) # 调用API - 参考VppAcl.add_vpp_config()的实现 # tag直接传递字符串,不需要encode reply = self.vpp.api.acl_add_replace( acl_index=0xFFFFFFFF, # 创建新ACL tag=tag, # 字符串,API会自动处理 count=len(vpp_rules), r=vpp_rules ) if reply.retval == 0: print(f" ✓ ACL创建成功,索引: {reply.acl_index}") return reply.acl_index else: print(f" ✗ ACL创建失败: retval={reply.retval}") return None except Exception as e: print(f" ✗ 添加ACL失败: {e}") import traceback traceback.print_exc() return None def bind_acl_to_interface(self, interface_name, acl_index, is_input=True): """绑定ACL到接口 - 参考VppAclInterface.add_vpp_config()的实现""" print(f"\n绑定ACL到接口 {interface_name}...") sw_if_index = self.get_interface_index(interface_name) if sw_if_index is None: print(f"✗ 接口 {interface_name} 不存在") return False try: # 使用acl_interface_add_del API self.vpp.api.acl_interface_add_del( is_add=True, is_input=is_input, sw_if_index=sw_if_index, acl_index=acl_index ) direction = "输入" if is_input else "输出" print(f" ✓ ACL {acl_index} 已绑定到接口 {interface_name} ({direction})") return True except Exception as e: print(f" ✗ 绑定失败: {e}") import traceback traceback.print_exc() return False def start_http_server(self, ip_address, port, www_root): """启动HTTP静态服务器""" print(f"\n启动HTTP服务器 {ip_address}:{port}...") try: # 重要:cli_inband API只接受ASCII编码的字符串 # 如果路径包含非ASCII字符(如中文),会导致编码错误 # 确保路径是ASCII编码 www_root_abs = os.path.abspath(www_root) # 检查路径是否包含非ASCII字符 try: www_root_abs.encode('ascii') except UnicodeEncodeError: print(f" ✗ 错误: 路径包含非ASCII字符,VPP CLI API不支持") print(f" 路径: {www_root_abs}") print(f" 请使用纯ASCII路径(不要包含中文等非ASCII字符)") print(f" 请手动在VPP CLI中执行:") print(f" http static server www-root <ASCII_PATH> uri tcp://{ip_address}/{port}") return False # 使用CLI命令启动HTTP服务器 # 命令格式: http static server www-root <path> uri tcp://<ip>/<port> uri = f"tcp://{ip_address}/{port}" cmd = f"http static server www-root {www_root_abs} uri {uri}" # 通过API执行CLI命令 # cmd参数必须是ASCII编码的字符串(vpp_serializer.py:162使用.encode("ascii")) try: reply = self.vpp.api.cli_inband(cmd=cmd) if reply and hasattr(reply, 'reply') and reply.reply: result = reply.reply if isinstance(result, bytes): result = result.decode('utf-8') if 'error' in result.lower(): print(f" ✗ 启动失败: {result}") print(f" 提示: 请手动在VPP CLI中执行: {cmd}") return False else: print(f" ✓ HTTP服务器已启动: http://{ip_address}:{port}") print(f" 根目录: {www_root_abs}") return True else: # 如果没有回复,假设命令已发送 print(f" ✓ HTTP服务器命令已发送") print(f" 如果服务未启动,请手动在VPP CLI中执行:") print(f" {cmd}") return True except (UnicodeEncodeError, AttributeError) as e: # cli_inband API只支持ASCII编码 if isinstance(e, UnicodeEncodeError): print(f" ✗ CLI命令包含非ASCII字符,无法通过API执行") print(f" 请手动在VPP CLI中执行:") print(f" http static server www-root {www_root_abs} uri {uri}") else: print(f" ⚠ CLI API不可用,请手动在VPP CLI中执行:") print(f" {cmd}") return True except Exception as e: print(f" ✗ 启动HTTP服务器失败: {e}") print(f" 请手动在VPP CLI中执行:") www_root_abs = os.path.abspath(www_root) print(f" http static server www-root {www_root_abs} uri tcp://{ip_address}/{port}") import traceback traceback.print_exc() return False def run_setup(self): """执行完整配置""" print("=" * 60) print("VPP ACL自动化配置") print("=" * 60) # 1. 连接VPP if not self.connect(): return False try: # 2. 配置接口 print("\n" + "=" * 60) print("步骤1: 配置网卡") print("=" * 60) self.configure_interface("eno1np0", "10.0.0.213", 24) time.sleep(1) # 3. 加载ACL规则 print("\n" + "=" * 60) print("步骤2: 导入ACL规则") print("=" * 60) acl_rules_file = os.path.join(script_dir, "acl_rules.json") if not os.path.exists(acl_rules_file): print(f"✗ ACL规则文件不存在: {acl_rules_file}") return False with open(acl_rules_file, 'r') as f: acl_config = json.load(f) acl_indices = {} for acl_config_item in acl_config.get('acls', []): tag = acl_config_item.get('tag') rules = acl_config_item.get('rules', []) if not tag or not rules: continue acl_index = self.add_acl(tag, rules) if acl_index is not None: acl_indices[tag] = acl_index time.sleep(0.5) # 4. 绑定ACL到接口 print("\n" + "=" * 60) print("步骤3: 绑定ACL到接口") print("=" * 60) # eno1np0: 应用ACL规则 if "eno1np0-acl" in acl_indices: self.bind_acl_to_interface("eno1np0", acl_indices["eno1np0-acl"], True) # 5. 启动HTTP服务 print("\n" + "=" * 60) print("步骤4: 启动HTTP静态服务器") print("=" * 60) www_root = os.path.join(script_dir, "www", "eno1np0") self.start_http_server("10.0.0.213", 80, www_root) # 完成 print("\n" + "=" * 60) print("配置完成!") print("=" * 60) print("\n测试说明:") print("ACL规则配置:") print(" - 10.0.0.1 (Windows主机) 可以HTTP访问 10.0.0.213:80") print(" - 其他地址 不能HTTP访问 10.0.0.213:80") print(" - 其他地址 可以ICMP访问 10.0.0.213") print(" - 10.0.0.1 (Windows主机) 不能ICMP访问 10.0.0.213") print("\n测试命令:") print(" # 从Windows主机(10.0.0.1)测试:") print(" curl http://10.0.0.213 # 应该成功") print(" ping 10.0.0.213 # 应该失败") print(" # 从Linux测试机器(10.0.0.100)测试:") print(" curl http://10.0.0.213 # 应该失败") print(" ping 10.0.0.213 # 应该成功") print("\n查看配置:") print(" vppctl# show acl-plugin acl") print(" vppctl# show acl-plugin interface") print(" vppctl# show http static server") return True except Exception as e: print(f"\n✗ 配置过程中出错: {e}") import traceback traceback.print_exc() return False finally: self.disconnect() def main(): """主函数""" setup = VppAclSetup() success = setup.run_setup() sys.exit(0 if success else 1) if __name__ == '__main__': main() VPP_ACL_SETUP_EOFchmod+x"${ACL_DIR}/vpp_acl_setup.py"echo" ✓ 生成文件:${ACL_DIR}/vpp_acl_setup.py"# 3. 生成index.html(占位符)echo""echo"步骤3: 生成index.html文件..."cat>"${ACL_DIR}/www/eno1np0/index.html"<<'INDEX_HTML_EOF' <!DOCTYPE html> <html> <head> <title>Welcome - eno1np0</title> <meta charset="UTF-8"> <style> body { font-family: Arial, sans-serif; text-align: center; padding: 50px; background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); color: white; margin: 0; min-height: 100vh; display: flex; flex-direction: column; justify-content: center; align-items: center; } .container { background: rgba(255, 255, 255, 0.1); padding: 40px; border-radius: 20px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); backdrop-filter: blur(10px); } h1 { font-size: 64px; margin: 20px 0; text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); } p { font-size: 24px; margin: 15px 0; line-height: 1.6; } .ip-address { font-family: 'Courier New', monospace; background: rgba(255, 255, 255, 0.2); padding: 10px 20px; border-radius: 10px; display: inline-block; margin: 10px 0; } .status { font-size: 32px; font-weight: bold; margin: 20px 0; } </style> </head> <body> <div class="container"> <h1>✅ Access Allowed!</h1> <p class="status">ACL Rule: PERMIT</p> <p>This is <span class="ip-address">eno1np0 (10.0.0.213)</span></p> <p>Source IP: <span class="ip-address">10.0.0.1</span> is allowed to access this server</p> <p>HTTP Port: 80</p> <p style="margin-top: 30px; font-size: 18px; opacity: 0.9;"> VPP ACL实战配置 - 允许访问示例 </p> </div> </body> </html> INDEX_HTML_EOFecho" ✓ 生成文件:${ACL_DIR}/www/eno1np0/index.html"# 4. 生成acl_rules.json(占位符)echo""echo"步骤4: 生成acl_rules.json配置文件..."cat>"${ACL_DIR}/acl_rules.json"<<'ACL_RULES_EOF' { "acls": [ { "tag": "web-server-acl", "acl_index": 4294967295, "rules": [ { "is_permit": 1, "src_prefix": "0.0.0.0/0", "dst_prefix": "192.168.1.100/32", "proto": 6, "srcport_or_icmptype_first": 0, "srcport_or_icmptype_last": 65535, "dstport_or_icmpcode_first": 80, "dstport_or_icmpcode_last": 443, "tcp_flags_mask": 0, "tcp_flags_value": 0 }, { "is_permit": 0, "src_prefix": "10.0.0.0/8", "dst_prefix": "192.168.1.100/32", "proto": 0, "srcport_or_icmptype_first": 0, "srcport_or_icmptype_last": 0, "dstport_or_icmpcode_first": 0, "dstport_or_icmpcode_last": 0, "tcp_flags_mask": 0, "tcp_flags_value": 0 } ] }, { "tag": "ssh-access-acl", "acl_index": 4294967295, "rules": [ { "is_permit": 1, "src_prefix": "192.168.1.0/24", "dst_prefix": "0.0.0.0/0", "proto": 6, "srcport_or_icmptype_first": 0, "srcport_or_icmptype_last": 65535, "dstport_or_icmpcode_first": 22, "dstport_or_icmpcode_last": 22, "tcp_flags_mask": 0, "tcp_flags_value": 0 } ] }, { "tag": "dns-acl", "acl_index": 4294967295, "rules": [ { "is_permit": 1, "src_prefix": "0.0.0.0/0", "dst_prefix": "8.8.8.8/32", "proto": 17, "srcport_or_icmptype_first": 0, "srcport_or_icmptype_last": 65535, "dstport_or_icmpcode_first": 53, "dstport_or_icmpcode_last": 53, "tcp_flags_mask": 0, "tcp_flags_value": 0 }, { "is_permit": 1, "src_prefix": "0.0.0.0/0", "dst_prefix": "8.8.4.4/32", "proto": 17, "srcport_or_icmptype_first": 0, "srcport_or_icmptype_last": 65535, "dstport_or_icmpcode_first": 53, "dstport_or_icmpcode_last": 53, "tcp_flags_mask": 0, "tcp_flags_value": 0 } ] } ] } ACL_RULES_EOFecho" ✓ 生成文件:${ACL_DIR}/acl_rules.json"# 完成echo""echo"=========================================="echo"环境生成完成!"echo"=========================================="echo""echo"下一步操作:"echo"1. 复制vpp_acl_setup.py的内容到:${ACL_DIR}/vpp_acl_setup.py"echo"2. 复制index.html的内容到:${ACL_DIR}/www/eno1np0/index.html"echo"3. 复制acl_rules.json的内容到:${ACL_DIR}/acl_rules.json"echo""echo"然后按照README.md中的说明进行配置和测试。"echo""

注意

  • 脚本会在VPP根目录下创建learning/acl_rules目录
  • vpp_acl_setup.pyindex.htmlacl_rules.json这三个文件会创建为占位符(空字符串),需要手动复制内容
  • 其他配置文件(interfaces.jsonacl_bindings.json)会自动生成完整内容

3.2 脚本功能说明

我们提供了一个自动化配置脚本vpp_acl_setup.py,它可以:

  1. 配置网卡:设置网卡IP地址和状态
  2. 配置ACL规则:从JSON文件导入ACL规则
  3. 绑定ACL到接口:将ACL规则应用到指定接口
  4. 启动HTTP服务:启动HTTP静态页面服务

3.2 运行脚本

cdlearning/acl_rulessudopython3 vpp_acl_setup.py

注意:确保路径learning/acl_rules不包含非ASCII字符(如中文),否则CLI命令会失败。

脚本会自动执行以下操作:

  • 配置eno1np0:IP 10.0.0.213/24,启用接口
  • 导入ACL规则
  • 绑定ACL到接口
  • 启动HTTP静态页面服务

注意:eno2np1不需要脚本配置,它由Linux内核管理,IP地址为10.0.0.100/24。

3.3 手动配置(可选)

如果不想使用脚本,可以手动执行以下命令:

配置网卡(eno1np0)
vppctl# set int ip addr eno1np0 10.0.0.213/24vppctl# set int state eno1np0 up

四、ACL规则配置

4.1 ACL规则说明

我们创建了一个ACL规则集,包含4条规则,实现精细化的访问控制:

ACL规则集:eno1np0-acl
{"tag":"eno1np0-acl","rules":[{"is_permit":1,// 规则1:允许"src_prefix":"10.0.0.1/32",// Windows主机IP"dst_prefix":"10.0.0.213/32",// eno1np0 IP"proto":6,// TCP协议"dstport_or_icmpcode_first":80,// HTTP端口"dstport_or_icmpcode_last":80},{"is_permit":0,// 规则2:拒绝"src_prefix":"0.0.0.0/0",// 所有源地址"dst_prefix":"10.0.0.213/32","proto":6,// TCP协议"dstport_or_icmpcode_first":80,// HTTP端口"dstport_or_icmpcode_last":80},{"is_permit":0,// 规则3:拒绝(更具体的规则,必须放在规则4之前)"src_prefix":"10.0.0.1/32",// Windows主机IP"dst_prefix":"10.0.0.213/32","proto":1,// ICMP协议"srcport_or_icmptype_first":0,// ICMP类型范围"srcport_or_icmptype_last":255,"dstport_or_icmpcode_first":0,// ICMP代码范围"dstport_or_icmpcode_last":255},{"is_permit":1,// 规则4:允许(更通用的规则,放在规则3之后)"src_prefix":"0.0.0.0/0",// 所有源地址"dst_prefix":"10.0.0.213/32","proto":1,// ICMP协议"srcport_or_icmptype_first":0,// ICMP类型范围"srcport_or_icmptype_last":255,"dstport_or_icmpcode_first":0,// ICMP代码范围"dstport_or_icmpcode_last":255}]}

规则说明(按匹配顺序):

  • 规则1:允许10.0.0.1访问10.0.0.213的80端口(HTTP)✅
  • 规则2:拒绝其他所有地址访问10.0.0.213的80端口(HTTP)❌
  • 规则3:拒绝10.0.0.1 ICMP访问10.0.0.213(ping等)❌(更具体的规则,必须在前)
  • 规则4:允许其他所有地址ICMP访问10.0.0.213(ping等)✅(更通用的规则,必须在后)

匹配逻辑(first-match原则):

  1. 如果源IP是10.0.0.1且协议是TCP端口80 → 规则1匹配 →允许
  2. 如果源IP不是10.0.0.1且协议是TCP端口80 → 规则2匹配 →拒绝
  3. 如果源IP是10.0.0.1且协议是ICMP → 规则3匹配 →拒绝(先匹配,规则4不会检查)
  4. 如果源IP不是10.0.0.1且协议是ICMP → 规则4匹配 →允许

⚠️ 重要:规则顺序非常关键!因为VPP ACL使用first-match原则,更具体的规则(规则3)必须放在更通用的规则(规则4)之前,否则规则3永远不会被匹配到。

为什么规则1和规则2不冲突?

这是一个很好的问题!规则1和规则2看起来有冲突,但实际上不会,原因如下:

规则对比
规则源地址目标地址协议端口动作
规则110.0.0.1/3210.0.0.213/32TCP80✅ 允许
规则20.0.0.0/010.0.0.213/32TCP80❌ 拒绝
为什么规则2的0.0.0.0/0不会匹配到10.0.0.1?

答案:规则2会匹配10.0.0.1,但由于first-match原则,规则1先匹配,所以规则2不会被检查!

详细匹配过程

场景1:10.0.0.1发送TCP:80数据包

数据包:源IP=10.0.0.1, 目标IP=10.0.0.213, 协议=TCP, 端口=80 步骤1:检查规则1 ├─ 源IP匹配? 10.0.0.1 == 10.0.0.1/32 ✅ 匹配 ├─ 目标IP匹配? 10.0.0.213 == 10.0.0.213/32 ✅ 匹配 ├─ 协议匹配? TCP == TCP ✅ 匹配 ├─ 端口匹配? 80 == 80 ✅ 匹配 └─ 结果:✅ 规则1完全匹配 → 动作:允许 → **停止检查,数据包通过** 步骤2:检查规则2 └─ 由于规则1已匹配,**不再检查规则2**

场景2:10.0.0.100发送TCP:80数据包

数据包:源IP=10.0.0.100, 目标IP=10.0.0.213, 协议=TCP, 端口=80 步骤1:检查规则1 ├─ 源IP匹配? 10.0.0.100 == 10.0.0.1/32 ❌ 不匹配 └─ 结果:规则1不匹配 → **继续检查规则2** 步骤2:检查规则2 ├─ 源IP匹配? 10.0.0.100 在 0.0.0.0/0 范围内 ✅ 匹配(0.0.0.0/0匹配所有IP) ├─ 目标IP匹配? 10.0.0.213 == 10.0.0.213/32 ✅ 匹配 ├─ 协议匹配? TCP == TCP ✅ 匹配 ├─ 端口匹配? 80 == 80 ✅ 匹配 └─ 结果:✅ 规则2完全匹配 → 动作:拒绝 → **停止检查,数据包被丢弃**
关键点总结
  1. 规则1更具体10.0.0.1/32只匹配一个IP地址
  2. 规则2更通用0.0.0.0/0匹配所有IP地址(包括10.0.0.1)
  3. First-Match原则:VPP按顺序检查规则,第一个匹配的规则生效后立即停止
  4. 规则顺序很重要:更具体的规则必须放在更通用的规则之前
如果顺序颠倒会怎样?

如果规则2在规则1之前:

规则2:拒绝0.0.0.0/0TCP:80(在前)规则1:允许10.0.0.1TCP:80(在后)

结果

  • 10.0.0.1的数据包会先匹配规则2(因为0.0.0.0/0匹配所有IP)
  • 规则1永远不会被检查
  • 10.0.0.1的HTTP访问会被拒绝!

这就是为什么规则1必须在规则2之前的原因!

4.2 ACL规则匹配顺序

VPP ACL使用first-match原则:

  • 规则按顺序匹配
  • 第一个匹配的规则生效
  • 后续规则不再检查

重要:规则的顺序非常重要!应该将最具体的规则放在前面,通用的规则放在后面。

实际案例

  • 错误顺序:规则3(允许所有地址ICMP)在规则4(拒绝10.0.0.1 ICMP)之前
    • 结果:10.0.0.1的ICMP包会先匹配规则3,导致规则4永远不会生效
  • 正确顺序:规则3(拒绝10.0.0.1 ICMP)在规则4(允许所有地址ICMP)之前
    • 结果:10.0.0.1的ICMP包先匹配规则3被拒绝,其他地址的ICMP包匹配规则4被允许

五、HTTP静态页面服务配置

5.1 创建HTML页面

我们创建了一个简单的HTML页面用于测试:

eno1np0的欢迎页面

文件:learning/acl_rules/www/eno1np0/index.html

<!DOCTYPEhtml><html><head><title>Welcome - eno1np0</title><style>body{font-family:Arial,sans-serif;text-align:center;padding:50px;background-color:#4CAF50;color:white;}h1{font-size:48px;}p{font-size:24px;}</style></head><body><h1>✅ Access Allowed!</h1><p>This is eno1np0 (10.0.0.213)</p><p>ACL Rule: Allow 10.0.0.1</p></body></html>

5.2 启动HTTP服务

方法1:使用脚本自动启动

脚本会自动启动HTTP服务:

  • eno1np0:http://10.0.0.213:80
方法2:手动启动
# 启动eno1np0的HTTP服务vppctl# http static server www-root ./learning/acl_rules/www/eno1np0 uri tcp://10.0.0.213/80

注意:VPP的HTTP静态服务器在同一时间只能启动一个实例。如果需要多个HTTP服务,需要使用不同的端口或IP地址。

重要:路径必须使用ASCII字符。如果路径包含非ASCII字符(如中文),请使用符号链接或重命名目录。

参数说明

  • www-root:HTML文件所在的根目录
  • uri:监听地址和端口,格式为tcp://IP地址/端口

六、功能测试

6.1 测试1:从10.0.0.1访问HTTP(应该成功)

在Windows主机(10.0.0.1)上执行:

# 使用浏览器访问http://10.0.0.213# 或使用curl测试curlhttp://10.0.0.213

预期结果

  • 成功访问:显示绿色页面 “Access Allowed!”
  • 页面内容:显示 “This is eno1np0 (10.0.0.213)”
  • 原因:ACL规则1允许10.0.0.1访问10.0.0.213的80端口

6.2 测试2:从10.0.0.1进行ICMP(应该失败)

在Windows主机(10.0.0.1)上执行:

ping10.0.0.213

预期结果

  • ping失败请求超时Destination host unreachable
  • 原因:ACL规则3拒绝10.0.0.1 ICMP访问10.0.0.213

6.3 测试3:从Linux测试机器访问HTTP(应该失败)

在Linux测试机器(10.0.0.100,eno2np1)上执行:

curlhttp://10.0.0.213

预期结果

  • 连接超时curl: (7) Failed to connect to 10.0.0.213 port 80: Connection timed out
  • 原因:ACL规则2拒绝其他地址访问10.0.0.213的80端口

6.4 测试4:从Linux测试机器进行ICMP(应该成功)

在Linux测试机器(10.0.0.100,eno2np1)上执行:

ping10.0.0.213

预期结果

  • ping成功:收到ICMP回复
  • 原因:ACL规则4允许其他地址ICMP访问10.0.0.213

6.5 测试总结

源IP协议端口/类型预期结果匹配规则
10.0.0.1 (Windows)TCP80✅ 允许规则1
10.0.0.100 (Linux测试机)TCP80❌ 拒绝规则2
10.0.0.100 (Linux测试机)ICMP-✅ 允许规则4
10.0.0.1 (Windows)ICMP-❌ 拒绝规则3

七、数据流向详解

7.1 允许访问的数据流(eno1np0)

┌─────────────────────────────────────────────────────────────────┐ │ 场景:Windows主机 (10.0.0.1) 访问 eno1np0 (10.0.0.213:80) │ └─────────────────────────────────────────────────────────────────┘ 【请求数据包流向】 ┌──────────────┐ │ Windows │ │ 10.0.0.1 │ └──────┬───────┘ │ │ HTTP GET请求 │ 源IP: 10.0.0.1 │ 目标IP: 10.0.0.213 │ 目标端口: 80 ▼ ┌──────────────────────────────────────────────────────────────┐ │ VMware主机模式网络 10.0.0.0/24 │ └───────────────────┬──────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ eno1np0 (10.0.0.213) │ │ 接收数据包:源10.0.0.1 → 目标10.0.0.213:80 │ └───────────────────┬──────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ VPP ACL处理(输入方向) │ │ 1. 匹配规则1:源10.0.0.1/32 → 目标10.0.0.213/32:80 │ │ 2. 动作:PERMIT(允许) │ │ 3. 数据包继续转发 │ └───────────────────┬──────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ HTTP静态服务器 │ │ 1. 接收HTTP请求 │ │ 2. 读取index.html文件 │ │ 3. 返回HTTP响应 │ └───────────────────┬──────────────────────────────────────────┘ │ │ HTTP响应 │ 源IP: 10.0.0.213 │ 目标IP: 10.0.0.1 │ 源端口: 80 ▼ ┌──────────────┐ │ Windows │ │ 10.0.0.1 │ ← 收到响应,显示页面! └──────────────┘

7.2 ICMP数据流(其他地址允许,10.0.0.1拒绝)

┌─────────────────────────────────────────────────────────────────┐ │ 场景1:Linux测试机器 (10.0.0.100) ping eno1np0 (10.0.0.213) │ └─────────────────────────────────────────────────────────────────┘ 【ICMP请求数据包流向】 ┌──────────────┐ │ Linux测试机 │ │ 10.0.0.100 │ └──────┬───────┘ │ │ ICMP Echo Request │ 源IP: 10.0.0.100 │ 目标IP: 10.0.0.213 │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ VPP ACL处理(输入方向) │ │ 1. 匹配规则4:源非10.0.0.1 → 目标10.0.0.213,ICMP │ │ 2. 动作:PERMIT(允许) │ │ 3. ICMP包继续处理 │ └───────────────────┬──────────────────────────────────────────┘ │ │ ICMP Echo Reply │ ▼ ┌──────────────┐ │ Linux测试机 │ │ 10.0.0.100 │ ← ping成功! └──────────────┘ ┌─────────────────────────────────────────────────────────────────┐ │ 场景2:Windows主机 (10.0.0.1) ping eno1np0 (10.0.0.213) │ └─────────────────────────────────────────────────────────────────┘ 【ICMP请求数据包流向】 ┌──────────────┐ │ Windows │ │ 10.0.0.1 │ └──────┬───────┘ │ │ ICMP Echo Request │ 源IP: 10.0.0.1 │ 目标IP: 10.0.0.213 │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ VPP ACL处理(输入方向) │ │ 1. 匹配规则3:源10.0.0.1/32 → 目标10.0.0.213/32,ICMP │ │ 2. 动作:DENY(拒绝) │ │ 3. ICMP包被丢弃 │ │ 4. 不发送任何响应 │ └───────────────────┬──────────────────────────────────────────┘ │ │ (数据包被丢弃,无响应) │ ▼ ┌──────────────┐ │ Windows │ │ 10.0.0.1 │ ← ping失败,超时! └──────────────┘

此时查看vpp的节点流向图可以看到节点acl-plugin-in-ip4-fa

八、ACL规则验证和调试

8.1 查看ACL配置

# 查看所有ACL规则vppctl# show acl-plugin acl# 查看接口的ACL绑定vppctl# show acl-plugin interface# 查看ACL统计信息vppctl# show acl-plugin tables

8.2 查看ACL匹配统计

# 查看ACL计数器(如果启用)vppctl# show acl-plugin statistics

8.3 测试ACL规则

可以使用ping测试ICMP ACL规则:

# 从Windows主机(10.0.0.1)ping eno1np0(应该失败)ping10.0.0.213# 从Linux测试机器(10.0.0.100)ping eno1np0(应该成功)ping10.0.0.213

注意:我们的ACL规则同时控制了TCP端口80和ICMP协议,实现了精细化的访问控制。


九、常见问题排查

9.1 问题1:无法连接到VPP API

症状:脚本报错 “Failed to connect to VPP”

解决方案

# 1. 检查VPP是否运行psaux|grepvpp# 2. 检查API socket是否存在ls-la /run/vpp/api.sock# 3. 检查权限sudochmod666/run/vpp/api.sock# 临时解决方案# 或配置vpp组权限(推荐)

9.2 问题2:HTTP服务无法访问

症状:浏览器无法访问HTTP页面

解决方案

# 1. 检查HTTP服务是否启动vppctl# show http static server# 2. 检查接口状态vppctl# show int# 3. 检查IP地址配置vppctl# show int addr# 4. 检查ACL规则是否正确应用vppctl# show acl-plugin interface

9.3 问题3:ACL规则不生效

症状:配置了ACL规则,但流量仍然可以通过

解决方案

# 1. 确认ACL已绑定到接口vppctl# show acl-plugin interface# 2. 检查ACL规则顺序(first-match原则)vppctl# show acl-plugin acl# 3. 确认规则方向(input/output)vppctl# show acl-plugin interface detail

9.4 问题4:Windows主机无法访问

症状:Windows主机无法ping通虚拟机

解决方案

# 1. 检查Windows主机IP地址ipconfig# Windows命令行# 2. 检查虚拟机网卡状态vppctl# show int# 3. 检查路由表vppctl# show ip fib# 4. 检查防火墙设置(Windows和Linux)

十、总结

10.1 实验成果

通过本次实验,我们成功实现了:

  1. 网卡配置:网卡采用桥接模式,与Windows主机互联
  2. ACL规则配置:实现了基于源IP、协议和端口的精细化访问控制
    • HTTP访问控制:10.0.0.1允许,其他地址拒绝
    • ICMP访问控制:其他地址允许,10.0.0.1拒绝
  3. HTTP服务部署:在网卡上部署了HTTP静态页面服务
  4. 访问控制验证:验证了ACL规则的正确性和有效性

10.2 关键知识点

  1. ACL规则匹配

    • 使用first-match原则
    • 规则顺序非常重要
    • 可以基于源IP、目标IP、协议、端口等字段匹配
  2. ACL动作

    • PERMIT(1):允许数据包通过
    • DENY(0):拒绝数据包,直接丢弃
  3. 接口绑定

    • ACL可以绑定到接口的输入方向(ingress)
    • ACL可以绑定到接口的输出方向(egress)
    • 可以同时绑定多个ACL规则
  4. HTTP静态服务器

    • VPP提供了内置的HTTP静态服务器
    • 可以绑定到特定IP地址和端口
    • 支持静态HTML文件服务

10.3 扩展实验

可以尝试以下扩展实验:

  1. 添加更多ACL规则

    • 允许特定IP范围访问
    • 允许特定端口范围
    • 添加UDP规则
    • 添加更多ICMP类型和代码的规则
  2. 测试不同协议

    • UDP服务
    • HTTPS服务(需要TLS配置)
  3. 性能测试

    • 测试ACL对性能的影响
    • 测试大量规则时的性能
  4. 有状态ACL

    • 使用PERMIT_REFLECT实现有状态ACL
    • 自动创建反向会话

附录

A. 配置文件位置

  • VPP配置文件:vpp1.conf
  • ACL规则文件:learning/acl_rules/acl_rules.json
  • 接口配置文件:learning/acl_rules/interfaces.json
  • ACL绑定文件:learning/acl_rules/acl_bindings.json
  • 配置脚本:learning/acl_rules/vpp_acl_setup.py
  • HTML页面:learning/acl_rules/www/

注意:所有路径使用ASCII字符,避免使用中文路径。

B. 参考命令速查

# VPP CLI命令vppctl# show int # 显示接口vppctl# show int addr # 显示接口IP地址vppctl# show acl-plugin acl # 显示ACL规则vppctl# show acl-plugin interface # 显示ACL接口绑定vppctl# show http static server # 显示HTTP服务器状态# Linux命令sudoiplinkseteno1np0 down# 关闭网卡sudomodprobe vfio-pci# 加载VFIO模块sudovpp -c vpp1.conf# 启动VPP# Python脚本sudopython3 vpp_acl_setup.py# 运行配置脚本

C. 相关文档

  • VPP ACL配置说明:VPP_ACL配置说明.md
  • VPP NAT44-EI实战指南:VPP_NAT44-EI实战配置指南.md
  • VPP官方文档:https://fd.io/vpp/

实验完成!🎉

通过本次实验,您应该已经掌握了VPP ACL的基本配置和使用方法。如有问题,请参考常见问题排查部分或查阅相关文档。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 12:44:39

springboot基于vue的博物馆藏品管理系统 博物馆参观预约系统_w106j1mv

目录 已开发项目效果实现截图开发技术系统开发工具&#xff1a; 核心代码参考示例1.建立用户稀疏矩阵&#xff0c;用于用户相似度计算【相似度矩阵】2.计算目标用户与其他用户的相似度系统测试总结源码文档获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&am…

作者头像 李华
网站建设 2026/4/23 12:49:25

springboot基于vue的商场百货零售业务系统 小程序_h5cz5i65

目录已开发项目效果实现截图开发技术系统开发工具&#xff1a;核心代码参考示例1.建立用户稀疏矩阵&#xff0c;用于用户相似度计算【相似度矩阵】2.计算目标用户与其他用户的相似度系统测试总结源码文档获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&…

作者头像 李华
网站建设 2026/4/23 12:53:34

springboot基于vue的城市宠物医院管理系统的设计与实现_420759zx

目录已开发项目效果实现截图开发技术系统开发工具&#xff1a;核心代码参考示例1.建立用户稀疏矩阵&#xff0c;用于用户相似度计算【相似度矩阵】2.计算目标用户与其他用户的相似度系统测试总结源码文档获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&…

作者头像 李华
网站建设 2026/4/22 20:50:00

边缘计算机设备管理系统搭建全流程技术攻略

摘要 边缘计算机因分布式部署、低带宽依赖、高可靠性要求的特性,其设备管理系统的搭建需聚焦“远程监控、运维提效、数据增值、客户服务”核心目标。本文针对边缘计算机产品制造商的核心需求,从系统架构设计、关键数据采集与传输技术、全流程落地步骤、核心避坑指南四大维度…

作者头像 李华