Trema/SimpleL3Switch(2)

主要な変更点

packet_in
  def packet_in(datapath_id, message)
    if to_me?(message)
      if message.arp_request?
        handle_arp_request datapath_id, message
      elsif message.arp_reply?
        handle_arp_reply message
      elsif message.ipv4?
        handle_ipv4 datapath_id, message
      else
        # noop.
      end
    else
      # forwarding local
      handle_switched_traffic datapath_id, message
    end
  end
  • 基本的には simple router と同様。
    • 自分の持っているセグメント内でのフォワーディングする処理を追加してある。(handle_switched_traffic)
handle_arp_request
  def handle_arp_request(dpid, message)
    port = message.in_port
    daddr = message.arp_tpa
    interface = @interfaces.find_by_ipaddr(daddr)

    if interface
      arp_reply = create_arp_reply_from message, interface.hwaddr
      packet_out dpid, arp_reply, SendOutPort.new(port)
    else
      handle_switched_traffic dpid, message
    end
  end
  • ARP Request (broadcast) の場合、Simple L3Switch が持っている interface に対するリクエストの場合と、それ以外(セグメント内のどこかのホストに対するリクエスト)の場合がある。
  • 自分が持っている interface に対するリクエストであれば、そのまま応答してやれば良い(simple router の処理)
  • そうではない場合、セグメントに Flooding してやる必要がある。
handle_switched_traffic
  def handle_switched_traffic(datapath_id, message)
    if message.arp?
      @arp_table.update message.in_port, message.arp_spa, message.arp_sha
    elsif message.ipv4?
      @arp_table.update message.in_port, message.ipv4_saddr, message.macsa
    end

    arp_entry = @arp_table.lookup_by_hwaddr(message.macda)
    if arp_entry
      flow_mod datapath_id, message, SendOutPort.new(arp_entry.port)
      packet_out datapath_id, message.data, SendOutPort.new(arp_entry.port)
    else
      # Broadcast, Unknown Unicast
      flood_to_segment(
        datapath_id,
        message.data,
        @segments.include(@port_name_of[message.in_port]),
        message.in_port
      )
    end
  end
  • Simple L3Switch 宛てではないパケットをどう扱うか、という処理。
  • 基本的には Learning Switch と同様。
    • 入ってきたパケットについて、arp table の学習
    • arp table に宛先の情報があれば、それにもとづいて送信。
    • arp table に宛先の情報がなければ flooding する。
flood_to_segment
  def flood_to_segment(datapath_id, data, segment_name, in_port = nil)
    if segment_name
      port_names = @segments.port_name_list_of(segment_name).dup
      port_names.delete(@port_name_of[in_port])

      actions = []
      port_names.each do |each|
        actions << SendOutPort.new(@port_number_of[each])
      end

      send_packet_out(
        datapath_id,
        :data => data,
        :actions => actions,
        :zero_padding => true
      )
    else
      info "SimpleL3Switch: not found a segment that has port:#{ in_port }"
    end
  end
  • Simple L3Switch のローカルネットワークセグメントに対して flooding する処理。
    • broadcast domain を制限するために OFPP_FLOOD は使っていない。
    • segment に所属しているポート一覧からパケットが入っているポートを除いて、それらのポートに対して packet out する。ただ、自分宛の ARP Request に対する応答など、in_port がないデータを送信する場合(handle_unresolved_packet)もあるので、in_port は省略可能。(in_port を省略した場合は、セグメントに含まれる全ポートに対して packet out するだけ。)
  • 一回、以下のようなエラーで落ちたことがあるので、send_packet_outでは、zero_padding を有効にしてある。
Sat Jan 19 22:16:35 2013 [critical] The length of the provided Ethernet frame is shorter than the minimum length of an Ethernet frame (= 64 bytes).

課題

機能上の課題、あるいは今後の拡張としてどういう発展が考えられるか。

  • 現状、OVS で VM が追加されたりした場合、コントローラ側でそのフォローができていない。最初に features reply で情報もらってくるだけなので。その場合、ポートの投げ先とかセグメントの情報に齟齬が出てコントローラがコケる。これは本当は話にならないので、スイッチ側のポート追加削除とかの変更とかに対して追従できる仕掛けを追加する必要がある。
  • いまのところ、1つのスイッチに対する処理を前提に作り込んでしまっている。複数スイッチで構成された環境だとどうなるか? (どうするべきか?)
  • セグメントの情報は、現時点では単なるポートグループでしかない。VLAN IDをつけるとか、Trunk Portを扱えるようにする、という拡張はすぐ考えられそう。