NEUTRON中通过FLOW规则防范ARP欺骗

之前的文章小秦整理过Neutron中的flow规则。实际上,还有一张flow table是用于做ARP欺骗的防范的。

首先先来说下什么是ARP欺骗(也叫ARP毒化、ARP spoofing)。原理很简单:

主机A想要知道IP为10.1.2.3的主机B的MAC地址,所以发送了一个ARP请求包,正常情况下主机B收到这个请求后就会返回一个ARP响应,告诉A我的IP是10.1.2.3,我的MAC是XXX。然后A和B就可以通信了。A的这个ARP请求是广播的,所以同一个VLAN下的主机C也能收到,但一般C发现请求的IP地址不是它的时候,它会忽略这个ARP请求报文。

但是,如果C想要窃取A和B的通信的话咋办呢?很简单,C不停的向网络中广播ARP响应报文,报文内容是:我的IP是10.1.2.3,我的MAC是YYY。此时当A发送一个ARP请求去请求10.1.2.3的MAC的时候,其立刻就能收到C的ARP响应报文(B之后也会响应,但已经晚了)。A收到C的报文后,会把C认作为B,同时由于ARP有cache的原因,很长一段时间内都会认为C就是B。这个时候A发送给B的报文就都会被C所接收了(其实本来C也能接收,只是ARP欺骗后B收不到了)。C接收后或许会做些手脚,然后再把报文转发给B。

那么Neutron是如何防范的呢?代码如下:

    @staticmethod
    def setup_arp_spoofing_protection(bridge, vif, port_details):
        # clear any previous flows related to this port in our ARP table
        bridge.delete_flows(table=constants.LOCAL_SWITCHING,
                            in_port=vif.ofport, proto='arp')
        bridge.delete_flows(table=constants.ARP_SPOOF_TABLE,
                            in_port=vif.ofport)
        if not port_details.get('port_security_enabled', True):
            LOG.info(_LI("Skipping ARP spoofing rules for port '%s' because "
                         "it has port security disabled"), vif.port_name)
            return
        # all of the rules here are based on 'in_port' match criteria
        # so their cleanup will be handled by 'update_stale_ofport_rules'

        # collect all of the addresses and cidrs that belong to the port
        addresses = [f['ip_address'] for f in port_details['fixed_ips']]
        if port_details.get('allowed_address_pairs'):
            addresses += [p['ip_address']
                          for p in port_details['allowed_address_pairs']]

        # allow ARP replies as long as they match addresses that actually
        # belong to the port.
        for ip in addresses:
            if netaddr.IPNetwork(ip).version != 4:
                continue
            bridge.add_flow(
                table=constants.ARP_SPOOF_TABLE, priority=2,
                proto='arp', arp_op=constants.ARP_REPLY, arp_spa=ip,
                in_port=vif.ofport, actions="NORMAL")

        # drop any ARP replies in this table that aren't explicitly allowed
        bridge.add_flow(
            table=constants.ARP_SPOOF_TABLE, priority=1, proto='arp',
            arp_op=constants.ARP_REPLY, actions="DROP")

        # Now that the rules are ready, direct ARP traffic from the port into
        # the anti-spoof table.
        # This strategy fails gracefully because OVS versions that can't match
        # on ARP headers will just process traffic normally.
        bridge.add_flow(table=constants.LOCAL_SWITCHING,
                        priority=10, proto='arp', in_port=vif.ofport,
                        arp_op=constants.ARP_REPLY,
                        actions=("resubmit(,%s)" % constants.ARP_SPOOF_TABLE))

Neutron用到了其天然的优势:其自己知道连在br-int交换机上的每个口应该是什么IP。所以这里的逻辑很简单:我添加一个flow规则,如果某个包是ARP的响应报文,那么我要保证这个包的发送IP就是交换机上这个包的入口的在Neutron数据库中的绑定IP。这样一来如果C想要冒充B,C伪造的ARP响应报文中的IP地址一定是B的IP地址,但是这个报文到达br-int的时候,br-int发现这个报文是从C这个主机的接口上来的,但是实际上的IP却又不是C,所以会把这个包DROP掉。

但是从这里也能看到问题:如果我的业务上需要这种逻辑,咋办?打个比方,以前在学校做过刷票的软件,有些网站会限制某个IP只能刷多少次,那这个时候为了突破这个限制就要改源IP了。对于这种情况那么就歇菜了。

不过话又说回来,在Neutron中,改了源IP的包想发到外网是没啥用的啦,如果你这个主机有浮动IP,那么内网地址匹配不上,会走默认的NAT,默认的NAT总会把你的IP转成路由器的IP做NAT发出去,所以想用Neutorn中的VM来突破源IP的限制刷票,或许还得再想办法。

1 Response

  1. MatheMatrix 2015年5月11日 / 上午11:04

    16 到 18 行,加了 allowed address pair,做开始这个用来 anti snooping,现在还用于防止 arp 毒化

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*