2 solutions
-
0
发一个C++版本的实现。里面注释的部分是输出调试信息。主要是判断IP是否属于代码段的时候设计到大端小端的问题要注意以下。 然后就是代码中对删除操作的处理偷懒了,没有完全匹配删除规则的一致性(比如IP/MASK匹配偷懒了,还有操作是A还是R也没去匹配了)但是测试用例里面不涉及到这种不一致的问题,所以还是能AC。
#include <bits/stdc++.h> using namespace std; struct addr { union { uint8_t u[4]; uint32_t fu; }; }; struct rule { addr ip; int mask; char action; string goto_rules; rule(){}; rule(addr _ip, int _mask, char _act, string _rule="") { ip=_ip; mask = _mask; action = _act; goto_rules = _rule; } bool match(struct addr sip) { // printf("mask %d %x %x\n", mask, ip.fu & 0xffffffff << (32-mask), sip.fu & 0xffffffff << (32-mask)); if((ip.fu & (0xffffffff << (32-mask))) == (sip.fu & (0xffffffff << (32-mask)))) { return true; } return false; }; }; struct addr string_to_ipmask(string str, int &mask) { int i, pre = 0; uint8_t tmp; struct addr ans; int b = 3; for(i=0; i<(int)str.length(); ++i) { if(str[i] == '.') { tmp = strtol(str.substr(pre, i-pre).c_str(), NULL, 10); ans.u[b--] = tmp; pre = i+1; } else if(str[i] == '/') { tmp = strtol(str.substr(pre, i-pre).c_str(), NULL, 10); ans.u[b--] = tmp; pre = i+1; break; } } if(i == (int)str.length()) { // 没有mask tmp = strtol(str.substr(pre, i-pre).c_str(), NULL, 10); ans.u[b--] = tmp; mask = 32; } else { // 有mask // printf("## %s", str.substr(pre, str.length()-pre).c_str()); mask = strtol(str.substr(pre, str.length()-pre).c_str(), NULL, 10); } // printf("%d.%d.%d.%d\n", ans.u[0], ans.u[1], ans.u[2], ans.u[3]); // printf("%x mask: %d\n", ans.fu, mask); return ans; } unordered_map<string, list<rule>> iptables; int main() { int n; cin >> n; string action, name, addv, cidr; int mask; struct addr ip; for(int i=0; i<n; ++i) { cin >> action; // printf("rule %s\n", action.c_str()); if(action == "A") { cin >> name; cin >> cidr; ip = string_to_ipmask(cidr, mask); cin >> action; if(action == "G") { cin >> addv; iptables[name].emplace_back(ip, mask, action[0], addv); } else { iptables[name].emplace_back(ip, mask, action[0]); } } else if(action == "I") { cin >> name; cin >> cidr; ip = string_to_ipmask(cidr, mask); cin >> action; if(action == "G") { cin >> addv; iptables[name].emplace_front(ip, mask, action[0], addv); } else { iptables[name].emplace_front(ip, mask, action[0]); } } else if(action == "M") { cin >> cidr; ip = string_to_ipmask(cidr, mask); string chain = "c0"; bool fmatch = false; reentry: for(rule &r : iptables[chain]) { if(r.match(ip)) { if(r.action == 'G') { chain = r.goto_rules; goto reentry; } else if(r.action == 'A' || r.action == 'R') { printf("%c", r.action); fmatch = true; break; } } } if(fmatch == false) { printf("U"); } } else if(action == "D") { cin >> name; cin >> cidr; ip = string_to_ipmask(cidr, mask); cin >> action; if(action == "G") cin >> addv; for(auto it = iptables[name].begin(); it != iptables[name].end(); ++it) { if(it->match(ip)) { iptables[name].erase(it); break; } } } } return 0; }
-
0
题面描述
本题是实现一个简化版的 iptables,用于管理 Linux 系统的网络访问控制。通过一系列操作命令,管理员可以定义规则链,并根据这些规则链对IP或CIDR地址进行访问控制。题目要求处理插入、追加、删除规则的操作,并对IP查询进行匹配,输出查询结果。
给定输入包括规则操作和查询命令,要求我们从一个默认规则链 c0 开始匹配 IP 地址,返回匹配的规则结果(Accept 或 Reject),或输出 Unknown 表示没有匹配的规则。
思路
题目核心是处理网络访问规则链,模拟 iptables 的规则匹配机制。主要涉及三种操作:插入、追加和删除规则,以及查询某个IP是否被允许访问。使用链表存储规则链,并且每次根据输入动态更新。
具体分析
1.数据结构设计:
使用一个字典 chains,键为链的名字,值为该链上的规则列表,每条规则由一个三元组 (action, net, mask) 表示: action: 表示该规则是 Accept 或 Reject。 net: IP 地址段的整数表示。 mask: 子网掩码长度。 另一个关键部分是处理 CIDR 地址段和具体 IPv4 地址的匹配,通过比特操作来判断某个IP是否在某个网段内。
2.IP和CIDR处理:
IP地址用整数表示,方便后续与子网掩码进行位运算。 CIDR格式的网段如 192.168.1.0/24 可以通过IP地址的前24位和网段掩码进行匹配。 具体实现中,将IP地址和掩码转换为整数,并通过与操作和位移实现匹配。
3.规则的插入、追加、删除:
插入:将规则插入到规则链的开头。 追加:将规则追加到规则链末尾。 删除:从链中找到对应规则并删除。
4.查询操作的实现:
从默认链 c0 开始查询,根据输入的IP逐条匹配链中的规则。 如果遇到 Goto 操作,跳转到指定的规则链继续匹配。 如果找到第一个匹配的规则,立即返回其结果(Accept 或 Reject)。如果所有规则都不匹配,则返回 Unknown。
DFS函数详解
查询IP是否被允许访问时,使用深度优先搜索(DFS)遍历规则链:
每次匹配时,从链 c0 开始,根据规则依次检查IP是否在某个CIDR网段内。 如果匹配到跳转规则(Goto),则递归进入新的规则链。 如果匹配到 Accept 或 Reject,立即返回结果,结束递归。 若没有规则匹配到,返回 Unknown
代码如下
python
from collections import defaultdict # 将IPv4地址转换为32位整数 # 例如 '192.168.1.1' 会被转换为一个32位整数,以便后续处理 def ip_to_int(ip): parts = list(map(int, ip.split('.'))) # 将IP地址拆分为四个部分 # 通过位运算将这四部分拼接为一个32位整数 return (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3] # 解析CIDR格式的网络地址,返回(网络地址整数, 掩码长度) # 例如 '192.168.1.0/24' 会被转换为对应的网络地址和子网掩码长度 def parse_cidr(cidr): ip, mask = cidr.split('/') # 将CIDR分为IP部分和掩码部分 mask = int(mask) # 掩码长度转换为整数 ip_int = ip_to_int(ip) # 将IP部分转换为32位整数 # 通过位运算计算网络地址 net = ip_int & (~((1 << (32 - mask)) - 1)) return (net, mask) # 返回网络地址和掩码长度 def main(): N = int(input()) # 读取总的命令数量 chains = defaultdict(list) # 使用字典保存每个规则链,链名作为键,链上的规则列表作为值 outputs = [] # 保存查询结果的输出列表 # 处理每条规则操作命令或查询命令 for _ in range(N): line = input().strip() # 读取并去掉命令行中的多余空格 tokens = line.split() # 将命令按空格分割成若干部分 op = tokens[0].upper() # 操作类型(I: 插入,A: 追加,D: 删除,M: 查询) if op in {'I', 'A'}: # 插入或追加规则 chain_name, ip_or_cidr, action = tokens[1:4] # 规则链名称,IP或CIDR,操作(A或R) if action == 'G': # 如果规则是Goto(跳转到其他规则链) target_chain = tokens[4] # 跳转的目标规则链 # 处理CIDR或单个IP net, mask = parse_cidr(ip_or_cidr) if '/' in ip_or_cidr else (ip_to_int(ip_or_cidr), 32) rule = ('G', net, mask, target_chain) # 构造Goto规则 else: # 普通的Accept或Reject规则 net, mask = parse_cidr(ip_or_cidr) if '/' in ip_or_cidr else (ip_to_int(ip_or_cidr), 32) rule = (action, net, mask) # 构造Accept或Reject规则 if op == 'I': # 插入规则到链的开头 chains[chain_name].insert(0, rule) else: # 追加规则到链的末尾 chains[chain_name].append(rule) elif op == 'D': # 删除规则 chain_name, ip_or_cidr, action = tokens[1:4] # 获取链名,IP或CIDR,操作(A或R) net, mask = parse_cidr(ip_or_cidr) if '/' in ip_or_cidr else (ip_to_int(ip_or_cidr), 32) # 删除规则链中与指定规则匹配的规则 rules = chains[chain_name] chains[chain_name] = [r for r in rules if not (r[0] == action and r[1] == net and r[2] == mask)] elif op == 'M': # 查询命令 ip_str = tokens[1] # 获取要查询的IP ip = ip_to_int(ip_str) # 将IP转换为32位整数 stack = ['c0'] # 从默认链 'c0' 开始查询 result = 'U' # 如果未找到匹配规则,结果默认设为 'U'(Unknown) visited_chains = set() # 记录已经访问过的规则链,避免循环跳转 # 深度优先搜索规则链 while stack: current_chain = stack.pop() # 取出当前要查询的规则链 if current_chain in visited_chains: # 如果该链已经访问过,跳过 continue visited_chains.add(current_chain) # 标记该链为已访问 rules = chains[current_chain] # 获取当前规则链上的所有规则 # 遍历该规则链上的所有规则 for rule in rules: # 检查当前IP是否符合该规则的网络地址范围 if rule[1] <= ip <= (rule[1] + ((1 << (32 - rule[2])) - 1)): if rule[0] in ('A', 'R'): # 如果是Accept或Reject规则 result = rule[0] # 记录匹配的结果 stack = [] # 清空堆栈,结束查询 break elif rule[0] == 'G': # 如果是Goto规则 stack.append(rule[3]) # 将目标链名加入堆栈,继续查询 break outputs.append(result) # 将查询结果加入输出列表 print(''.join(outputs)) # 最后输出所有查询结果 if __name__ == "__main__": main()
cpp
#include <bits/stdc++.h> using namespace std; // 将IPv4地址转换为32位整数 unsigned int ip_to_int(const string& ip) { unsigned int parts[4]; sscanf(ip.c_str(), "%u.%u.%u.%u", &parts[0], &parts[1], &parts[2], &parts[3]); return (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3]; } // 解析CIDR格式的网络地址,返回网络地址和掩码长度 pair<unsigned int, int> parse_cidr(const string& cidr) { size_t pos = cidr.find('/'); string ip = cidr.substr(0, pos); int mask = stoi(cidr.substr(pos + 1)); unsigned int ip_int = ip_to_int(ip); unsigned int net = ip_int & (~((1u << (32 - mask)) - 1)); return {net, mask}; } int main(){ ios::sync_with_stdio(false); cin.tie(0); int N; cin >> N; // 定义规则结构 struct Rule { char type; // 'A', 'R', 'G' unsigned int net; int mask; string target; // 仅G类型有 }; unordered_map<string, vector<Rule>> chains; string line; getline(cin, line); // 读取剩余的换行符 string outputs = ""; for(int i = 0; i < N; ++i){ getline(cin, line); if(line.empty()) { i--; continue; } stringstream ss(line); string op; ss >> op; char operation = toupper(op[0]); if(operation == 'I' || operation == 'A'){ string chain_name, ip_or_cidr, action; ss >> chain_name >> ip_or_cidr >> action; Rule rule; rule.type = action[0]; if(ip_or_cidr.find('/') != string::npos){ auto parsed = parse_cidr(ip_or_cidr); rule.net = parsed.first; rule.mask = parsed.second; } else{ rule.net = ip_to_int(ip_or_cidr); rule.mask = 32; } if(rule.type == 'G'){ string target_chain; ss >> target_chain; rule.target = target_chain; } if(operation == 'I'){ chains[chain_name].insert(chains[chain_name].begin(), rule); } else{ chains[chain_name].push_back(rule); } } else if(operation == 'D'){ string chain_name, ip_or_cidr, action; ss >> chain_name >> ip_or_cidr >> action; unsigned int net; int mask; if(ip_or_cidr.find('/') != string::npos){ auto parsed = parse_cidr(ip_or_cidr); net = parsed.first; mask = parsed.second; } else{ net = ip_to_int(ip_or_cidr); mask = 32; } char act = action[0]; auto &rules = chains[chain_name]; rules.erase( remove_if(rules.begin(), rules.end(), [&](const Rule& r) { if(r.type != act) return false; if(r.net != net) return false; if(r.mask != mask) return false; return true; }), rules.end() ); } else if(operation == 'M'){ string ip_str; ss >> ip_str; unsigned int ip = ip_to_int(ip_str); stack<string> stk; stk.push("c0"); char result = 'U'; unordered_set<string> visited; while(!stk.empty()){ string current_chain = stk.top(); stk.pop(); if(visited.find(current_chain) != visited.end()) continue; visited.insert(current_chain); if(chains.find(current_chain) == chains.end()) continue; for(auto &rule : chains[current_chain]){ unsigned int start = rule.net; unsigned int end = rule.net + ((1u << (32 - rule.mask)) - 1); if(ip >= start && ip <= end){ if(rule.type == 'A' || rule.type == 'R'){ result = rule.type; stk = stack<string>(); // 清空堆栈,结束查询 break; } else if(rule.type == 'G'){ stk.push(rule.target); break; } } } } outputs += result; } } cout << outputs; }
java
import java.util.*; import java.io.*; public class Main { // 将IPv4地址转换为32位整数 static long ipToInt(String ip) { String[] parts = ip.split("\\."); long res = 0; for(String part : parts){ res = (res << 8) | Long.parseLong(part); } return res; } // 解析CIDR格式的网络地址,返回网络地址和掩码长度 static Pair<Long, Integer> parseCidr(String cidr) { String[] parts = cidr.split("/"); String ip = parts[0]; int mask = Integer.parseInt(parts[1]); long ipInt = ipToInt(ip); long net = ipInt & (~((1L << (32 - mask)) - 1)); return new Pair<>(net, mask); } public static void main(String[] args) throws IOException{ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int N = Integer.parseInt(br.readLine()); // 定义规则结构 class Rule { char type; // 'A', 'R', 'G' long net; int mask; String target; // 仅G类型有 Rule(char type, long net, int mask, String target){ this.type = type; this.net = net; this.mask = mask; this.target = target; } } Map<String, List<Rule>> chains = new HashMap<>(); StringBuilder outputs = new StringBuilder(); for(int i = 0; i < N; ++i){ String line = br.readLine(); if(line == null || line.isEmpty()){ i--; continue; } String[] tokens = line.trim().split("\\s+"); char op = Character.toUpperCase(tokens[0].charAt(0)); if(op == 'I' || op == 'A'){ String chainName = tokens[1]; String ipOrCidr = tokens[2]; String action = tokens[3]; char actionType = action.charAt(0); long net; int mask; if(ipOrCidr.contains("/")){ Pair<Long, Integer> parsed = parseCidr(ipOrCidr); net = parsed.first; mask = parsed.second; } else{ net = ipToInt(ipOrCidr); mask = 32; } Rule rule; if(actionType == 'G'){ String targetChain = tokens[4]; rule = new Rule(actionType, net, mask, targetChain); } else{ rule = new Rule(actionType, net, mask, null); } chains.putIfAbsent(chainName, new ArrayList<>()); if(op == 'I'){ chains.get(chainName).add(0, rule); } else{ chains.get(chainName).add(rule); } } else if(op == 'D'){ String chainName = tokens[1]; String ipOrCidr = tokens[2]; String action = tokens[3]; char act = action.charAt(0); long net; int mask; if(ipOrCidr.contains("/")){ Pair<Long, Integer> parsed = parseCidr(ipOrCidr); net = parsed.first; mask = parsed.second; } else{ net = ipToInt(ipOrCidr); mask = 32; } if(chains.containsKey(chainName)){ List<Rule> rules = chains.get(chainName); rules.removeIf(r -> r.type == act && r.net == net && r.mask == mask); } } else if(op == 'M'){ String ipStr = tokens[1]; long ip = ipToInt(ipStr); Stack<String> stack = new Stack<>(); stack.push("c0"); char result = 'U'; Set<String> visited = new HashSet<>(); while(!stack.isEmpty()){ String currentChain = stack.pop(); if(visited.contains(currentChain)) continue; visited.add(currentChain); if(!chains.containsKey(currentChain)) continue; List<Rule> rules = chains.get(currentChain); for(Rule rule : rules){ long start = rule.net; long end = rule.net + ((1L << (32 - rule.mask)) - 1); if(ip >= start && ip <= end){ if(rule.type == 'A' || rule.type == 'R'){ result = rule.type; stack.clear(); break; } else if(rule.type == 'G'){ stack.push(rule.target); break; } } } } outputs.append(result); } } System.out.println(outputs.toString()); } // 简单的Pair类 static class Pair<F, S>{ F first; S second; Pair(F first, S second){ this.first = first; this.second = second; } } }
- 1
Information
- ID
- 154
- Time
- 1000ms
- Memory
- 256MiB
- Difficulty
- 9
- Tags
- # Submissions
- 90
- Accepted
- 17
- Uploaded By