# # A router implementation in Trema # # Copyright (C) 2013 NEC Corporation # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # require 'arp-table' require 'interface' require 'routing-table' class SimpleRouter < Controller def start load 'simple_router.conf' @interfaces = Interfaces.new($interface) @arp_table = ARPTable.new @routing_table = RoutingTable.new($route) end def packet_in(dpid, message) return unless to_me?(message) if message.arp_request? handle_arp_request dpid, message elsif message.arp_reply? handle_arp_reply message elsif message.ipv4? handle_ipv4 dpid, message else # noop. end end private def to_me?(message) return true if message.macda.broadcast? interface = @interfaces.find_by_port(message.in_port) if interface && interface.has?(message.macda) return true end end def handle_arp_request(dpid, message) port = message.in_port daddr = message.arp_tpa interface = @interfaces.find_by_port_and_ipaddr(port, daddr) if interface arp_reply = create_arp_reply_from(message, interface.hwaddr) packet_out dpid, arp_reply, SendOutPort.new(interface.port) end end def handle_arp_reply(message) @arp_table.update message.in_port, message.arp_spa, message.arp_sha end def handle_ipv4(dpid, message) if should_forward?(message) forward dpid, message elsif message.icmpv4_echo_request? handle_icmpv4_echo_request dpid, message else # noop. end end def should_forward?(message) not @interfaces.find_by_ipaddr(message.ipv4_daddr) end def handle_icmpv4_echo_request(dpid, message) interface = @interfaces.find_by_port(message.in_port) saddr = message.ipv4_saddr arp_entry = @arp_table.lookup(saddr) if arp_entry icmpv4_reply = create_icmpv4_reply(arp_entry, interface, message) packet_out dpid, icmpv4_reply, SendOutPort.new(interface.port) else handle_unresolved_packet dpid, message, interface, saddr end end def forward(dpid, message) next_hop = resolve_next_hop(message.ipv4_daddr) interface = @interfaces.find_by_prefix(next_hop) unless interface || interface.port == message.in_port return end arp_entry = @arp_table.lookup(next_hop) if arp_entry macsa = interface.hwaddr macda = arp_entry.hwaddr action = create_action_from(macsa, macda, interface.port) flow_mod dpid, message, action packet_out dpid, message.data, action else handle_unresolved_packet dpid, message, interface, next_hop end end def resolve_next_hop(daddr) interface = @interfaces.find_by_prefix(daddr.value) if interface daddr else @routing_table.lookup(daddr.value) end end def flow_mod(dpid, message, action) send_flow_mod_add( dpid, :match => ExactMatch.from(message), :actions => action ) end def packet_out(dpid, packet, action) send_packet_out( dpid, :data => packet, :actions => action ) end def handle_unresolved_packet(dpid, message, interface, ipaddr) arp_request = create_arp_request_from(interface, ipaddr) packet_out dpid, arp_request, SendOutPort.new(interface.port) end def create_action_from(macsa, macda, port) [ SetEthSrcAddr.new(macsa), SetEthDstAddr.new(macda), SendOutPort.new(port) ] end def create_arp_request_from(interface, addr) Pio::Arp::Request.new( :source_mac => interface.hwaddr, :sender_protocol_address => interface.ipaddr, :target_protocol_address => addr ).to_binary end def create_arp_reply_from(message, replyaddr) Pio::Arp::Reply.new( :source_mac => replyaddr, :destination_mac => message.macsa, :sender_protocol_address => message.arp_tpa, :target_protocol_address => message.arp_spa ).to_binary end def create_icmpv4_reply(entry, interface, message) request = Pio::Icmp.read(message.data) Pio::Icmp::Reply.new( :destination_mac => entry.hwaddr, :source_mac => interface.hwaddr, :ip_source_address => message.ipv4_daddr, :ip_destination_address => message.ipv4_saddr, :icmp_identifier => request.icmp_identifier, :icmp_sequence_number => request.icmp_sequence_number, :echo_data => request.echo_data ).to_binary end end ### Local variables: ### mode: Ruby ### coding: utf-8 ### indent-tabs-mode: nil ### End: