fuel-web/dhcp-checker/dhcp_checker/api.py

116 lines
4.1 KiB
Python

# Copyright 2013 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import itertools
import logging
import multiprocessing
logging.getLogger('scapy.runtime').setLevel(logging.ERROR)
from dhcp_checker import utils
from scapy import all as scapy
CONCURRENCY_LIMIT = 10
@utils.multiproc_map
@utils.single_format
def check_dhcp_on_eth(iface, timeout):
"""Check if there is roque dhcp server in network on given iface
@iface - name of the ethernet interface
@timeout - scapy timeout for waiting on response
>>> check_dhcp_on_eth('eth1')
"""
scapy.conf.iface = iface
scapy.conf.checkIPaddr = False
dhcp_options = [("message-type", "discover"),
("param_req_list", utils.format_options(
[1, 2, 3, 4, 5, 6,
11, 12, 13, 15, 16, 17, 18, 22, 23,
28, 40, 41, 42, 43, 50, 51, 54, 58, 59, 60, 66, 67])),
"end"]
fam, hw = scapy.get_if_raw_hwaddr(iface)
dhcp_discover = (
scapy.Ether(src=hw, dst="ff:ff:ff:ff:ff:ff") /
scapy.IP(src="0.0.0.0", dst="255.255.255.255") /
scapy.UDP(sport=68, dport=67) /
scapy.BOOTP(chaddr=hw) /
scapy.DHCP(options=dhcp_options))
ans, unans = scapy.srp(dhcp_discover, multi=True,
nofilter=1, timeout=timeout, verbose=0)
return ans
@utils.filter_duplicated_results
def check_dhcp(ifaces, timeout=5, repeat=2):
"""Given list of ifaces. Process them in separate processes
@ifaces - lsit of ifaces
@timeout - timeout for scapy to wait for response
@repeat - number of packets sended
>>> check_dhcp(['eth1', 'eth2'])
"""
ifaces_filtered = list(utils.filtered_ifaces(ifaces))
if not ifaces_filtered:
raise EnvironmentError("No valid interfaces provided.")
concurrency_limit = (CONCURRENCY_LIMIT
if len(ifaces_filtered) > CONCURRENCY_LIMIT
else len(ifaces_filtered))
pool = multiprocessing.Pool(concurrency_limit)
return itertools.chain(*pool.map(check_dhcp_on_eth, (
(iface, timeout) for iface in ifaces_filtered * repeat)))
def check_dhcp_with_vlans(config, timeout=5, repeat=2):
"""Provide config of {iface: [vlans..]} pairs
@config - {'eth0': (100, 101), 'eth1': (100, 102)}
@ifaces - string : eth0, eth1
@vlans - iterable (100, 101, 102)
"""
with utils.VlansContext(config) as vifaces:
return check_dhcp(list(vifaces), timeout=timeout, repeat=repeat)
@utils.single_format
def check_dhcp_request(iface, server, range_start, range_end, timeout=5):
"""Provide interface, server endpoint and pool of ip adresses
Should be used after offer received
>>> check_dhcp_request('eth1','10.10.0.5','10.10.0.10','10.10.0.15')
"""
scapy.conf.iface = iface
scapy.conf.checkIPaddr = False
fam, hw = scapy.get_if_raw_hwaddr(iface)
ip_address = next(utils.pick_ip(range_start, range_end))
# note lxc dhcp server does not respond to unicast
dhcp_request = (scapy.Ether(src=hw, dst="ff:ff:ff:ff:ff:ff") /
scapy.IP(src="0.0.0.0", dst="255.255.255.255") /
scapy.UDP(sport=68, dport=67) /
scapy.BOOTP(chaddr=hw) /
scapy.DHCP(options=[("message-type", "request"),
("server_id", server),
("requested_addr", ip_address),
"end"]))
ans, unans = scapy.srp(dhcp_request, nofilter=1, multi=True,
timeout=timeout, verbose=0)
return ans