Module: Dcmgr::NodeModules::Nat
- Includes:
- Helpers::NicHelper, Logger
- Included in:
- ServiceNetfilter
- Defined in:
- lib/dcmgr/node_modules/service_netfilter.rb
Instance Method Summary collapse
-
#arp_respond(ip, mac_addr) ⇒ Object
Returns ebtables command to respond to ARP requests for the address ip.
-
#build_nat_chains(inst_map, action = :create) ⇒ Object
Builds or deletes the chains for each vnic in an instance.
- #is_natted_ip?(ip) ⇒ Boolean
-
#nat_exceptions(inst_map) ⇒ Object
Returns the netfilter rules for destination IP addresses that will not use static nat.
-
#nat_instance(inst_map) ⇒ Object
Takes an instance and nats it.
-
#stop_arp_reply(inst_map) ⇒ Object
Similar hack to unlink_nat_chains.
-
#unlink_nat_chains(inst_map) ⇒ Object
Quick and dirty hack to unlink the nat chains before deleting them.
Methods included from Logger
create, default_logdev, included
Methods included from Helpers::NicHelper
#clean_mac, #find_nic, #is_natted?, #nic_state, #valid_nic?
Instance Method Details
#arp_respond(ip, mac_addr) ⇒ Object
Returns ebtables command to respond to ARP requests for the address ip. mac_addr is the mac address that we will reply with.
209 210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'lib/dcmgr/node_modules/service_netfilter.rb', line 209 def arp_respond(ip,mac_addr) ip = IPAddress(ip) if ip.is_a?(String) raise "Invalid IP address: #{ip}" unless ip.is_a?(IPAddress) #Get the mac address for our physical nic #nic = find_nic(@node.manifest.config.hv_ifindex) #TODO: Find a prettier way to get the mac address #mac_addr = %x{ifconfig | grep '#{nic}' | tr -s ' ' | cut -d ' ' -f5}.chomp logger.debug "Replying ARP requests for address: #{ip.address}" "ebtables -t nat -A PREROUTING -p arp --arp-ip-dst #{ip.address} --arp-opcode REQUEST -j arpreply --arpreply-mac #{mac_addr}" end |
#build_nat_chains(inst_map, action = :create) ⇒ Object
Builds or deletes the chains for each vnic in an instance. We use different chains for incoming and outgoing packets per vnic This way every packet only needs to be checked against chains that are specifically intended for it. inst_map is a map of the instance to build or delte chains for. action decides wether we will create or delete the rules. It can be either of the following:
-
:create is the default value and creates chains for inst_map
-
:delete deletes the chains for inst_map
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/dcmgr/node_modules/service_netfilter.rb', line 102 def build_nat_chains(inst_map, action = :create) actions = { :create => ['N'], :delete => ['F', 'X'] } raise ArgumentError, "#{action} is not a valid action. Valid actions are #{actions.keys.join(',')}." unless actions.keys.member?(action) raise ArgumentError, "inst_map must be a Hash." unless inst_map.is_a?(Hash) chain_cmds = [] inst_map[:instance_nics].each { |nic| ['s','d'].each { |bound| actions[action].each { |a| chain_cmds << "iptables -t nat -#{a} #{bound}_#{nic[:uuid]}" } } } chain_cmds end |
#is_natted_ip?(ip) ⇒ Boolean
223 224 225 226 227 228 |
# File 'lib/dcmgr/node_modules/service_netfilter.rb', line 223 def is_natted_ip?(ip) ip = IPAddress(ip) if ip.is_a?(String) raise ArgumentError, "Invalid IP address: #{ip}" unless ip.is_a?(IPAddress) rpc.request('hva-collector', 'is_natted_ip?', ip.address) end |
#nat_exceptions(inst_map) ⇒ Object
Returns the netfilter rules for destination IP addresses that will not use static nat. These are the IP addresses of other instances in the same security group. inst_map is a map of the instance that the rules will be defined for.
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
# File 'lib/dcmgr/node_modules/service_netfilter.rb', line 186 def nat_exceptions(inst_map) inside_exception_ips = rpc.request('hva-collector','get_group_instance_ipv4s',inst_map[:uuid]).map {|ip| IPAddress(ip)} outside_exception_ips = rpc.request('hva-collector','get_group_instance_ipv4s',inst_map[:uuid],:outside).map {|ip| IPAddress(ip)} cmds = [] inst_map[:instance_nics].each { |nic| # strict check next unless valid_nic?(nic[:uuid]) internal_ip = IPAddress(rpc.request('hva-collector', 'get_iplease_for_nic', nic[:uuid])) inside_exception_ips.each { |ex_ip| cmds << "iptables -t nat -A s_#{nic[:uuid]} -s #{internal_ip.address} -d #{ex_ip.address}/#{ex_ip.prefix} -j ACCEPT" } outside_exception_ips.each { |ex_ip| cmds << "iptables -t nat -A d_#{nic[:uuid]} -s #{internal_ip.address} -d #{ex_ip.address}/#{ex_ip.prefix} -j ACCEPT" } } cmds end |
#nat_instance(inst_map) ⇒ Object
Takes an instance and nats it. If the instance is in a network that has a nat_network mapped to it, it will receive a second ip lease for that network. This lease will then be natted to the ip the instance already had in its own network. For example if 192.168.0.0/24 is natted to 172.16.0.0/16, then an instance with ip 192.168.0.10 might be natted to ip 172.16.46.23.
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/dcmgr/node_modules/service_netfilter.rb', line 125 def nat_instance(inst_map) raise ArgumentError, "inst_map must be a Hash." unless inst_map.is_a?(Hash) nat_cmd = [] inst_map[:instance_nics].each { |nic| # strict check next unless valid_nic?(nic[:uuid]) nat_ips = rpc.request('hva-collector', 'get_nat_leases', nic[:uuid]).map {|ip| IPAddress(ip)} #Get the internal ip for this nic internal_ip = IPAddress rpc.request('hva-collector', 'get_iplease_for_nic', nic[:uuid]) inside_exception_ips = rpc.request('hva-collector','get_group_instance_ipv4s',inst_map[:uuid]).map {|ip| IPAddress(ip)} outside_exception_ips = rpc.request('hva-collector','get_group_instance_ipv4s',inst_map[:uuid],:outside).map {|ip| IPAddress(ip)} #output the commands to nat this nic and answer arp requests for its outside ip friend_ipset = nic[:uuid] + "_friend_ips" nat_ips.each { |external_ip| if @node.manifest.config.use_ipset nat_cmd << "ipset -N #{friend_ipset} iphash" inside_exception_ips.each { |ex_ip| nat_cmd << "ipset -A #{friend_ipset} #{ex_ip.address}" } # The good rules that use ipset postrouting_command = "iptables -t nat -A s_#{nic[:uuid]} -s #{internal_ip.address} -m set ! --match-set #{friend_ipset} dst" prerouting_command = "iptables -t nat -A d_#{nic[:uuid]} -d #{external_ip.address} -m set ! --match-set #{friend_ipset} src" else # The ugly rules to use in case ipset is not installed postrouting_command = "iptables -t nat -A s_#{nic[:uuid]} -s #{internal_ip.address}" prerouting_command = "iptables -t nat -A d_#{nic[:uuid]} -d #{external_ip.address}" end # Set up the proper chain jumps nat_cmd << "iptables -t nat -A PREROUTING -d #{external_ip.address} -j d_#{nic[:uuid]}" nat_cmd << "iptables -t nat -A POSTROUTING -s #{internal_ip.address} -j s_#{nic[:uuid]}" # Build the final nat rules and log any packets that traverse them nat_cmd << postrouting_command + " -j LOG --log-prefix 'Snat '" if @node.manifest.config.packet_drop_log nat_cmd << postrouting_command + " -j SNAT --to #{external_ip.address}" nat_cmd << prerouting_command + " -j LOG --log-prefix 'Dnat '" if @node.manifest.config.packet_drop_log nat_cmd << prerouting_command + " -j DNAT --to #{internal_ip.address}" logger.debug "Natting #{internal_ip.address} to #{external_ip.address}" mac = clean_mac(nic[:mac_addr]) nat_cmd << arp_respond(external_ip,mac) } } nat_cmd end |
#stop_arp_reply(inst_map) ⇒ Object
Similar hack to unlink_nat_chains. Once an instance is terminated, we no longer know which IP it had so we grep the rules by mac address and delete them by rule numer
79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/dcmgr/node_modules/service_netfilter.rb', line 79 def stop_arp_reply(inst_map) raise ArgumentError, "inst_map must be a Hash." unless inst_map.is_a?(Hash) del_cmds = [] inst_map[:instance_nics].each { |nic| mac = clean_mac(nic[:mac_addr]) rule_number = %x{ebtables -t nat -L --Ln --Lmac2 | grep #{mac} | cut -d '.' -f1} del_cmds << "ebtables -t nat -D PREROUTING #{rule_number}" unless rule_number.empty? } del_cmds end |
#unlink_nat_chains(inst_map) ⇒ Object
Quick and dirty hack to unlink the nat chains before deleting them. It would be cleaner to recall the creation method with a :delete action but NAT rules are based on IP leases and those are deleted on instance termination Therefore we use grep to get the referring rules based on vnic uuid and then delete them. Run build_nat_chains(inst_map, :delete) afterwards to delete the chains themselves
61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/dcmgr/node_modules/service_netfilter.rb', line 61 def unlink_nat_chains(inst_map) raise ArgumentError, "inst_map must be a Hash." unless inst_map.is_a?(Hash) del_cmds = [] inst_map[:instance_nics].each { |nic| post = %x{iptables -t nat -L POSTROUTING --line-numbers | grep s_#{nic[:uuid]} | tr -s ' ' | cut -d ' ' -f1}.chomp pre = %x{iptables -t nat -L PREROUTING --line-numbers | grep d_#{nic[:uuid]} | tr -s ' ' | cut -d ' ' -f1}.chomp del_cmds << "iptables -t nat -D POSTROUTING #{post}" unless post.empty? del_cmds << "iptables -t nat -D PREROUTING #{pre}" unless pre.empty? } del_cmds end |