Skip to content

Commit

Permalink
Add support for udhcpd in wifi technology
Browse files Browse the repository at this point in the history
  • Loading branch information
ConnorRigby authored and fhunleth committed May 15, 2019
1 parent e61d32d commit f726935
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 15 deletions.
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ The following network configurations are supported:
* [x] WPA2 PSK and EAP
* [ ] USB gadget mode Ethernet, IPv4 DHCP server to supply host IP address
* [ ] Cellular networks
* [ ] WiFi AP mode
* [x] WiFi AP mode
* [ ] IPv6

`VintageNet` takes a different approach to networking from `nerves_network`. It
Expand Down Expand Up @@ -268,8 +268,7 @@ iex> VintageNet.configure("wlan0", %{
})
```

Example of host mode:
(note this will not create a DHCP server)
Example of access point mode:

```elixir
iex> VintageNet.configure("wlan0", %{
Expand All @@ -284,6 +283,10 @@ iex> VintageNet.configure("wlan0", %{
address: "192.168.24.1",
netmask: "255.255.255.0",
gateway: "192.168.24.1"
},
dhcpd: %{
start: "192.168.24.2",
end: "192.168.24.10"
}
})
```
Expand Down
72 changes: 72 additions & 0 deletions lib/vintage_net/ip/config_to_udhcpd.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
defmodule VintageNet.IP.ConfigToUdhcpd do
@moduledoc """
This is a helper module for VintageNet.Technology implementations that use
the udhcpd server.
"""

@doc """
Convert a configuration to the contents of a /etc/udhcpd.conf file
`start` - Start of the lease block
`end` - End of the lease block
`max_leases` - The maximum number of leases
`decline_time` - The amount of time that an IP will be reserved (leased to nobody)
`conflict_time` -The amount of time that an IP will be reserved
`offer_time` - How long an offered address is reserved (seconds)
`min_lease` - If client asks for lease below this value, it will be rounded up to this value (seconds)
`auto_time` - The time period at which udhcpd will write out leases file.
`static_leases` - list of `{macaddress, ipaddress}`
"""
@spec config_to_udhcpd_contents(VintageNet.ifname(), map(), Path.t()) :: String.t()
def config_to_udhcpd_contents(ifname, %{dhcpd: dhcpd}, tmpdir) do
pidfile = Path.join(tmpdir, "udhcpd.#{ifname}.pid")
lease_file = Path.join(tmpdir, "udhcpd.#{ifname}.leases")

initial = """
interface #{ifname}
pidfile #{pidfile}
lease_file #{lease_file}
"""

config = Enum.map(dhcpd, &to_udhcpd_string/1)
IO.iodata_to_binary([initial, "\n", config, "\n"])
end

defp to_udhcpd_string({:start, val}) do
"start #{val}\n"
end

defp to_udhcpd_string({:end, val}) do
"end #{val}\n"
end

defp to_udhcpd_string({:max_leases, val}) do
"max_leases #{val}\n"
end

defp to_udhcpd_string({:decline_time, val}) do
"decline_time #{val}\n"
end

defp to_udhcpd_string({:conflict_time, val}) do
"conflict_time #{val}\n"
end

defp to_udhcpd_string({:offer_time, val}) do
"offer_time #{val}\n"
end

defp to_udhcpd_string({:min_lease, val}) do
"min_lease #{val}\n"
end

defp to_udhcpd_string({:auto_time, val}) do
"auto_time #{val}\n"
end

defp to_udhcpd_string({:static_leases, leases}) do
Enum.map(leases, fn {mac, ip} ->
"static_lease #{mac} #{ip}\n"
end)
end
end
63 changes: 51 additions & 12 deletions lib/vintage_net/technology/wifi.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule VintageNet.Technology.WiFi do

alias VintageNet.WiFi.{Scan, WPA2}
alias VintageNet.Interface.RawConfig
alias VintageNet.IP.ConfigToInterfaces
alias VintageNet.IP.{ConfigToInterfaces, ConfigToUdhcpd}

@impl true
def to_raw_config(ifname, %{type: __MODULE__, wifi: wifi_config} = config, opts) do
Expand Down Expand Up @@ -35,17 +35,33 @@ defmodule VintageNet.Technology.WiFi do
{:run, killall, ["-q", "wpa_supplicant"]}
]

{:ok,
%RawConfig{
ifname: ifname,
type: __MODULE__,
source_config: config,
files: files,
cleanup_files: [Path.join(control_interface_path, ifname)],
child_specs: [{VintageNet.Interface.ConnectivityChecker, ifname}],
up_cmds: up_cmds,
down_cmds: down_cmds
}}
case maybe_add_udhcpd(ifname, config, opts) do
{udhcpd_files, udhcpd_up_cmds, udhcpd_down_cmds} ->
{:ok,
%RawConfig{
ifname: ifname,
type: __MODULE__,
source_config: config,
files: files ++ udhcpd_files,
cleanup_files: [Path.join(control_interface_path, ifname)],
child_specs: [{VintageNet.Interface.ConnectivityChecker, ifname}],
up_cmds: up_cmds ++ udhcpd_up_cmds,
down_cmds: down_cmds ++ udhcpd_down_cmds
}}

nil ->
{:ok,
%RawConfig{
ifname: ifname,
type: __MODULE__,
source_config: config,
files: files,
cleanup_files: [Path.join(control_interface_path, ifname)],
child_specs: [{VintageNet.Interface.ConnectivityChecker, ifname}],
up_cmds: up_cmds,
down_cmds: down_cmds
}}
end
end

def to_raw_config(ifname, %{type: __MODULE__}, opts) do
Expand Down Expand Up @@ -84,6 +100,29 @@ defmodule VintageNet.Technology.WiFi do
{:error, :bad_configuration}
end

defp maybe_add_udhcpd(ifname, %{dhcpd: _dhcpd} = config, opts) do
tmpdir = Keyword.fetch!(opts, :tmpdir)
killall = Keyword.fetch!(opts, :bin_killall)
udhcpd = Keyword.fetch!(opts, :bin_udhcpd)
udhcpd_conf_path = Path.join(tmpdir, "udhcpd.conf.#{ifname}")

files = [
{udhcpd_conf_path, ConfigToUdhcpd.config_to_udhcpd_contents(ifname, config, tmpdir)}
]

up_cmds = [
{:run, udhcpd, [udhcpd_conf_path]}
]

down_cmds = [
{:run, killall, ["-q", "udhcpd"]}
]

{files, up_cmds, down_cmds}
end

defp maybe_add_udhcpd(_, _, _), do: nil

@impl true
def ioctl(ifname, :scan, _args) do
Scan.scan(ifname)
Expand Down
1 change: 1 addition & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ defmodule VintageNet.MixProject do
bin_wpa_supplicant: "/usr/sbin/wpa_supplicant",
bin_wpa_cli: "/usr/sbin/wpa_cli",
bin_ip: "/sbin/ip",
bin_udhcpd: "/usr/sbin/udhcpd",
udhcpc_handler: VintageNet.Interface.Udhcpc,
resolvconf: "/etc/resolv.conf",
persistence: VintageNet.Persistence.FlatFile,
Expand Down
40 changes: 40 additions & 0 deletions test/vintage_net/ip/config_to_udhcpd_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
defmodule VintageNet.IP.ConfigToUdhcpdTest do
use ExUnit.Case
alias VintageNet.IP.ConfigToUdhcpd

test "dhcp server config" do
tmp_dir = "test_tmp"

input = %{
dhcpd: %{
start: "192.168.1.2",
end: "192.168.1.100",
max_leases: 98,
decline_time: 100,
conflict_time: 200,
offer_time: 300,
min_lease: 60,
auto_time: 60,
static_leases: [
{"00:60:08:11:CE:4E", "192.168.1.55"},
{"00:60:08:11:CE:3E", "192.168.1.56"}
]
}
}

output = ConfigToUdhcpd.config_to_udhcpd_contents("eth0", input, tmp_dir)

assert output =~ """
auto_time 60
conflict_time 200
decline_time 100
end 192.168.1.100
max_leases 98
min_lease 60
offer_time 300
start 192.168.1.2
static_lease 00:60:08:11:CE:4E 192.168.1.55
static_lease 00:60:08:11:CE:3E 192.168.1.56
"""
end
end
74 changes: 74 additions & 0 deletions test/vintage_net/technology/wifi_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -1042,4 +1042,78 @@ defmodule VintageNet.Technology.WiFiTest do

assert {:ok, output} == WiFi.to_raw_config("wlan0", input, default_opts())
end

test "create a dhcpd config" do
input = %{
type: VintageNet.Technology.WiFi,
wifi: %{
ssid: "example ap",
key_mgmt: :none,
mode: :host
},
ipv4: %{
method: :static,
address: "192.168.24.1",
netmask: "255.255.255.0",
gateway: "192.168.24.1"
},
dhcpd: %{
start: "192.168.24.2",
end: "192.168.24.100"
},
hostname: "unit_test"
}

output = %RawConfig{
ifname: "wlan0",
type: VintageNet.Technology.WiFi,
source_config: input,
child_specs: [{VintageNet.Interface.ConnectivityChecker, "wlan0"}],
files: [
{"/tmp/vintage_net/network_interfaces.wlan0",
"""
iface wlan0 inet static
address 192.168.24.1
gateway 192.168.24.1
netmask 255.255.255.0
hostname unit_test
"""},
{"/tmp/vintage_net/wpa_supplicant.conf.wlan0",
"""
ctrl_interface=/tmp/vintage_net/wpa_supplicant
country=00
network={
ssid="example ap"
key_mgmt=NONE
mode=2
}
"""},
{"/tmp/vintage_net/udhcpd.conf.wlan0",
"""
interface wlan0
pidfile /tmp/vintage_net/udhcpd.wlan0.pid
lease_file /tmp/vintage_net/udhcpd.wlan0.leases
end 192.168.24.100
start 192.168.24.2
"""}
],
up_cmds: [
{:run_ignore_errors, "/usr/bin/killall", ["-q", "wpa_supplicant"]},
{:run, "/usr/sbin/wpa_supplicant",
["-B", "-i", "wlan0", "-c", "/tmp/vintage_net/wpa_supplicant.conf.wlan0", "-dd"]},
{:run, "/sbin/ifup", ["-i", "/tmp/vintage_net/network_interfaces.wlan0", "wlan0"]},
{:run, "/usr/sbin/udhcpd", ["/tmp/vintage_net/udhcpd.conf.wlan0"]}
],
down_cmds: [
{:run, "/sbin/ifdown", ["-i", "/tmp/vintage_net/network_interfaces.wlan0", "wlan0"]},
{:run, "/usr/bin/killall", ["-q", "wpa_supplicant"]},
{:run, "/usr/bin/killall", ["-q", "udhcpd"]}
],
cleanup_files: ["/tmp/vintage_net/wpa_supplicant/wlan0"]
}

assert {:ok, output} == WiFi.to_raw_config("wlan0", input, default_opts())
end
end

0 comments on commit f726935

Please sign in to comment.