首页 > 代码库 > 基于Cisco CDP协议的家用路由器以及盒子的自动配置随想

基于Cisco CDP协议的家用路由器以及盒子的自动配置随想

每次买来一个设备,比如一台新的路由器,家庭盒子,就是荣耀立方,小米盒子这种,唯一觉得头大的就是它们的初始配置。
        这种盒子几乎都有一个初始IP地址用于插上网线登录界面后来配置盒子的IP地址,或者说更加先进一点,其本身有一个AP,然后用你的手机连上这个热点进行手机配置,看起来简直特别的酷!但这是假象!
        手机连接这个AP期间,手机不能连别的AP,甚至无法4G/3G,或者说用笔记本配置的话,网线连接笔记本和盒子的时候,这个笔记本要添加一个盒子的初始IP地址同网段的IP,然后接入,除非笔记本有额外的无线网卡(一般都有,好吧,这没问题!)或者双网卡,不然笔记本就不能上网查资料(配置过程中遇到问题很正常)!万一家里现有的网络节点IP地址与这个初始IP地址冲突,就...要知道192.168.1.1几乎成了通用的初始IP,如果你一下子买了多个(同型号?)盒子的话,你不得不做重复的劳动,连接AP,或者不断插拔网线!
        还有很多别的不便,请用过的人,踩过坑的人来补充...
        所以说,鉴于以上的不便,这种设备一直都是极客的玩具,对于一般人而言,那简直是噩梦。我本人是个是个方便至上的人,最讨厌的就是麻烦,当有人问我如何翻墙的时候,我的回答就是不翻墙,因为太麻烦,我不用google又死不了人,所以我宁可不用我也不会折腾什么翻墙。对于我不买家庭盒子或者买了扔在那不用的原因,也一样,不用又死不了人,所以我宁可不用也不愿意麻烦!
        但是,当我在工作中发现了Mikrotik之后,我改变了想法。
        Mikrotik可以装在VMWare中,这就对研究它提供了方便。而我确实也将它装在了VMWare中。启动后,我正愁如何配置它,上网搜索,发现了一个叫做WinBox的东西,这玩意儿可以在不配置IP地址的情况下,直接通过这个Mikrotik系统的MAC地址”连接“到系统,然后对它配置IP地址!!
        这其实没有什么不可思议的,谁告诉你建立连接一定要通过IP地址的啊!谁告诉你TCP就是一切的啊!事实上,昨天当前同事问我怎么才可以精通网络的时候,我告诉他,把除了TCP之外的所有关于网络的东西都精通了,你就精通网络了。除了单播IP地址之外,我们还有组播,即便没有组播IP地址,我们还有组播MAC地址可供利用!几乎所有的交换机都会向所有端口转发组播流量!
        而这关于组播的,就是根本!组播是一个抽象的组织者,组播地址并不需要与任何主机绑定,它是无实体的,对于MAC组播而言,只要组播流量到达该网卡,它就能接收,对于IP组播而言,只要本机加入了一个”组播组“,它就能处理。对于还没有IP地址的组播通信,显然只能用MAC组播了,怎么把网卡收到的组播流量路由到应用程序,无法靠协议栈路由了,因为此时IP层是通不过的,靠什么呢?靠PACKET套接字!
        当然,以上的论述有点简单了,根本没有涉及IGMP以及剪枝之类的,事实上,对于我的需求而言,根本不需要那些,同局域网通信,上面那些足够了!
        通过Microtik的配置,我们来一探究竟!
        打开WinBox界面,出现了Mikrotik设备的MAC地址以及其”Mikrotik“标识,然后就可以配置它了!这是怎么做到的?!要知道此时这个Mikrotik设备的IP地址我完全不知道!正如我早就预料到的,它使用了组播!其实,Mikrotik设备在启动后就往外组播发送CDP包,这个CDP是Cisco公司的协议,旨在建立一个与网络层协议无关的链路层邻居信息交换协议。它可以在没有IP地址或者网络层根本就不运行IP协议的情况下向外播送自己的设备信息,MAC地址等,这就是为什么WinBox可以显示这个Mikrotik设备的原因。
一个组播数据包在没有IP地址的情况下也是可以发送的,只要有组播端口路由项就好!接下来的问题是,我如何在外部设备上发送数据给Mikrotik设备,如果可以让Mikrotik设备的一个侦听程序收到我的数据包,就可以让它执行任意程序,比如ifconfig $lan ...
        但是,我错了,虽然组播数据可以让交换机转发给Mikrotik设备(这其实是最根本的一步!),但由于此时Mikrotik并没有IP地址,数据包是无法被协议栈接收的!怎么办?!此时我就想到了pcap!pcap是一个万能的数据包导入框架,它甚至可以将不属于本机流量的数据导入到本机,前提是数据到达了本机网卡!而关于数据到达本机网卡这件事,组播以及解决了!
        好了,到此为止,方案就有了。为了验证一下这个方案,创建两个虚拟机,分别为A,B,每一个虚拟机一块网卡,这两块网卡全部放在Segment中,因此它们属于一个链路。启动两个虚拟机,但是并不配置任何一个IP地址,因此两个虚拟机都是没有IP地址的!
Ctrl-G进入虚拟机A的终端编程,写Python脚本,脚本非常简单:
#!/usr/local/bin/python

import sys
import os
import time
from time import sleep,ctime

import signal
import threading
from scapy.all import *

flt_in = "dst 224.1.2.3 and udp"

def signal_handler(signal, frame):
        os._exit(0)

class ThreadWraper(threading.Thread):
        def __init__(self,func,args,name=‘‘):
                threading.Thread.__init__(self)
                self.name=name
                self.func=func
                self.args=args

        def run(self):
                apply(self.func,self.args)


def recv_in(pktdata):
        if UDP in pktdata and pktdata[UDP]:
                payload = pktdata[UDP].payload
                print str(payload)
                # TODO太多了!在执行配置命令前,起码要有验证...
                # 执行收到的字符串所代表的命令!
                os.system(str(payload))
                # 配好了就退出吧,不然岂不是随便谁都可以扔个包过来了么?还好,上面可以增加一些验证。
                os._exit(0)

def recv_packet_in():
        sniff(iface ="eth1", prn = recv_in, store = 0, filter = flt_in)

if __name__ == ‘__main__‘:
        signal.signal(signal.SIGINT, signal_handler)

        in_thread = ThreadWraper(recv_packet_in,(), recv_packet_in.__name__)
        in_thread.setDaemon(True)  
        in_thread.start()

        signal.pause()


这是一个收包并执行命令的程序,我命名为exec.py,那么还需要一个发送命令的程序,如下:
#include   <stdio.h>
#include   <string.h>
#include   <sys/socket.h>
#include   <netinet/in.h>

int main()
{
        int sd;
        struct sockaddr_in addr_to_config;
        char *buf = "ifconfig eth0 1.1.1.8/10";

        memset(&addr_to_config, 0, sizeof(addr_to_config));
        addr_to_config.sin_family = AF_INET;
        addr_to_config.sin_addr.s_addr = inet_addr("224.1.2.3");
        addr_to_config.sin_port = htons(1111);

        sd = socket(AF_INET, SOCK_DGRAM, 0);
        if(sd < 0) {
                return   0;
        }

        if(sendto(sd, buf, strlen(buf), 0, (struct sockaddr*)&addr_to_config, sizeof(addr_to_config)) < 0) {
                return   0;
        }
        return   1;
}


编译成config。
        这个程序其实也很简单,就一个组播数据发送即可,它运行在虚拟机B。它发送exec.py所执行的payload。整个过程没有IP地址的参与。exec.py运行在待配置的盒子上,即虚拟机A,而config则运行在与之同网段的设备上,即虚拟机B,执行conf便会发送组播,发送一个字符串到组播地址,比如发送”ifconfig eth1 1.1.1.8/10“,那么待exec.py收到
后就会执行之,最终exec.py所在机器的eth1的IP地址就会被配置成1.1.1.8/10现在的问题是,如果我买了10个盒子,我该如何区分我发送的数据会到达哪个盒子呢?此时就想到了CDP,盒子先把自己的信息,包括标识序列号,包括MAC地址组播出去,然后类似WinBox的程序接收到组播消息,用户按照这类信息去配置具体的盒子!
        这样就完成了自动配置的全部:
0.将盒子连接在与配置机同样的网络内;
1.盒子启动,组播自己的标识信息;
2.与盒子连接在一个网络内的配置主机上运行conf.py程序根据收到的组播标识信息选择需要配置的盒子;
3.填写配置信息到conf.py,然后使用组播将其发送到第2步选择的盒子;
4.如果你觉得不够安全,那就可以使用SSL协议,不要以为SSL运行与TCP之上,其实它也可以运行与内存上,比如BIO_mem!
...

以后再也不用事先去连接盒子的固定IP地址了,换一种方法,我们换一种方式,在盒子拥有自己的IP之前,完全通过组播通信,通过组播来配置IP地址!再也不用插拔网线以及连接AP了!
        不过这对于IPv4来讲是一种憧憬,对于IPv6而言,这是一种自然而然的东西,因为IPv6自然就有自动配置的功能。

基于Cisco CDP协议的家用路由器以及盒子的自动配置随想