9bd1db420e
Scapy packet filtering is slow and blocking. Pcap filtering can be used in same thread, this allows us to use higher timeout for dhcp offer waiting Setup proper logger for dhcp_checker app. ERROR level logs will be redirected to stderr DEBUG level to /var/log/dhcp_checker.log Change-Id: I7d9a0f1b8e3082637bc2474302fc0f7c17e0adb8 Closes-Bug: #1247284
147 lines
4.7 KiB
Python
147 lines
4.7 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 time
|
|
|
|
from scapy import config as scapy_config
|
|
|
|
scapy_config.use_pcap = True
|
|
|
|
logging.getLogger('scapy.runtime').setLevel(logging.CRITICAL)
|
|
|
|
from dhcp_checker import utils
|
|
import pcap
|
|
from scapy import all as scapy
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
def _get_dhcp_discover_message(iface):
|
|
|
|
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))
|
|
|
|
return dhcp_discover
|
|
|
|
|
|
@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_discover = _get_dhcp_discover_message(iface)
|
|
ans, unans = scapy.srp(dhcp_discover, multi=True,
|
|
nofilter=1, timeout=timeout, verbose=0)
|
|
|
|
return ans
|
|
|
|
|
|
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'])
|
|
"""
|
|
config = {}
|
|
for iface in ifaces:
|
|
config[iface] = ()
|
|
return check_dhcp_with_vlans(config, timeout=timeout, repeat=repeat)
|
|
|
|
|
|
def send_dhcp_discover(iface):
|
|
dhcp_discover = _get_dhcp_discover_message(iface)
|
|
scapy.sendp(dhcp_discover, iface=iface, verbose=0)
|
|
|
|
|
|
def make_listeners(ifaces):
|
|
listeners = []
|
|
for iface in ifaces:
|
|
try:
|
|
listener = pcap.pcap(iface)
|
|
listener.setfilter('dst port 68')
|
|
listeners.append(listener)
|
|
except Exception:
|
|
LOG.warning(
|
|
'Spawning listener for {iface} failed.'.format(iface=iface))
|
|
return listeners
|
|
|
|
|
|
@utils.filter_duplicated_results
|
|
def check_dhcp_with_vlans(config, timeout=5, repeat=2):
|
|
"""Provide config of {iface: [vlans..]} pairs
|
|
@config - {'eth0': (100, 101), 'eth1': (100, 102)}
|
|
"""
|
|
# vifaces - list of pairs ('eth0', ['eth0.100', 'eth0.101'])
|
|
with utils.VlansContext(config) as vifaces:
|
|
ifaces, vlans = zip(*vifaces)
|
|
listeners = make_listeners(ifaces)
|
|
|
|
for i in utils.filtered_ifaces(itertools.chain(ifaces, *vlans)):
|
|
send_dhcp_discover(i)
|
|
|
|
time.sleep(timeout)
|
|
|
|
for l in listeners:
|
|
for pkt in l.readpkts():
|
|
yield utils.format_answer(scapy.Ether(pkt[1]), l.name)
|
|
|
|
|
|
@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
|