netdriver.py 11.2 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
def build_flow_rule(
        in_port=None,
        dl_src=None,
        protocol=None,
        nw_src=None,
        ipv6_src=None,
66 67
        icmp_type=None,
        nd_target=None,
68 69 70
        tp_dst=None,
        priority=None,
        actions=None):
71 72 73
    """
    Generate flow rule from the parameters.

74 75 76 77 78
    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)
79 80
    icmp_type   - ICMP/ICMPv6 type
    nd_target   - IPv6 Neighbor Discovery target IP(v6)
81 82 83
    tp_dst      - Destination port
    priority    - Rule priority
    actions     - Action for the matching rule
84 85 86 87

    return - Open vSwitch compatible flow rule.

    """
88 89 90 91 92 93 94 95
    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),
96 97
                  ('icmp_type=%s', icmp_type),
                  ('nd_target=%s', nd_target),
98 99 100 101 102 103 104 105
                  ('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 + ","
106
    flow_rule += rule[-1]
107 108 109 110
    return flow_rule


def set_port_vlan(network_name, vlan):
111 112
    """ Setting vlan for interface named net_name. """

113 114 115 116 117
    cmd_list = ['set', 'Port', network_name, 'tag=' + str(vlan)]
    ovs_command_execute(cmd_list)


def add_port_to_bridge(network_name, bridge):
118
    """ Add bridge to network_name. """
119 120
    cmd_list = ['add-port', bridge, network_name]
    ovs_command_execute(cmd_list)
Guba Sándor committed
121 122


123
def del_port_from_bridge(network_name):
124
    """ Delete network_name port. """
125 126 127
    ovs_command_execute(['del-port', network_name])


128 129 130
def mac_filter(network, port_number, remove=False):
    """ Apply/Remove mac filtering rule for network. """
    if not remove:
131 132 133 134 135 136 137 138
        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])


139 140 141
def ban_dhcp_server(network, port_number, remove=False):
    """ Apply/Remove dhcp-server ban rule to network. """
    if not remove:
142 143 144 145 146 147 148 149 150 151
        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])


152 153 154
def ipv4_filter(network, port_number, remove=False):
    """ Apply/Remove ipv4 filter rule to network.  """
    if not remove:
155 156 157 158 159 160 161 162 163 164
        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])


165 166
def ipv6_filter(network, port_number, remove=False):
    """ Apply/Remove ipv6 filter rule to network.  """
167 168 169 170

    LINKLOCAL_SUBNET = "FE80::/64"
    ICMPv6_NA = "136"  # The type of IPv6 Neighbor Advertisement

171
    if not remove:
172 173 174 175 176 177 178 179 180 181
        # Enable Neighbor Advertisement from linklocal address
        # if target ip same as network.ipv6
        flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac,
                                   protocol="icmp6", ipv6_src=LINKLOCAL_SUBNET,
                                   icmp_type=ICMPv6_NA,
                                   nd_target=network.ipv6,
                                   priority=42001, actions="normal")
        ofctl_command_execute(["add-flow", network.bridge, flow_cmd])

        # Enable traffic from valid source
182 183 184 185 186 187
        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,
188 189 190
                                   protocol="icmp6", ipv6_src=LINKLOCAL_SUBNET,
                                   icmp_type=ICMPv6_NA,
                                   nd_target=network.ipv6)
191 192
        ofctl_command_execute(["del-flows", network.bridge, flow_cmd])

193
        flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac,
194 195 196 197
                                   protocol="ipv6", ipv6_src=network.ipv6)
        ofctl_command_execute(["del-flows", network.bridge, flow_cmd])


198 199 200
def arp_filter(network, port_number, remove=False):
    """ Apply/Remove arp filter rule to network. """
    if not remove:
201 202 203 204 205 206 207 208 209 210
        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])


211 212 213
def enable_dhcp_client(network, port_number, remove=False):
    """ Apply/Remove allow dhcp-client rule to network. """
    if not remove:
214 215 216 217 218 219 220 221 222 223
        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])


224 225 226
def disable_all_not_allowed_trafic(network, port_number, remove=False):
    """ Apply/Remove explicit deny all not allowed network. """
    if not remove:
227
        flow_cmd = build_flow_rule(in_port=port_number,
228
                                   priority="30000", actions="drop")
229 230 231 232
        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
233 234


235
def port_create(network):
236
    """ Adding port to bridge apply rules and pull up interface. """
237 238 239 240
    # For testing purpose create tuntap iface
    if driver == "test":
        add_tuntap_interface(network.name)

241
    if not native_ovs:
242 243 244 245
        try:
            del_port_from_bridge(network.name)
        except:
            pass
246 247 248 249
        # 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)
250

251 252 253
    # Clear all old rules
    clear_port_rules(network)

254
    # Getting network FlowPortNumber
255
    port_number = get_fport_for_network(network)
256 257

    # Set Flow rules to avoid mac or IP spoofing
258
    if network.managed:
259
        # Allow traffic from fource MAC and IP
260
        ban_dhcp_server(network, port_number)
Guba Sándor committed
261 262 263 264
        if network.ipv4 != "None":
            ipv4_filter(network, port_number)
        if network.ipv6 != "None":
            ipv6_filter(network, port_number)
265 266
        arp_filter(network, port_number)
        enable_dhcp_client(network, port_number)
267 268 269 270 271 272
    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)
273 274 275


def port_delete(network):
276
    """ Remove port from bridge and remove rules from flow database. """
Guba Sándor committed
277
    # Clear all port rules
278 279 280
    try:
        clear_port_rules(network)
    except:
Bach Dániel committed
281
        pass  # Missing port (deleted already)
282

283 284 285
    if not native_ovs:
        # Delete port
        del_port_from_bridge(network.name)
286

287 288 289 290
    # For testing purpose dele tuntap iface
    if driver == "test":
        del_tuntap_interface(network.name)

291

292 293 294 295 296 297 298
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])


299
def pull_up_interface(network):
300 301 302 303 304
    """ Pull up interface named network.

    return command output

    """
305
    command = ['sudo', 'ip', 'link', 'set', 'up', network.name]
306 307 308 309 310
    return_val = subprocess.call(command)
    logging.info('IP command: %s executed.', command)
    return return_val


311
def get_fport_for_network(network):
312 313 314 315 316 317 318
    """ Return the OpenFlow port number for a given network.

    Example: ovs-vsctl get Interface vm-88 ofport

    return stripped output string

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