{"action":"created","comment":{"url":"https://api.github.com/repos/trema/trema/pulls/comments/41349765","id":41349765,"diff_hunk":"@@ -0,0 +1,167 @@\n+require \"pio\"\n+\n+require \"arp-table\"\n+require \"interface\"\n+require \"routing-table\"\n+require \"router-utils\"\n+\n+class SimpleRouter < Trema::Controller\n+  include RouterUtils\n+\n+  def start( _args )\n+    load \"simple_router.conf\"\n+    @interfaces = Interfaces.new( $interface )\n+    @arp_table = ARPTable.new\n+    @routing_table = RoutingTable.new( $route )\n+  end\n+\n+  def switch_ready( datapath_id )\n+    logger.info \"#{datapath_id.to_hex} is connected\"\n+    @switche = datapath_id\n+    init_flows( datapath_id )\n+  end\n+\n+  def packet_in( datapath_id, message )\n+    return if not to_me?( message )\n+\n+    case message.data\n+    when Pio::Arp::Request\n+      handle_arp_request( datapath_id, message )\n+    when Pio::Arp::Reply\n+      handle_arp_reply( message )\n+    when Pio::Parser::IPv4Packet\n+      handle_ipv4( datapath_id, message )\n+    else\n+      fail 'Failed to handle packet_in data type'\n+    end\n+  end\n+\n+  private\n+\n+  def init_flows( datapath_id )\n+    send_flow_mod_delete( datapath_id, match: Match.new() )\n+  end\n+\n+  def to_me?( message )\n+    return true if message.destination_mac.broadcast?\n+\n+    interface = @interfaces.find_by_port( message.in_port )\n+    if interface and interface.has?( message.destination_mac )\n+      return true\n+    end\n+  end\n+\n+  def handle_arp_request( datapath_id, message )\n+    port = message.in_port\n+    destination_mac = message.data.target_protocol_address\n+    interface = @interfaces.find_by_port_and_ipaddr( port, destination_mac )\n+    if interface\n+      arp_reply = create_arp_reply_from( message, interface.hwaddr )\n+      packet_out_raw( datapath_id, arp_reply, SendOutPort.new( interface.port ) )\n+    end\n+  end\n+\n+  def handle_arp_reply( message )\n+    @arp_table.update( message.in_port,\n+                      message.sender_protocol_address,\n+                      message.source_mac\n+                     )\n+  end\n+\n+  def handle_ipv4( datapath_id, message )\n+    if should_forward?( message )\n+      forward( datapath_id, message )\n+    elsif message.data.ip_protocol == 1\n+      icmp = Pio::Icmp.read( message.raw_data )\n+      if icmp.icmp_type == 8\n+        handle_icmpv4_echo_request( datapath_id, message )\n+      else\n+        fail \"Faild to handle icmp type\"\n+      end\n+    else\n+      fail \"Faild to handle ipv4 packet\"\n+    end\n+  end\n+\n+  def should_forward?( message )\n+    not @interfaces.find_by_ipaddr( message.data.ip_destination_address )\n+  end\n+\n+  def handle_icmpv4_echo_request( datapath_id, message )\n+    interface = @interfaces.find_by_port( message.in_port )\n+    ip_source_address = message.data.ip_source_address\n+    arp_entry = @arp_table.lookup( ip_source_address )\n+    if arp_entry\n+      icmpv4_reply = create_icmpv4_reply( arp_entry, interface, message )\n+      packet_out_raw( datapath_id, icmpv4_reply, SendOutPort.new( interface.port ) )\n+    else\n+      handle_unresolved_packet( datapath_id, message, interface, ip_source_address )\n+    end\n+  end\n+\n+  def forward( datapath_id, message )\n+    next_hop = resolve_next_hop( message.data.ip_destination_address )\n+\n+    interface = @interfaces.find_by_prefix( next_hop )\n+    if not interface or interface.port == message.in_port\n+      return\n+    end\n+\n+    arp_entry = @arp_table.lookup( next_hop )\n+    if arp_entry\n+      macsa = interface.hwaddr\n+      macda = arp_entry.hwaddr\n+      action = create_action_from( macsa, macda, interface.port )\n+      flow_mod( datapath_id, message, action )\n+      packet_out( datapath_id, message, action )\n+    else\n+      handle_unresolved_packet( datapath_id, message, interface, next_hop )\n+    end\n+  end\n+\n+  def resolve_next_hop( ip_destination_address )\n+    interface = @interfaces.find_by_prefix( ip_destination_address )\n+    if interface\n+      ip_destination_address\n+    else\n+      @routing_table.lookup( ip_destination_address )\n+    end\n+  end\n+\n+  def flow_mod( datapath_id, message, action )\n+    send_flow_mod_add(\n+      datapath_id,\n+      match: ExactMatch.new( message ),\n+      actions: action\n+    )\n+  end\n+\n+  def packet_out( datapath_id, packet, action )\n+    send_packet_out(\n+      datapath_id,\n+      packet_in: packet,\n+      actions: action\n+    )\n+  end\n+\n+  def packet_out_raw( datapath_id, raw_data, action )\n+    send_packet_out(\n+      datapath_id,\n+      raw_data: raw_data,\n+      actions: action\n+    )\n+  end\n+\n+  def handle_unresolved_packet( datapath_id, message, interface, ipaddr )\n+    arp_request = create_arp_request_from( interface, ipaddr )\n+    packet_out_raw( datapath_id, arp_request, SendOutPort.new( interface.port ) )\n+  end\n+\n+  def create_action_from( source_mac, destination_mac, port )\n+    [\n+      SetEtherSourceAddress.new( source_mac ),\n+      SetEtherDestinationAddress.new( destination_mac ),\n+      SendOutPort.new( port )","path":"examples/simple-router/simple-router.rb","position":164,"original_position":164,"commit_id":"f76f9c0151dbc85bebf84b807aea60685ca57d41","original_commit_id":"f76f9c0151dbc85bebf84b807aea60685ca57d41","user":{"login":"houndci","id":6697940,"avatar_url":"https://avatars.githubusercontent.com/u/6697940?v=3","gravatar_id":"","url":"https://api.github.com/users/houndci","html_url":"https://github.com/houndci","followers_url":"https://api.github.com/users/houndci/followers","following_url":"https://api.github.com/users/houndci/following{/other_user}","gists_url":"https://api.github.com/users/houndci/gists{/gist_id}","starred_url":"https://api.github.com/users/houndci/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/houndci/subscriptions","organizations_url":"https://api.github.com/users/houndci/orgs","repos_url":"https://api.github.com/users/houndci/repos","events_url":"https://api.github.com/users/houndci/events{/privacy}","received_events_url":"https://api.github.com/users/houndci/received_events","type":"User","site_admin":false},"body":"Space inside parentheses detected.","created_at":"2015-10-07T04:09:05Z","updated_at":"2015-10-07T04:09:05Z","html_url":"https://github.com/trema/trema/pull/410#discussion_r41349765","pull_request_url":"https://api.github.com/repos/trema/trema/pulls/410","_links":{"self":{"href":"https://api.github.com/repos/trema/trema/pulls/comments/41349765"},"html":{"href":"https://github.com/trema/trema/pull/410#discussion_r41349765"},"pull_request":{"href":"https://api.github.com/repos/trema/trema/pulls/410"}}},"pull_request":{"url":"https://api.github.com/repos/trema/trema/pulls/410","id":46985306,"html_url":"https://github.com/trema/trema/pull/410","diff_url":"https://github.com/trema/trema/pull/410.diff","patch_url":"https://github.com/trema/trema/pull/410.patch","issue_url":"https://api.github.com/repos/trema/trema/issues/410","number":410,"state":"open","locked":false,"title":"add example/simple-router/","user":{"login":"rumb","id":14933459,"avatar_url":"https://avatars.githubusercontent.com/u/14933459?v=3","gravatar_id":"","url":"https://api.github.com/users/rumb","html_url":"https://github.com/rumb","followers_url":"https://api.github.com/users/rumb/followers","following_url":"https://api.github.com/users/rumb/following{/other_user}","gists_url":"https://api.github.com/users/rumb/gists{/gist_id}","starred_url":"https://api.github.com/users/rumb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rumb/subscriptions","organizations_url":"https://api.github.com/users/rumb/orgs","repos_url":"https://api.github.com/users/rumb/repos","events_url":"https://api.github.com/users/rumb/events{/privacy}","received_events_url":"https://api.github.com/users/rumb/received_events","type":"User","site_admin":false},"body":"trema 0.3.0 にあった src/examples/simple-router をtrema 0.7.1で動くように書き換えました。","created_at":"2015-10-07T04:08:55Z","updated_at":"2015-10-07T04:09:06Z","closed_at":null,"merged_at":null,"merge_commit_sha":"dfc428a5603a6a1e9ab73f4ea376c9e9bfed4c02","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/trema/trema/pulls/410/commits","review_comments_url":"https://api.github.com/repos/trema/trema/pulls/410/comments","review_comment_url":"https://api.github.com/repos/trema/trema/pulls/comments{/number}","comments_url":"https://api.github.com/repos/trema/trema/issues/410/comments","statuses_url":"https://api.github.com/repos/trema/trema/statuses/f76f9c0151dbc85bebf84b807aea60685ca57d41","head":{"label":"rumb:add_simple-router","ref":"add_simple-router","sha":"f76f9c0151dbc85bebf84b807aea60685ca57d41","user":{"login":"rumb","id":14933459,"avatar_url":"https://avatars.githubusercontent.com/u/14933459?v=3","gravatar_id":"","url":"https://api.github.com/users/rumb","html_url":"https://github.com/rumb","followers_url":"https://api.github.com/users/rumb/followers","following_url":"https://api.github.com/users/rumb/following{/other_user}","gists_url":"https://api.github.com/users/rumb/gists{/gist_id}","starred_url":"https://api.github.com/users/rumb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rumb/subscriptions","organizations_url":"https://api.github.com/users/rumb/orgs","repos_url":"https://api.github.com/users/rumb/repos","events_url":"https://api.github.com/users/rumb/events{/privacy}","received_events_url":"https://api.github.com/users/rumb/received_events","type":"User","site_admin":false},"repo":{"id":43789487,"name":"trema","full_name":"rumb/trema","owner":{"login":"rumb","id":14933459,"avatar_url":"https://avatars.githubusercontent.com/u/14933459?v=3","gravatar_id":"","url":"https://api.github.com/users/rumb","html_url":"https://github.com/rumb","followers_url":"https://api.github.com/users/rumb/followers","following_url":"https://api.github.com/users/rumb/following{/other_user}","gists_url":"https://api.github.com/users/rumb/gists{/gist_id}","starred_url":"https://api.github.com/users/rumb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rumb/subscriptions","organizations_url":"https://api.github.com/users/rumb/orgs","repos_url":"https://api.github.com/users/rumb/repos","events_url":"https://api.github.com/users/rumb/events{/privacy}","received_events_url":"https://api.github.com/users/rumb/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/rumb/trema","description":"Full-Stack OpenFlow Framework in Ruby","fork":true,"url":"https://api.github.com/repos/rumb/trema","forks_url":"https://api.github.com/repos/rumb/trema/forks","keys_url":"https://api.github.com/repos/rumb/trema/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rumb/trema/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rumb/trema/teams","hooks_url":"https://api.github.com/repos/rumb/trema/hooks","issue_events_url":"https://api.github.com/repos/rumb/trema/issues/events{/number}","events_url":"https://api.github.com/repos/rumb/trema/events","assignees_url":"https://api.github.com/repos/rumb/trema/assignees{/user}","branches_url":"https://api.github.com/repos/rumb/trema/branches{/branch}","tags_url":"https://api.github.com/repos/rumb/trema/tags","blobs_url":"https://api.github.com/repos/rumb/trema/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rumb/trema/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rumb/trema/git/refs{/sha}","trees_url":"https://api.github.com/repos/rumb/trema/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rumb/trema/statuses/{sha}","languages_url":"https://api.github.com/repos/rumb/trema/languages","stargazers_url":"https://api.github.com/repos/rumb/trema/stargazers","contributors_url":"https://api.github.com/repos/rumb/trema/contributors","subscribers_url":"https://api.github.com/repos/rumb/trema/subscribers","subscription_url":"https://api.github.com/repos/rumb/trema/subscription","commits_url":"https://api.github.com/repos/rumb/trema/commits{/sha}","git_commits_url":"https://api.github.com/repos/rumb/trema/git/commits{/sha}","comments_url":"https://api.github.com/repos/rumb/trema/comments{/number}","issue_comment_url":"https://api.github.com/repos/rumb/trema/issues/comments{/number}","contents_url":"https://api.github.com/repos/rumb/trema/contents/{+path}","compare_url":"https://api.github.com/repos/rumb/trema/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rumb/trema/merges","archive_url":"https://api.github.com/repos/rumb/trema/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rumb/trema/downloads","issues_url":"https://api.github.com/repos/rumb/trema/issues{/number}","pulls_url":"https://api.github.com/repos/rumb/trema/pulls{/number}","milestones_url":"https://api.github.com/repos/rumb/trema/milestones{/number}","notifications_url":"https://api.github.com/repos/rumb/trema/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rumb/trema/labels{/name}","releases_url":"https://api.github.com/repos/rumb/trema/releases{/id}","created_at":"2015-10-07T01:53:20Z","updated_at":"2015-10-07T01:53:22Z","pushed_at":"2015-10-07T03:12:15Z","git_url":"git://github.com/rumb/trema.git","ssh_url":"git@github.com:rumb/trema.git","clone_url":"https://github.com/rumb/trema.git","svn_url":"https://github.com/rumb/trema","homepage":"http://trema.github.com/trema/","size":26023,"stargazers_count":0,"watchers_count":0,"language":"Ruby","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"develop"}},"base":{"label":"trema:develop","ref":"develop","sha":"73281944328b8eee5aa83c35a48e55e75f3b2110","user":{"login":"trema","id":696690,"avatar_url":"https://avatars.githubusercontent.com/u/696690?v=3","gravatar_id":"","url":"https://api.github.com/users/trema","html_url":"https://github.com/trema","followers_url":"https://api.github.com/users/trema/followers","following_url":"https://api.github.com/users/trema/following{/other_user}","gists_url":"https://api.github.com/users/trema/gists{/gist_id}","starred_url":"https://api.github.com/users/trema/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/trema/subscriptions","organizations_url":"https://api.github.com/users/trema/orgs","repos_url":"https://api.github.com/users/trema/repos","events_url":"https://api.github.com/users/trema/events{/privacy}","received_events_url":"https://api.github.com/users/trema/received_events","type":"Organization","site_admin":false},"repo":{"id":1617268,"name":"trema","full_name":"trema/trema","owner":{"login":"trema","id":696690,"avatar_url":"https://avatars.githubusercontent.com/u/696690?v=3","gravatar_id":"","url":"https://api.github.com/users/trema","html_url":"https://github.com/trema","followers_url":"https://api.github.com/users/trema/followers","following_url":"https://api.github.com/users/trema/following{/other_user}","gists_url":"https://api.github.com/users/trema/gists{/gist_id}","starred_url":"https://api.github.com/users/trema/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/trema/subscriptions","organizations_url":"https://api.github.com/users/trema/orgs","repos_url":"https://api.github.com/users/trema/repos","events_url":"https://api.github.com/users/trema/events{/privacy}","received_events_url":"https://api.github.com/users/trema/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/trema/trema","description":"Full-Stack OpenFlow Framework in Ruby","fork":false,"url":"https://api.github.com/repos/trema/trema","forks_url":"https://api.github.com/repos/trema/trema/forks","keys_url":"https://api.github.com/repos/trema/trema/keys{/key_id}","collaborators_url":"https://api.github.com/repos/trema/trema/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/trema/trema/teams","hooks_url":"https://api.github.com/repos/trema/trema/hooks","issue_events_url":"https://api.github.com/repos/trema/trema/issues/events{/number}","events_url":"https://api.github.com/repos/trema/trema/events","assignees_url":"https://api.github.com/repos/trema/trema/assignees{/user}","branches_url":"https://api.github.com/repos/trema/trema/branches{/branch}","tags_url":"https://api.github.com/repos/trema/trema/tags","blobs_url":"https://api.github.com/repos/trema/trema/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/trema/trema/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/trema/trema/git/refs{/sha}","trees_url":"https://api.github.com/repos/trema/trema/git/trees{/sha}","statuses_url":"https://api.github.com/repos/trema/trema/statuses/{sha}","languages_url":"https://api.github.com/repos/trema/trema/languages","stargazers_url":"https://api.github.com/repos/trema/trema/stargazers","contributors_url":"https://api.github.com/repos/trema/trema/contributors","subscribers_url":"https://api.github.com/repos/trema/trema/subscribers","subscription_url":"https://api.github.com/repos/trema/trema/subscription","commits_url":"https://api.github.com/repos/trema/trema/commits{/sha}","git_commits_url":"https://api.github.com/repos/trema/trema/git/commits{/sha}","comments_url":"https://api.github.com/repos/trema/trema/comments{/number}","issue_comment_url":"https://api.github.com/repos/trema/trema/issues/comments{/number}","contents_url":"https://api.github.com/repos/trema/trema/contents/{+path}","compare_url":"https://api.github.com/repos/trema/trema/compare/{base}...{head}","merges_url":"https://api.github.com/repos/trema/trema/merges","archive_url":"https://api.github.com/repos/trema/trema/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/trema/trema/downloads","issues_url":"https://api.github.com/repos/trema/trema/issues{/number}","pulls_url":"https://api.github.com/repos/trema/trema/pulls{/number}","milestones_url":"https://api.github.com/repos/trema/trema/milestones{/number}","notifications_url":"https://api.github.com/repos/trema/trema/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/trema/trema/labels{/name}","releases_url":"https://api.github.com/repos/trema/trema/releases{/id}","created_at":"2011-04-15T02:38:25Z","updated_at":"2015-09-16T06:40:20Z","pushed_at":"2015-10-07T04:08:56Z","git_url":"git://github.com/trema/trema.git","ssh_url":"git@github.com:trema/trema.git","clone_url":"https://github.com/trema/trema.git","svn_url":"https://github.com/trema/trema","homepage":"http://trema.github.com/trema/","size":26023,"stargazers_count":237,"watchers_count":237,"language":"Ruby","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":79,"mirror_url":null,"open_issues_count":18,"forks":79,"open_issues":18,"watchers":237,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/trema/trema/pulls/410"},"html":{"href":"https://github.com/trema/trema/pull/410"},"issue":{"href":"https://api.github.com/repos/trema/trema/issues/410"},"comments":{"href":"https://api.github.com/repos/trema/trema/issues/410/comments"},"review_comments":{"href":"https://api.github.com/repos/trema/trema/pulls/410/comments"},"review_comment":{"href":"https://api.github.com/repos/trema/trema/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/trema/trema/pulls/410/commits"},"statuses":{"href":"https://api.github.com/repos/trema/trema/statuses/f76f9c0151dbc85bebf84b807aea60685ca57d41"}}}}