netdriver.py 9.95 KB
Newer Older
1
""" CIRCLE driver for Open vSwitch. """
2 3 4
import subprocess
import logging

Guba Sándor committed
5
from netcelery import celery
6 7
from os import getenv
from vm import VMNetwork
8
from vmcelery import native_ovs
9 10 11 12 13
driver = getenv("HYPERVISOR_TYPE", "test")


@celery.task
def create(network):
14 15
    """ Create a network port. """
    port_create(VMNetwork.deserialize(network))
16 17 18 19


@celery.task
def delete(network):
20 21
    """ Delete a network port. """
    port_delete(VMNetwork.deserialize(network))
22 23 24


def add_tuntap_interface(if_name):
25
    """ For testing purpose only adding tuntap interface. """
26 27 28 29
    subprocess.call(['sudo', 'ip', 'tuntap', 'add', 'mode', 'tap', if_name])


def del_tuntap_interface(if_name):
30
    """ For testing purpose only deleting tuntap interface. """
31
    subprocess.call(['sudo', 'ip', 'tuntap', 'del', 'mode', 'tap', if_name])
Guba Sándor committed
32

33

34
def ovs_command_execute(command):
35 36
    """ Execute OpenVSwitch commands.

37
    command -   List of strings
38 39 40
    return  -   Command output

    """
41 42 43 44 45 46 47
    command = ['sudo', 'ovs-vsctl'] + command
    return_val = subprocess.call(command)
    logging.info('OVS command: %s executed.', command)
    return return_val


def ofctl_command_execute(command):
48 49
    """ Execute OpenVSwitch flow commands.

50
    command -   List of strings
51 52 53
    return  -   Command output

    """
54 55 56 57 58 59
    command = ['sudo', 'ovs-ofctl'] + command
    return_val = subprocess.call(command)
    logging.info('OVS flow command: %s executed.', command)
    return return_val


60 61 62 63 64 65 66 67 68
def build_flow_rule(
        in_port=None,
        dl_src=None,
        protocol=None,
        nw_src=None,
        ipv6_src=None,
        tp_dst=None,
        priority=None,
        actions=None):
69 70 71
    """
    Generate flow rule from the parameters.

72 73 74 75 76 77 78 79
    in_port     - Interface flow-port number
    dl_src      - Source mac addsress (virtual interface)
    protocol    - Protocol for the rule like ip,ipv6,arp,udp,tcp
    nw_src      - Source network IP(v4)
    ipv6_src    - Source network IP(v6)
    tp_dst      - Destination port
    priority    - Rule priority
    actions     - Action for the matching rule
80 81 82 83

    return - Open vSwitch compatible flow rule.

    """
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
    flow_rule = ""
    if in_port is None:
        raise AttributeError("Parameter in_port is mandantory")
    parameters = [('in_port=%s', in_port),
                  ('dl_src=%s', dl_src),
                  ('%s', protocol),
                  ('nw_src=%s', nw_src),
                  ('ipv6_src=%s', ipv6_src),
                  ('tp_dst=%s', tp_dst),
                  ('priority=%s', priority),
                  ('actions=%s', actions)]
    # Checking for values if not None making up rule list
    rule = [p1 % p2 for (p1, p2) in parameters if p2 is not None]
    # Generate rule string with comas, except the last item
    for i in rule[:-1]:
        flow_rule += i + ","
100
    flow_rule += rule[-1]
101 102 103 104
    return flow_rule


def set_port_vlan(network_name, vlan):
105 106
    """ Setting vlan for interface named net_name. """

107 108 109 110 111
    cmd_list = ['set', 'Port', network_name, 'tag=' + str(vlan)]
    ovs_command_execute(cmd_list)


def add_port_to_bridge(network_name, bridge):
112
    """ Add bridge to network_name. """
113 114
    cmd_list = ['add-port', bridge, network_name]
    ovs_command_execute(cmd_list)
Guba Sándor committed
115 116


117
def del_port_from_bridge(network_name):
118
    """ Delete network_name port. """
119 120 121
    ovs_command_execute(['del-port', network_name])


122 123 124
def mac_filter(network, port_number, remove=False):
    """ Apply/Remove mac filtering rule for network. """
    if not remove:
125 126 127 128 129 130 131 132
        flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac,
                                   priority="40000", actions="normal")
        ofctl_command_execute(["add-flow", network.bridge, flow_cmd])
    else:
        flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac)
        ofctl_command_execute(["del-flows", network.bridge, flow_cmd])


133 134 135
def ban_dhcp_server(network, port_number, remove=False):
    """ Apply/Remove dhcp-server ban rule to network. """
    if not remove:
136 137 138 139 140 141 142 143 144 145
        flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac,
                                   protocol="udp", tp_dst="68",
                                   priority="43000", actions="drop")
        ofctl_command_execute(["add-flow", network.bridge, flow_cmd])
    else:
        flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac,
                                   protocol="udp", tp_dst="68")
        ofctl_command_execute(["del-flows", network.bridge, flow_cmd])


146 147 148
def ipv4_filter(network, port_number, remove=False):
    """ Apply/Remove ipv4 filter rule to network.  """
    if not remove:
149 150 151 152 153 154 155 156 157 158
        flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac,
                                   protocol="ip", nw_src=network.ipv4,
                                   priority=42000, actions="normal")
        ofctl_command_execute(["add-flow", network.bridge, flow_cmd])
    else:
        flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac,
                                   protocol="ip", nw_src=network.ipv4)
        ofctl_command_execute(["del-flows", network.bridge, flow_cmd])


159 160 161
def ipv6_filter(network, port_number, remove=False):
    """ Apply/Remove ipv6 filter rule to network.  """
    if not remove:
162 163 164 165 166 167 168 169 170 171
        flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac,
                                   protocol="ipv6", ipv6_src=network.ipv6,
                                   priority=42000, actions="normal")
        ofctl_command_execute(["add-flow", network.bridge, flow_cmd])
    else:
        flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac,
                                   protocol="ipv6", ipv6_src=network.ipv6)
        ofctl_command_execute(["del-flows", network.bridge, flow_cmd])


172 173 174
def arp_filter(network, port_number, remove=False):
    """ Apply/Remove arp filter rule to network. """
    if not remove:
175 176 177 178 179 180 181 182 183 184
        flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac,
                                   protocol="arp", nw_src=network.ipv4,
                                   priority=41000, actions="normal")
        ofctl_command_execute(["add-flow", network.bridge, flow_cmd])
    else:
        flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac,
                                   protocol="arp", nw_src=network.ipv4)
        ofctl_command_execute(["del-flows", network.bridge, flow_cmd])


185 186 187
def enable_dhcp_client(network, port_number, remove=False):
    """ Apply/Remove allow dhcp-client rule to network. """
    if not remove:
188 189 190 191 192 193 194 195 196 197
        flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac,
                                   protocol="udp", tp_dst="67",
                                   priority="40000", actions="normal")
        ofctl_command_execute(["add-flow", network.bridge, flow_cmd])
    else:
        flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac,
                                   protocol="udp", tp_dst="67")
        ofctl_command_execute(["del-flows", network.bridge, flow_cmd])


198 199 200
def disable_all_not_allowed_trafic(network, port_number, remove=False):
    """ Apply/Remove explicit deny all not allowed network. """
    if not remove:
201
        flow_cmd = build_flow_rule(in_port=port_number,
202
                                   priority="30000", actions="drop")
203 204 205 206
        ofctl_command_execute(["add-flow", network.bridge, flow_cmd])
    else:
        flow_cmd = build_flow_rule(in_port=port_number)
        ofctl_command_execute(["del-flows", network.bridge, flow_cmd])
Guba Sándor committed
207 208


209
def port_create(network):
210
    """ Adding port to bridge apply rules and pull up interface. """
211 212 213 214
    # For testing purpose create tuntap iface
    if driver == "test":
        add_tuntap_interface(network.name)

215
    if not native_ovs:
216 217 218 219
        try:
            del_port_from_bridge(network.name)
        except:
            pass
220 221 222 223
        # Create the port for virtual network
        add_port_to_bridge(network.name, network.bridge)
        # Set VLAN parameter for tap interface
        set_port_vlan(network.name, network.vlan)
224

225 226 227
    # Clear all old rules
    clear_port_rules(network)

228
    # Getting network FlowPortNumber
229
    port_number = get_fport_for_network(network)
230 231

    # Set Flow rules to avoid mac or IP spoofing
232
    if network.managed:
233
        # Allow traffic from fource MAC and IP
234
        ban_dhcp_server(network, port_number)
Guba Sándor committed
235 236 237 238
        if network.ipv4 != "None":
            ipv4_filter(network, port_number)
        if network.ipv6 != "None":
            ipv6_filter(network, port_number)
239 240
        arp_filter(network, port_number)
        enable_dhcp_client(network, port_number)
241 242 243 244 245 246
    else:
        # Allow all traffic from source MAC address
        mac_filter(network, port_number)
    # Explicit deny all other traffic
    disable_all_not_allowed_trafic(network, port_number)
    pull_up_interface(network)
247 248 249


def port_delete(network):
250
    """ Remove port from bridge and remove rules from flow database. """
Guba Sándor committed
251
    # Clear all port rules
252 253 254
    try:
        clear_port_rules(network)
    except:
Bach Dániel committed
255
        pass  # Missing port (deleted already)
256

257 258 259
    if not native_ovs:
        # Delete port
        del_port_from_bridge(network.name)
260

261 262 263 264
    # For testing purpose dele tuntap iface
    if driver == "test":
        del_tuntap_interface(network.name)

265

266 267 268 269 270 271 272
def clear_port_rules(network):
    """ Clear all rules for a port. """
    port_number = get_fport_for_network(network)
    flow_cmd = build_flow_rule(in_port=port_number)
    ofctl_command_execute(["del-flows", network.bridge, flow_cmd])


273
def pull_up_interface(network):
274 275 276 277 278
    """ Pull up interface named network.

    return command output

    """
279
    command = ['sudo', 'ip', 'link', 'set', 'up', network.name]
280 281 282 283 284
    return_val = subprocess.call(command)
    logging.info('IP command: %s executed.', command)
    return return_val


285
def get_fport_for_network(network):
286 287 288 289 290 291 292
    """ Return the OpenFlow port number for a given network.

    Example: ovs-vsctl get Interface vm-88 ofport

    return stripped output string

    """
293
    output = subprocess.check_output(
294
        ['sudo', 'ovs-vsctl', 'get', 'Interface', network.name, 'ofport'])
295
    return str(output).strip()