openflow/match: Refactored
This commit is contained in:
parent
e65bf77a3c
commit
9ddd82fcab
27 changed files with 1201 additions and 1584 deletions
|
|
@ -11,9 +11,9 @@ defmodule LearningSwitch.Ofctl do
|
|||
|
||||
@aging_time 180
|
||||
|
||||
@mcast {"010000000000", "110000000000"}
|
||||
@bcast "ffffffffffff"
|
||||
@ipv6_mcast {"333300000000", "ffff00000000"}
|
||||
@mcast {<<0x010000000000::48>>, <<0x110000000000::48>>}
|
||||
@bcast <<0xffffffffffff::48>>
|
||||
@ipv6_mcast {<<0x333300000000::48>>, <<0xffff00000000::48>>}
|
||||
|
||||
defmodule State do
|
||||
defstruct [
|
||||
|
|
|
|||
|
|
@ -63,12 +63,12 @@ defmodule NxLearningSwitch do
|
|||
hard_timeout: 10,
|
||||
flow_specs: [
|
||||
NxFlowSpecMatch.new(
|
||||
src: :nx_eth_src,
|
||||
dst: :nx_eth_dst
|
||||
src: :eth_src,
|
||||
dst: :eth_dst
|
||||
),
|
||||
NxFlowSpecMatch.new(
|
||||
src: :nx_vlan_tci,
|
||||
dst: :nx_vlan_tci,
|
||||
src: :vlan_vid,
|
||||
dst: :vlan_vid,
|
||||
offset: 0,
|
||||
n_bits: 12
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
defmodule SimpleRouter.Config do
|
||||
@moduledoc false
|
||||
|
||||
alias Tres.IPv4Address
|
||||
alias SimpleRouter.IPv4Address
|
||||
|
||||
@spec interfaces() :: %{String.t() => map()}
|
||||
def interfaces do
|
||||
|
|
@ -24,7 +24,7 @@ defmodule SimpleRouter.Config do
|
|||
|
||||
entry =
|
||||
%{
|
||||
mac_address: String.replace(mac, ~r/:/, ""),
|
||||
mac_address: mac_to_bin(mac),
|
||||
ip_address: ipaddr,
|
||||
subnet_mask: mask,
|
||||
network_address: IPv4Address.to_network({ipaddr, mask}),
|
||||
|
|
@ -50,6 +50,13 @@ defmodule SimpleRouter.Config do
|
|||
end
|
||||
end
|
||||
|
||||
defp mac_to_bin(mac) do
|
||||
mac
|
||||
|> String.replace(~r/:/, "")
|
||||
|> String.to_integer(16)
|
||||
|> :binary.encode_unsigned(:big)
|
||||
end
|
||||
|
||||
defp get_env(key, default),
|
||||
do: Application.get_env(:simple_router, key, default)
|
||||
end
|
||||
|
|
|
|||
120
examples/simple_router/lib/simple_router/ipv4_address.ex
Normal file
120
examples/simple_router/lib/simple_router/ipv4_address.ex
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
defmodule SimpleRouter.IPv4Address do
|
||||
@moduledoc """
|
||||
IP Address Utils
|
||||
"""
|
||||
|
||||
use Bitwise
|
||||
|
||||
@typep in_addr :: {byte, byte, byte, byte}
|
||||
@typep in_addr_str :: String.t()
|
||||
|
||||
@doc """
|
||||
iex> {{192, 168, 0, 1}, {255, 255, 255, 255}} = IPv4Address.parse(<<192, 168, 0, 1>>)
|
||||
iex> {{192, 168, 0, 1}, {255, 255, 255, 255}} = IPv4Address.parse("192.168.0.1")
|
||||
iex> {{192, 168, 0, 1}, {255, 255, 255, 0}} = IPv4Address.parse("192.168.0.1/24")
|
||||
"""
|
||||
@spec parse(in_addr_str | binary()) :: {in_addr, in_addr}
|
||||
def parse(<<a1, a2, a3, a4>>), do: {{a1, a2, a3, a4}, {255, 255, 255, 255}}
|
||||
|
||||
def parse(addr) do
|
||||
case String.split(addr, ~r/\//) do
|
||||
[^addr] ->
|
||||
{:ok, ipaddr} = addr |> to_charlist |> :inet.parse_address()
|
||||
{ipaddr, {255, 255, 255, 255}}
|
||||
|
||||
[netaddr, cidr_str] ->
|
||||
cidr = String.to_integer(cidr_str)
|
||||
mask = 0xFFFFFFFF >>> (32 - cidr) <<< (32 - cidr)
|
||||
<<m1, m2, m3, m4>> = <<mask::32>>
|
||||
{:ok, ipaddr} = netaddr |> to_charlist |> :inet.parse_address()
|
||||
{ipaddr, {m1, m2, m3, m4}}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
iex> addr = IPv4Address.parse("192.168.10.1/24")
|
||||
iex> {192, 168, 10, 0} = IPv4Address.to_network(addr)
|
||||
"""
|
||||
@spec to_network({in_addr(), in_addr()}) :: in_addr()
|
||||
def to_network({{a1, a2, a3, a4}, {m1, m2, m3, m4}}) do
|
||||
addr_int = :binary.decode_unsigned(<<a1, a2, a3, a4>>, :big)
|
||||
mask_int = :binary.decode_unsigned(<<m1, m2, m3, m4>>, :big)
|
||||
<<n1, n2, n3, n4>> = <<addr_int &&& mask_int::32>>
|
||||
{n1, n2, n3, n4}
|
||||
end
|
||||
|
||||
@doc """
|
||||
iex> "192.168.0.1" = IPv4Address.to_str({192, 168, 0, 1})
|
||||
"""
|
||||
@spec to_str(in_addr | in_addr_str) :: in_addr_str
|
||||
def to_str(addr) when is_binary(addr) do
|
||||
addr
|
||||
end
|
||||
|
||||
def to_str(addr) when is_tuple(addr) do
|
||||
"#{:inet.ntoa(addr)}"
|
||||
end
|
||||
|
||||
@doc """
|
||||
iex> 0xc0a80001 = IPv4Address.to_int({192, 168, 0, 1})
|
||||
"""
|
||||
@spec to_int(in_addr) :: 0..0xFFFFFFFF
|
||||
def to_int({a, b, c, d}) do
|
||||
<<a, b, c, d>> |> :binary.decode_unsigned(:big)
|
||||
end
|
||||
|
||||
@doc """
|
||||
iex> true = IPv4Address.is_private?({192, 168, 0, 1})
|
||||
"""
|
||||
@spec is_private?(in_addr) :: boolean
|
||||
def is_private?(addr) do
|
||||
ipaddr_int = to_int(addr)
|
||||
priv_class_a?(ipaddr_int) or priv_class_b?(ipaddr_int) or priv_class_c?(ipaddr_int)
|
||||
end
|
||||
|
||||
@doc """
|
||||
iex> false = IPv4Address.is_loopback?({192, 168, 0, 1})
|
||||
iex> true = IPv4Address.is_loopback?({127, 0, 0, 1})
|
||||
"""
|
||||
@spec is_loopback?(in_addr) :: boolean
|
||||
def is_loopback?({127, _, _, _}), do: true
|
||||
def is_loopback?({_, _, _, _}), do: false
|
||||
|
||||
@doc """
|
||||
iex> false = IPv4Address.is_multicast?({192, 168, 0, 1})
|
||||
iex> true = IPv4Address.is_multicast?({224, 0, 0, 1})
|
||||
"""
|
||||
@spec is_multicast?(in_addr) :: boolean
|
||||
def is_multicast?(in_addr) do
|
||||
in_addr
|
||||
|> to_int
|
||||
|> mcast_class_d?
|
||||
end
|
||||
|
||||
@doc """
|
||||
iex> false = IPv4Address.is_broadcast?({192, 168, 0, 1})
|
||||
iex> true = IPv4Address.is_broadcast?({255, 255, 255, 255})
|
||||
"""
|
||||
@spec is_broadcast?(in_addr) :: boolean
|
||||
def is_broadcast?({255, _, _, _}), do: true
|
||||
def is_broadcast?({_, _, _, _}), do: false
|
||||
|
||||
# private functions
|
||||
|
||||
@class_a_subnet 0xFF000000
|
||||
@class_b_subnet 0xFFF00000
|
||||
@class_c_subnet 0xFFFF0000
|
||||
@class_d_subnet 0xE0000000
|
||||
|
||||
@class_a_start_addr 167_772_160
|
||||
@class_b_start_addr 2_886_729_728
|
||||
@class_c_start_addr 3_232_235_520
|
||||
|
||||
defp priv_class_a?(ipaddr_int), do: (ipaddr_int &&& @class_a_subnet) == @class_a_start_addr
|
||||
|
||||
defp priv_class_b?(ipaddr_int), do: (ipaddr_int &&& @class_b_subnet) == @class_b_start_addr
|
||||
|
||||
defp priv_class_c?(ipaddr_int), do: (ipaddr_int &&& @class_c_subnet) == @class_c_start_addr
|
||||
|
||||
defp mcast_class_d?(ipaddr_int), do: (ipaddr_int &&& @class_d_subnet) == @class_d_subnet
|
||||
end
|
||||
87
examples/simple_router/lib/simple_router/mac_address.ex
Normal file
87
examples/simple_router/lib/simple_router/mac_address.ex
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
defmodule SimpleRouter.MacAddress do
|
||||
@moduledoc """
|
||||
MAC Address Utils
|
||||
"""
|
||||
|
||||
use Bitwise
|
||||
|
||||
@mac_addr_pattern ~r/^(?:[0-9a-fA-F][0-9a-fA-F]){6}$/
|
||||
|
||||
@spec bin_to_str(<<_::48>>) :: String.t()
|
||||
def bin_to_str(<<_::6-bytes>> = mac) do
|
||||
mac
|
||||
|> :erlang.binary_to_list()
|
||||
|> Enum.reduce("", &to_hex/2)
|
||||
|> String.downcase()
|
||||
end
|
||||
|
||||
@spec to_a(String.t()) :: [non_neg_integer()]
|
||||
def to_a(mac) do
|
||||
mac
|
||||
|> check_format()
|
||||
|> to_a([])
|
||||
rescue
|
||||
_e in ArgumentError ->
|
||||
{:error, :invalid_format}
|
||||
end
|
||||
|
||||
@spec to_i(String.t()) :: non_neg_integer()
|
||||
def to_i(mac) do
|
||||
mac
|
||||
|> check_format()
|
||||
|> String.to_integer(16)
|
||||
rescue
|
||||
_e in ArgumentError ->
|
||||
{:error, :invalid_format}
|
||||
end
|
||||
|
||||
@spec is_broadcast?(String.t()) :: boolean()
|
||||
def is_broadcast?(mac) do
|
||||
mac
|
||||
|> check_format()
|
||||
|> to_a()
|
||||
|> Enum.at(0)
|
||||
|> Kernel.==(0xFF)
|
||||
rescue
|
||||
_e in ArgumentError ->
|
||||
{:error, :invalid_format}
|
||||
end
|
||||
|
||||
@spec is_multicast?(String.t()) :: boolean()
|
||||
def is_multicast?(mac) do
|
||||
mac
|
||||
|> check_format()
|
||||
|> to_a()
|
||||
|> Enum.at(0)
|
||||
|> Bitwise.&&&(1)
|
||||
|> Kernel.==(1)
|
||||
rescue
|
||||
_e in ArgumentError ->
|
||||
{:error, :invalid_format}
|
||||
end
|
||||
|
||||
# private functions
|
||||
|
||||
defp to_a(<<>>, acc), do: Enum.reverse(acc)
|
||||
|
||||
defp to_a(<<octet::2-bytes, rest::bytes>>, acc) do
|
||||
to_a(rest, [String.to_integer(octet, 16) | acc])
|
||||
end
|
||||
|
||||
@spec to_hex(0..0xFF, String.t()) :: String.t()
|
||||
defp to_hex(int, acc), do: "#{acc}#{to_hex(int)}"
|
||||
|
||||
@spec to_hex(0..0xFF) :: String.t()
|
||||
defp to_hex(int) do
|
||||
int
|
||||
|> Integer.to_string(16)
|
||||
|> String.pad_leading(2, "0")
|
||||
end
|
||||
|
||||
@spec check_format(String.t()) :: String.t() | none()
|
||||
defp check_format(mac) when is_binary(mac) do
|
||||
if String.match?(mac, @mac_addr_pattern),
|
||||
do: mac,
|
||||
else: raise(ArgumentError, message: "MAC address should be 12 letters hex string format")
|
||||
end
|
||||
end
|
||||
|
|
@ -5,8 +5,7 @@ defmodule SimpleRouter.Openflow.FlowTables do
|
|||
|
||||
use Tres.Controller
|
||||
|
||||
alias Tres.IPv4Address
|
||||
alias Tres.MacAddress
|
||||
alias SimpleRouter.IPv4Address
|
||||
|
||||
@classifier_table_id 0
|
||||
@arp_handler_table_id 1
|
||||
|
|
@ -219,7 +218,7 @@ defmodule SimpleRouter.Openflow.FlowTables do
|
|||
defp arp_packet(iface) do
|
||||
ether_header = <<
|
||||
0xffffffffffff::48, # destination ethernet address
|
||||
(MacAddress.to_i(iface.mac_address))::48, # source ethernet address
|
||||
iface.mac_address::48-bits, # source ethernet address
|
||||
0x0806::16 # ethernet type
|
||||
>>
|
||||
|
||||
|
|
@ -230,7 +229,7 @@ defmodule SimpleRouter.Openflow.FlowTables do
|
|||
6::8, # hardware address length
|
||||
4::8, # protocol address length
|
||||
1::16, # ARPOP_REQUEST
|
||||
(MacAddress.to_i(iface.mac_address))::48, # Source Hardware Address
|
||||
iface.mac_address::48-bits, # Source Hardware Address
|
||||
(IPv4Address.to_int(iface.ip_address))::32, # Source Protocol Address
|
||||
0x000000000000::48 # Target Hardware Address
|
||||
>>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue