当前位置: 首页>编程语言>正文

python获取路由器信号 python获取路由器数据包

老实说这是老东西了,自己之前用scala写过,用ruby写过,这次是用python第二次实现,github上这种生成脚本也挺多的,自己全当是练手。

这个脚本其实就是把apnic提供的数据过滤出指定数据并解析然后生成路由表更新脚本的程序。用途相信各位也清楚,以下是自己重复造轮子的过程:

程序的主逻辑是读取apnic数据,用正则表达式过滤和解析,用解析出来的数据生成路由表修改脚本和恢复脚本。

首先是读取apnic数据。个人的方式是查找当前目录,然后脚本所在目录,如果再没有自动下载文件到脚本所在目录并返回。这里没有read on fly,即边下载边读,也没有比对并确保最新的逻辑。之后可以考虑加上。

Python

APNIC_URL = 'http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest'
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
def locate_apnic_path(filename = 'delegated-apnic-latest'):
if os.path.exists(filename):
return filename
apnic_path = os.path.join(SCRIPT_DIR, filename)
if not os.path.exists(apnic_path):
print 'Download apnic file from %s to %s' % (APNIC_URL, apnic_path)
urllib.urlretrieve(APNIC_URL, apnic_path)
return apnic_path

使用正则表达式过滤并解析的代码如下。解析结果是个CIDR格式,即网关的IP和对应子网掩码的路由前缀数。原始数据中IP后面的数据是子网主机数。

Python

NET_PATTERN = re.compile(
r'^apnic\|CN\|ipv4\|([0-9.]+)\|(\d+)\|\d+\|a.*$', re.IGNORECASE)
def parse_cn_net(apnic_path):
with open(apnic_path) as lines:
for line in lines:
# example: apnic|CN|ipv4|1.0.32.0|8192|20110412|allocated
m = NET_PATTERN.match(line)
if not m: continue
# (IP, routing prefix), e.g (1.2.3.4, 24) means 1.2.3.4/24
yield (m.group(1), 32 - int(math.log(int(m.group(2)), 2)))

理论上接下就直接用解析出来的CIDR生成脚本了,但是考虑到部分网站或者内网也要纳入这个脚本,所以个人额外加了个excluding_net的配置,解析代码如下:

excluding_net中支持#开始的注释,忽略空行,允许直接host或者CIDR格式的单行配置。

Python
def load_excluding_net(filename = 'excluding_net'):
filepath = os.path.join(SCRIPT_DIR, filename)
if os.path.exists(filepath):
with open(filepath) as lines:
for line in lines:
if line.startswith('#'): continue # skip comment
host_or_cidr = line.rstrip() # remove line break
if not host_or_cidr: continue # skip empty line
i = host_or_cidr.find('/')
if i < 0: # host
yield (host_or_cidr, 32)
else:
yield (host_or_cidr[:i], int(host_or_cidr[(i + 1):]))
# xnnyygn.in, gssxgss.me
116.251.214.29/32
# internal net
192.168.1.0/24
192.168.3.0/24

接下来才是用上面解析出来的CIDR数据生成脚本。在撰写如何生成脚本的代码中个人考虑了好久,主要是因为不同平台生成的脚本文件名,命令模式不同。

首先我可以确定一点,默认网关IP是可以通过命令得到,而不应该在生成脚本时决定。否则笔记本一会儿有线一会儿无线网关地址变化了就麻烦了。

第二是如果默认网关地址变化,那么恢复默认网关时即恢复脚本时,需要通过类似/tmp/prev_gw来交互。

再加上脚本头,一些容错处理的话,程序中嵌入脚本文本会很多,自然而然会想到模板。不过我并没有使用通用的模板,直接分离为脚本头和route设置命令,同时增加输出脚本名配置用于定位脚本头模板和输出的脚本的名字。配置如下:

Python
PLATFORM_META = {
'linux': (
'linux-ip-pre-up', 'route add -net %s/%d gw ${PREV_GW} metric 5\n',
'linux-ip-down', 'route delete -net %s/%d gw ${PREV_GW} metric 5\n'
),
'mac': (
'mac-ip-up', 'route add %s/%d ${PREV_GW}\n',
'mac-ip-down', 'route delete %s/%d ${PREV_GW}\n'
)
}

这里字典的key是转换过的平台名,value的四个值分别是启动脚本名,启动脚本命令模板,恢复脚本名,恢复脚本命令模板。每个命令模板接受CIDR格式的tuple。

为了方便输出,个人用了个类来集合输出操作,这个类在初始化时就会把头模板填充进来。

Python

HEADER_DIR = 'header'
class RouteScript:
def __init__(self, name, cmd_pattern):
self.cmd_pattern = cmd_pattern
self.script = open(name, 'w')
header_path = os.path.join(SCRIPT_DIR, HEADER_DIR, name)
if os.path.exists(header_path):
with open(header_path) as f:
self.script.write(f.read())
def append(self, cidr):
self.script.write(self.cmd_pattern % cidr)
def close(self):
self.script.close()

基于上面类的输出代码,比没有类的时候要短小很多:

Python

def generate(platform):
config = PLATFORM_META[platform]
up_script = RouteScript(config[0], config[1])
down_script = RouteScript(config[2], config[3])
for cidr in itertools.chain(
load_excluding_net(), parse_cn_net(locate_apnic_path())):
up_script.append(cidr)
down_script.append(cidr)
up_script.close()
down_script.close()

最后脚本还做了一点平台支持的逻辑,即根据sys.platform决定用那个平台,比如linux2 -> linux, darwin -> mac。上面代码的platform是转换过的平台关键字。另外支持命令行第一个参数指定平台。

Python

PLATFORM_MAPPING = {
'linux2': 'linux',
'darwin': 'mac'
}
def determine_platform():
if len(sys.argv) > 1:
return sys.argv[1]
key = sys.platform
return PLATFORM_MAPPING.get(key) or key
if __name__ == '__main__':
platform = determine_platform()
if platform in PLATFORM_META:
generate(platform)
else:
print >>sys.stderr, 'unsupported platform [%s], or you can specify one' % platform

完整代码在这里。


https://www.xamrdz.com/lan/53h1939432.html

相关文章: