OpenvSwitch.rb

ovswitch functions - Sébastien LEFEUVRE, 05/27/2015 10:43 AM

Download (6.1 KB)

 
1
# -------------------------------------------------------------------------- #
2
# Copyright 2002-2015, OpenNebula Project (OpenNebula.org), C12G Labs        #
3
#                                                                            #
4
# Licensed under the Apache License, Version 2.0 (the "License"); you may    #
5
# not use this file except in compliance with the License. You may obtain    #
6
# a copy of the License at                                                   #
7
#                                                                            #
8
# http://www.apache.org/licenses/LICENSE-2.0                                 #
9
#                                                                            #
10
# Unless required by applicable law or agreed to in writing, software        #
11
# distributed under the License is distributed on an "AS IS" BASIS,          #
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   #
13
# See the License for the specific language governing permissions and        #
14
# limitations under the License.                                             #
15
#--------------------------------------------------------------------------- #
16

    
17
require 'OpenNebulaNetwork'
18

    
19
class OpenvSwitchVLAN < OpenNebulaNetwork
20
    DRIVER = "ovswitch"
21

    
22
    FIREWALL_PARAMS =  [:black_ports_tcp,
23
                        :black_ports_udp,
24
                        :icmp]
25

    
26
    XPATH_FILTER = "TEMPLATE/NIC"
27

    
28
    def initialize(vm, deploy_id = nil, hypervisor = nil)
29
        super(vm,XPATH_FILTER,deploy_id,hypervisor)
30
        @locking = false
31

    
32
        @vm.nics.each do |nic|
33
            if nic[:bridge_ovs] && !nic[:bridge_ovs].empty?
34
                nic[:bridge] = nic[:bridge_ovs]
35
            end
36
        end
37
    end
38

    
39
    def activate(nic_id = nil)
40
        lock
41

    
42
        process do |nic|
43
            @nic = nic
44

    
45
            if (nic_id.nil? || (!nic_id.nil? && nic_id == @nic[:nic_id]))
46
     
47
              if @nic[:tap].nil?
48
                  STDERR.puts "No tap device found for nic #{@nic[:nic_id]}"
49
                  unlock
50
                  exit 1
51
              end
52

    
53
              # Apply VLAN
54
              if @nic[:vlan] == "YES"
55
                  tag_vlan
56
                  tag_trunk_vlans
57
              end
58

    
59
              # Prevent ARP Cache Poisoning
60
              arp_cache_poisoning if CONF[:arp_cache_poisoning] && @nic[:ip]
61

    
62
              # Prevent Mac-spoofing
63
              mac_spoofing
64

    
65
              # Apply Firewall
66
              configure_fw if FIREWALL_PARAMS & @nic.keys != []
67
            end
68
        end
69

    
70
        unlock
71

    
72
        return 0
73
    end
74

    
75
    def deactivate(nic_id = nil)
76
        lock
77

    
78
        process do |nic|
79
            @nic = nic
80

    
81
            if (nic_id.nil? || (!nic_id.nil? && nic_id == @nic[:nic_id]))
82
              # Remove flows
83
              del_flows
84
            end
85
        end
86

    
87
        unlock
88
    end
89

    
90
    def vlan
91
        if @nic[:vlan_id]
92
            return @nic[:vlan_id]
93
        else
94
            return CONF[:start_vlan] + @nic[:network_id].to_i
95
        end
96
    end
97

    
98
    def tag_vlan
99
        cmd =  "#{COMMANDS[:ovs_vsctl]} set Port #{@nic[:tap]} "
100
        cmd << "tag=#{vlan}"
101

    
102
        run cmd
103
    end
104

    
105
    def tag_trunk_vlans
106
        range = @nic[:vlan_tagged_id]
107
        if range? range
108
            ovs_vsctl_cmd = "#{COMMANDS[:ovs_vsctl]} set Port #{@nic[:tap]}"
109

    
110
            cmd = "#{ovs_vsctl_cmd} trunks=#{range}"
111
            run cmd
112

    
113
            cmd = "#{ovs_vsctl_cmd} vlan_mode=native-untagged"
114
            run cmd
115
        end
116
    end
117

    
118
    def arp_cache_poisoning
119
        add_flow("in_port=#{port},arp,dl_src=#{@nic[:mac]}",:drop,45000)
120
        add_flow("in_port=#{port},arp,dl_src=#{@nic[:mac]},nw_src=#{@nic[:ip]}",:normal,46000)
121
    end
122

    
123
    def mac_spoofing
124
        add_flow("in_port=#{port},dl_src=#{@nic[:mac]}",:normal,40000)
125
        add_flow("in_port=#{port}",:drop,39000)
126
    end
127

    
128
    def configure_fw
129
        # TCP
130
        if range = @nic[:black_ports_tcp]
131
            if range? range
132
                range.split(",").each do |p|
133
                    base_rule = "tcp,dl_dst=#{@nic[:mac]},tp_dst=#{p}"
134
                    base_rule << ",dl_vlan=#{vlan}" if @nic[:vlan] == "YES"
135

    
136
                    add_flow(base_rule,:drop)
137
                end
138
            end
139
        end
140

    
141
        # UDP
142
        if range = @nic[:black_ports_udp]
143
            if range? range
144
                range.split(",").each do |p|
145
                    base_rule = "udp,dl_dst=#{@nic[:mac]},tp_dst=#{p}"
146
                    base_rule << ",dl_vlan=#{vlan}" if @nic[:vlan] == "YES"
147

    
148
                    add_flow(base_rule,:drop)
149
                end
150
            end
151
        end
152

    
153
        # ICMP
154
        if @nic[:icmp]
155
            if %w(no drop).include? @nic[:icmp].downcase
156
                base_rule = "icmp,dl_dst=#{@nic[:mac]}"
157
                base_rule << ",dl_vlan=#{vlan}" if @nic[:vlan] == "YES"
158

    
159
                add_flow(base_rule,:drop)
160
            end
161
        end
162
    end
163

    
164
    def del_flows
165
        in_port = ""
166

    
167
        dump_flows = "#{COMMANDS[:ovs_ofctl]} dump-flows #{@nic[:bridge]}"
168
        `#{dump_flows}`.lines do |flow|
169
            next unless flow.match("#{@nic[:mac]}")
170
            flow = flow.split.select{|e| e.match(@nic[:mac])}.first
171
            if in_port.empty? and (m = flow.match(/in_port=(\d+)/))
172
                in_port = m[1]
173
            end
174
            del_flow flow
175
        end
176

    
177
        del_flow "in_port=#{in_port}" if !in_port.empty?
178
    end
179

    
180
    def add_flow(filter,action,priority=nil)
181
        priority = (priority.to_s.empty? ? "" : "priority=#{priority},")
182

    
183
        run "#{COMMANDS[:ovs_ofctl]} add-flow " <<
184
            "#{@nic[:bridge]} #{filter},#{priority}actions=#{action}"
185
    end
186

    
187
    def del_flow(filter)
188
        filter.gsub!(/priority=(\d+)/,"")
189
        run "#{COMMANDS[:ovs_ofctl]} del-flows " <<
190
            "#{@nic[:bridge]} #{filter}"
191
    end
192

    
193
    def run(cmd)
194
        OpenNebula.exec_and_log(cmd)
195
    end
196

    
197
    def port
198
        return @nic[:port] if @nic[:port]
199

    
200
        dump_ports = `#{COMMANDS[:ovs_ofctl]} \
201
                      dump-ports #{@nic[:bridge]} #{@nic[:tap]}`
202

    
203
        @nic[:port] = dump_ports.scan(/^\s*port\s*(\d+):/).flatten.first
204
    end
205

    
206
    def range?(range)
207
        !range.to_s.match(/^\d+(,\d+)*$/).nil?
208
    end
209
end