Compare commits

...

8 Commits

Author SHA1 Message Date
Rick Carlino c480887d16
Merge branch 'staging' into remove-dnsmasq 2020-05-22 16:24:12 -05:00
Rick Carlino 3453a9c39b
Merge branch 'staging' into remove-dnsmasq 2020-05-18 17:06:06 -05:00
Rick Carlino b0b0a0f6e3
Merge branch 'staging' into remove-dnsmasq 2020-05-15 09:22:20 -05:00
Rick Carlino 9716651107
Merge branch 'staging' into remove-dnsmasq 2020-04-27 11:28:30 -05:00
Rick Carlino 561554aefa
Merge branch 'staging' into remove-dnsmasq 2020-04-10 16:51:02 -05:00
Rick Carlino ad13dfc8bf
Merge branch 'staging' into remove-dnsmasq 2020-04-09 11:47:39 -05:00
Rick Carlino fe3732a7b6
Merge branch 'staging' into remove-dnsmasq 2020-04-08 15:25:04 -05:00
Connor Rigby 5e483c01e2 Remove usage of dnsmasq
This implementes a small DNS server that can be controlled/setup by
VintageNet natively, instead of shelling out to dnsmasq. It also
by extension removes the usage of dnsmasq's dhcp server, replacing it
with dhcpcd which is built into VintageNet as well.
2020-03-28 10:18:51 -07:00
7 changed files with 103 additions and 97 deletions

View File

@ -10,22 +10,7 @@ config :vintage_net,
persistence: VintageNet.Persistence.Null,
config: [
{"wlan0", %{type: VintageNet.Technology.Null}},
{"usb0",
%{
type: FarmbotOS.Platform.Target.Configurator.CaptivePortal,
ipv4: %{
method: :static,
address: "192.168.25.1",
netmask: "255.255.255.0"
},
dnsmasq: %{
domain: "farmbot",
server: "192.168.25.1",
address: "192.168.25.1",
start: "192.168.25.2",
end: "192.168.25.10"
}
}}
{"usb0", %{type: VintageNetDirect}}
]
config :mdns_lite,

View File

@ -10,22 +10,7 @@ config :vintage_net,
persistence: VintageNet.Persistence.Null,
config: [
{"wlan0", %{type: VintageNet.Technology.Null}},
{"usb0",
%{
type: FarmbotOS.Platform.Target.Configurator.CaptivePortal,
ipv4: %{
method: :static,
address: "192.168.25.1",
netmask: "255.255.255.0"
},
dnsmasq: %{
domain: "farmbot",
server: "192.168.25.1",
address: "192.168.25.1",
start: "192.168.25.2",
end: "192.168.25.10"
}
}}
{"usb0", %{type: VintageNetDirect}}
]
config :mdns_lite,

View File

@ -0,0 +1,79 @@
defmodule FarmbotOS.Configurator.CaptiveDNS do
use GenServer
alias __MODULE__, as: State
defstruct [:dns_socket, :dns_port, :ifname]
def start_link(ifname, port) do
GenServer.start_link(__MODULE__, [ifname, port])
end
@impl GenServer
def init([ifname, port]) do
send(self(), :open_dns)
# use charlist here because :inet module works with charlists
{:ok, %State{dns_port: port, ifname: to_charlist(ifname)}}
end
@impl GenServer
# open a UDP socket on port 53
def handle_info(:open_dns, state) do
case :gen_udp.open(state.dns_port, [:binary, active: true, reuseaddr: true]) do
{:ok, socket} ->
{:noreply, %State{state | dns_socket: socket}}
error ->
{:stop, error, state}
end
end
# binary dns message from the socket
def handle_info(
{:udp, socket, ip, port, packet},
%{dns_socket: socket} = state
) do
record = DNS.Record.decode(packet)
{answers, state} = handle_dns(record.qdlist, [], state)
response = DNS.Record.encode(%{record | anlist: answers})
_ = :gen_udp.send(socket, ip, port, response)
{:noreply, state}
end
# recursively check for dns queries, respond to each of them with the local ip address.
# respond to `a` with our current ip address
defp handle_dns(
[%{type: :a} = q | rest],
answers,
state
) do
ifname = state.ifname
{:ok, interfaces} = :inet.getifaddrs()
{^ifname, ifinfo} = List.keyfind(interfaces, ifname, 0)
addr =
Enum.find_value(ifinfo, fn
{:addr, {_, _, _, _} = ipv4_addr} -> ipv4_addr
_ -> false
end)
answer = make_record(q.domain, q.type, 120, addr)
handle_dns(rest, [answer | answers], state)
end
# stop recursing when qdlist is fully enumerated
defp handle_dns([], answers, state) do
{Enum.reverse(answers), state}
end
defp make_record(domain, type, ttl, data) do
%DNS.Resource{
domain: domain,
class: :in,
type: type,
ttl: ttl,
data: data
}
end
end
FarmbotOS.Configurator.CaptiveDNS.start_link("lo0", 4040)

View File

@ -92,6 +92,7 @@ defmodule FarmbotOS.MixProject do
{:cors_plug, "~> 2.0"},
{:plug_cowboy, "~> 2.1"},
{:phoenix_html, "~> 2.13"},
{:dns, "~> 2.1"},
# Nerves stuff.
{:nerves, "~> 1.5", runtime: false},

View File

@ -6,6 +6,7 @@ defmodule FarmbotOS.Platform.Target.Configurator.CaptivePortal do
@behaviour VintageNet.Technology
require FarmbotCore.Logger
alias FarmbotOS.Configurator.CaptiveDNS
@impl VintageNet.Technology
def normalize(%{vintage_net_wifi: _} = config) do
@ -24,7 +25,7 @@ defmodule FarmbotOS.Platform.Target.Configurator.CaptivePortal do
ifname
|> vintage_wifi(normalized, opts)
|> dnsmasq(opts)
|> add_captive_dns()
end
def to_raw_config(ifname, config, opts) do
@ -32,7 +33,7 @@ defmodule FarmbotOS.Platform.Target.Configurator.CaptivePortal do
ifname
|> vintage_ethernet(normalized, opts)
|> dnsmasq(opts)
|> add_captive_dns()
end
@impl VintageNet.Technology
@ -45,67 +46,12 @@ defmodule FarmbotOS.Platform.Target.Configurator.CaptivePortal do
VintageNetWiFi.ioctl(ifname, ioctl, args)
end
defp dnsmasq(
%{ifname: ifname, source_config: %{dnsmasq: config}} = raw_config,
opts
) do
tmpdir = Keyword.fetch!(opts, :tmpdir)
killall = Keyword.fetch!(opts, :bin_killall)
dnsmasq = System.find_executable("dnsmasq")
dnsmasq_conf_path = Path.join(tmpdir, "dnsmasq.conf.#{ifname}")
dnsmasq_lease_file = Path.join(tmpdir, "dnsmasq.leases.#{ifname}")
dnsmasq_pid_file = Path.join(tmpdir, "dnsmasq.pid.#{ifname}")
dnsmasq_conf_contents = """
interface=#{ifname}
except-interface=lo
localise-queries
bogus-priv
bind-interfaces
listen-address=#{config[:address]}
server=#{config[:address]}
address=/#/#{config[:address]}
dhcp-option=6,#{config[:address]}
dhcp-range=#{config[:start]},#{config[:end]},12h
"""
files = [
{dnsmasq_conf_path, dnsmasq_conf_contents}
defp add_captive_dns(%{ifname: ifname} = raw_config) do
child_specs = [
{CaptiveDNS, [ifname, 53]}
]
up_cmds = [
{:run, dnsmasq,
[
"-K",
"-l",
dnsmasq_lease_file,
"-x",
dnsmasq_pid_file,
"-C",
dnsmasq_conf_path
]}
]
down_cmds = [
{:run, killall, ["-q", "-9", "dnsmasq"]}
]
updated_raw_config = %{
raw_config
| files: raw_config.files ++ files,
up_cmds: raw_config.up_cmds ++ up_cmds,
down_cmds: raw_config.down_cmds ++ down_cmds,
cleanup_files:
raw_config.cleanup_files ++
[dnsmasq_conf_path, dnsmasq_lease_file, dnsmasq_pid_file]
}
updated_raw_config
end
defp dnsmasq(%{} = raw_config, _opts) do
FarmbotCore.Logger.error(1, "DNSMASQ Disabled")
raw_config
%{raw_config | child_specs: raw_config.child_specs ++ child_specs}
end
defp vintage_wifi(ifname, config, opts) do

View File

@ -36,10 +36,7 @@ defmodule FarmbotOS.Platform.Target.Network do
address: "192.168.24.1",
netmask: "255.255.255.0"
},
dnsmasq: %{
domain: "farmbot",
server: "192.168.24.1",
address: "192.168.24.1",
dhcpd: %{
start: "192.168.24.2",
end: "192.168.24.10"
}

View File

@ -0,0 +1,13 @@
defmodule FarmbotOS.Configurator.CaptiveDNSTest do
use ExUnit.Case, async: false
@dig System.find_executable("dig")
if @dig do
test "all dns queries resolve to local ip address" do
{:ok, dns_server} = FarmbotOS.Configurator.CaptiveDNS.start_link("lo0", 4040)
res = :os.cmd('dig -p 4040')
refute String.contains?(to_string(res), "no servers could be reached")
end
end
end