diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..51a839c --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*~ +*.[oa] +*.beam +/ebin/*.app +/priv/*.so + +tetrapak/.local.cache + diff --git a/c_src/Makefile b/c_src/Makefile new file mode 100644 index 0000000..73fc2df --- /dev/null +++ b/c_src/Makefile @@ -0,0 +1,19 @@ +PRIVDIR := ../priv + +LD ?= ld +CC ?= gcc +RM ?= rm + +CFLAGS ?= -O2 + +override CFLAGS += -std=gnu99 -Wall -fpic +override LDFLAGS += -shared -fpic + +all : $(PRIVDIR)/dhcp_server.so + +$(PRIVDIR)/dhcp_server.so : dhcp_server.o + $(CC) $(LDFLAGS) -o $@ $^ + +clean: + $(RM) -f $(PRIVDIR)/dhcp_server.so dhcp_server.o + diff --git a/c_src/dhcp_server.c b/c_src/dhcp_server.c new file mode 100644 index 0000000..72183c7 --- /dev/null +++ b/c_src/dhcp_server.c @@ -0,0 +1,88 @@ +/* + * + * Erlang ioctl wrapper to inject ARP entries to kernel + * + * Copyright (c) 2011-2012 by Travelping GmbH + * Copyright (C) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "erl_driver.h" // for erl_errno_id + +static ERL_NIF_TERM atom_error; +static ERL_NIF_TERM atom_ok; + +static ERL_NIF_TERM arp_inject_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + struct arpreq arp; + struct sockaddr_in *in; + + ErlNifBinary ip; + int htype; + ErlNifBinary addr; + ErlNifBinary ifname; + int fd; + + if (!enif_inspect_binary(env, argv[0], &ifname) + || ifname.size > 16 + || !enif_inspect_binary(env, argv[1], &ip) + || ip.size != 4 + || !enif_get_int(env, argv[2], &htype) + || !enif_inspect_binary(env, argv[3], &addr) + || addr.size > 16 + || !enif_get_int(env, argv[4], &fd)) + return enif_make_badarg(env); + + memset(&arp, 0, sizeof(arp)); + in = (struct sockaddr_in *)&arp.arp_pa; + in->sin_family = AF_INET; + memcpy(&in->sin_addr.s_addr, ip.data, 4); + + arp.arp_ha.sa_family = htype; + memcpy(arp.arp_ha.sa_data, addr.data, addr.size); + memcpy(arp.arp_dev, ifname.data, ifname.size); + arp.arp_flags = ATF_COM; + + if (ioctl(fd, SIOCSARP, &arp) < 0) { + return enif_make_tuple2(env, atom_error, enif_make_atom(env, erl_errno_id(errno))); + } + + return atom_ok; +} + +static ErlNifFunc nif_funcs[] = { + {"arp_inject_nif", 5, arp_inject_nif} +}; + +static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) +{ + atom_error = enif_make_atom(env,"error"); + atom_ok = enif_make_atom(env,"ok"); + + return 0; +} + +ERL_NIF_INIT(dhcp_server, nif_funcs, load, NULL, NULL, NULL) diff --git a/priv/dhcp.conf b/priv/dhcp.conf index 13da75c..853c79c 100644 --- a/priv/dhcp.conf +++ b/priv/dhcp.conf @@ -3,58 +3,21 @@ %%% %%% Global configuration -%%{server_id, {47,80,19,32}}. +{server_id, {172,20,48,1}}. %%{next_server, {47,80,19,32}}. +{interface, <<"tap0">>}. {authoritative, true}. -{log_facility, local7}. -{logfile, "/var/log/dhcp.log"}. {lease_file, "/var/run/dhcp_leases.dets"}. {netns, "/var/run/netns/core"}. %%% Hub subnet {subnet, - {192,168,1,0}, %% Network + {172,20,48,0}, %% Network {255,255,255,0}, %% Netmask - {{192,168,1,5},{192,168,1,10}}, %% Range + {{172,20,48,5},{172,20,48,100}}, %% Range [{1,{255,255,255,0}}, %% Subnet Mask - {28,{192,168,1,255}}, %% Broadcast Address - {3,[{192,168,1,1}]}, %% Routers - {15,"us.nortel.com"}, %% Domain Name - {6,[{47,80,19,33}]}, %% Domain Name Servers + {28,{172,20,48,255}}, %% Broadcast Address + {3,[{192,20,48,1}]}, %% Routers + {15,"wlan"}, %% Domain Name + {6,[{172,20,48,1}]}, %% Domain Name Servers {51,3600}]}. %% Address Lease Time - -%%% Red subnet -{subnet, - {192,168,10,0}, %% Network - {255,255,255,0}, %% Netmask - {{192,168,10,5},{192,168,10,10}}, %% Range - [{1,{255,255,255,0}}, %% Subnet Mask - {28,{192,168,10,255}}, %% Broadcast Address - {3,[{192,168,10,1}]}, %% Routers - {15,"us.nortel.com"}, %% Domain Name - {6,[{47,80,19,33}]}, %% Domain Name Servers - {51,3600}]}. %% Address Lease Time - -%%% Yellow subnet -{subnet, - {192,168,20,0}, %% Network - {255,255,255,0}, %% Netmask - {{192,168,20,5},{192,168,20,10}}, %% Range - [{1,{255,255,255,0}}, %% Subnet Mask - {28,{192,168,20,255}}, %% Broadcast Address - {3,[{192,168,20,1}]}, %% Routers - {15,"us.nortel.com"}, %% Domain Name - {6,[{47,80,19,30}]}, %% Domain Name Servers - {51,7200}]}. %% Address Lease Time - -%%% Green subnet -{subnet, - {192,168,30,0}, %% Network - {255,255,255,0}, %% Netmask - {{192,168,30,5},{192,168,30,10}}, %% Range - [{1,{255,255,255,0}}, %% Subnet Mask - {28,{192,168,30,255}}, %% Broadcast Address - {3,[{192,168,30,1}]}, %% Routers - {15,"us.nortel.com"}, %% Domain Name - {6,[{47,80,19,30}]}, %% Domain Name Servers - {51,86400}]}. %% Address Lease Time diff --git a/src/dhcp.app.src b/src/dhcp.app.src index 201a2bd..5e29f28 100644 --- a/src/dhcp.app.src +++ b/src/dhcp.app.src @@ -3,6 +3,6 @@ {vsn, git}, {modules, [dhcp_app, dhcp_sup, dhcp_server, dhcp_alloc, dhcp_lib]}, {registered, [dhcp_sup, dhcp_server, dhcp_alloc]}, - {applications, [kernel, stdlib]}, + {applications, [kernel, stdlib, lager]}, {mod, {dhcp_app, []}} ]}. diff --git a/src/dhcp_app.erl b/src/dhcp_app.erl index 66a6244..8644012 100644 --- a/src/dhcp_app.erl +++ b/src/dhcp_app.erl @@ -19,15 +19,16 @@ %% Function: start(Type, StartArgs) -> {ok, Pid} | %% {ok, Pid, State} | %% {error, Reason} -%% Description: This function is called whenever an application +%% Description: This function is called whenever an application %% is started using application:start/1,2, and should start the processes %% of the application. If the application is structured according to the %% OTP design principles as a supervision tree, this means starting the %% top supervisor of the tree. %%-------------------------------------------------------------------- start(_Type, _StartArgs) -> + lager:set_loglevel(lager_console_backend, debug), case dhcp_sup:start_link() of - {ok, Pid} -> + {ok, Pid} -> {ok, Pid}; Error -> Error @@ -37,7 +38,7 @@ start(_Type, _StartArgs) -> %% Function: stop(State) -> void() %% Description: This function is called whenever an application %% has stopped. It is intended to be the opposite of Module:start/2 and -%% should do any necessary cleaning up. The return value is ignored. +%% should do any necessary cleaning up. The return value is ignored. %%-------------------------------------------------------------------- stop(_State) -> ok. diff --git a/src/dhcp_lib.erl b/src/dhcp_lib.erl index 7a86cfa..81eb349 100644 --- a/src/dhcp_lib.erl +++ b/src/dhcp_lib.erl @@ -1,7 +1,7 @@ %%%------------------------------------------------------------------- %%% File : dhcp_lib.erl %%% Author : Ruslan Babayev -%%% Description : +%%% Description : %%% %%% Created : 17 Apr 2006 by Ruslan Babayev %%%------------------------------------------------------------------- @@ -9,6 +9,7 @@ %% API -export([decode/1, encode/1]). +-export([ip_to_binary/1, eth_to_binary/1]). -import(lists, [keymember/3, keysearch/3, keyreplace/4]). -include("dhcp.hrl"). @@ -16,7 +17,7 @@ %% API %%==================================================================== %%-------------------------------------------------------------------- -%% Function: +%% Function: %% Description: %%-------------------------------------------------------------------- decode(< + LibDir = filename:join([filename:dirname(code:which(?MODULE)), "..", "priv"]), --record(state, {socket, server_id, next_server}). + %% load our nif library + case erlang:load_nif(filename:join(LibDir, "dhcp_server"), 0) of + ok -> + ok; + {error, {reload, _}} -> + ok; + {error, Error} -> + error_logger:error_msg("could not load dhcp_server nif library: ~p", [Error]), + error({load_nif, Error}) + end. %%==================================================================== %% API @@ -31,9 +51,9 @@ %% Function: start_link() -> {ok,Pid} | ignore | {error,Error} %% Description: Starts the server %%-------------------------------------------------------------------- -start_link(ServerId, NextServer, LogFile, NetNameSpace) -> +start_link(NetNameSpace, Interface, ServerId, NextServer) -> gen_server:start_link({local, ?SERVER}, ?MODULE, - [ServerId, NextServer, LogFile, NetNameSpace], []). + [NetNameSpace, Interface, ServerId, NextServer], []). %%==================================================================== %% gen_server callbacks @@ -46,18 +66,18 @@ start_link(ServerId, NextServer, LogFile, NetNameSpace) -> %% {stop, Reason} %% Description: Initiates the server %%-------------------------------------------------------------------- -init([ServerId, NextServer, LogFile, NetNameSpace]) -> - error_logger:logfile({open, LogFile}), - Options = get_sockopt(NetNameSpace), +init([NetNameSpace, Interface, ServerId, NextServer]) -> + Options = get_sockopt(NetNameSpace, Interface), io:format("Opts: ~p~n", [Options]), case gen_udp:open(?DHCP_SERVER_PORT, Options) of {ok, Socket} -> - error_logger:info_msg("Starting DHCP server..."), - {ok, #state{socket = Socket, + lager:info("Starting DHCP server..."), + {ok, #state{if_name = Interface, + socket = Socket, server_id = ServerId, next_server = NextServer}}; {error, Reason} -> - error_logger:error_msg("Cannot open udp port ~w", + lager:error("Cannot open udp port ~w", [?DHCP_SERVER_PORT]), {stop, Reason} end. @@ -90,11 +110,21 @@ handle_cast(_Msg, State) -> %% {stop, Reason, State} %% Description: Handling all non call/cast messages %%-------------------------------------------------------------------- -handle_info({udp, Socket, _IP, _Port, Packet}, State) -> - DHCP = dhcp_lib:decode(Packet), - case optsearch(?DHO_DHCP_MESSAGE_TYPE, DHCP) of +handle_info({udp, Socket, IP, Port, Packet}, State = #state{socket = Socket}) -> + Source = {IP, Port}, + Request = dhcp_lib:decode(Packet), + case optsearch(?DHO_DHCP_MESSAGE_TYPE, Request) of {value, MsgType} -> - handle_dhcp(MsgType, DHCP, Socket, State); + case handle_dhcp(MsgType, Request, State) of + ok -> + ok; + {reply, Reply} -> + send_reply(Source, MsgType, Reply, State); + {error, Reason} -> + lager:error(Reason); + Other -> + lager:debug("DHCP result: ~w", [Other]) + end; false -> ok end, @@ -110,7 +140,6 @@ handle_info(_Info, State) -> %% The return value is ignored. %%-------------------------------------------------------------------- terminate(_Reason, State) -> - error_logger:logfile(close), gen_udp:close(State#state.socket), ok. @@ -128,21 +157,21 @@ code_change(_OldVsn, State, _Extra) -> %%%------------------------------------------------------------------- %%% The DHCP message handler %%%------------------------------------------------------------------- -handle_dhcp(?DHCPDISCOVER, D, Socket, State) -> - error_logger:info_msg("DHCPDISCOVER from ~s ~s ~s", +handle_dhcp(?DHCPDISCOVER, D, State) -> + lager:info("DHCPDISCOVER from ~s ~s ~s", [fmt_clientid(D), fmt_hostname(D), fmt_gateway(D)]), ClientId = get_client_id(D), Gateway = D#dhcp.giaddr, RequestedIP = get_requested_ip(D), case dhcp_alloc:reserve(ClientId, Gateway, RequestedIP) of {ok, IP, Options} -> - send_offer(State, Socket, D, IP, Options); - {error, Reason} -> - error_logger:error_msg(Reason) + offer(D, IP, Options, State); + Other -> + Other end; -handle_dhcp(?DHCPREQUEST, D, Socket, State) -> +handle_dhcp(?DHCPREQUEST, D, State) -> ClientId = get_client_id(D), - error_logger:info_msg("DHCPREQUEST from ~s ~s ~s", + lager:info("DHCPREQUEST from ~s ~s ~s", [fmt_clientid(D), fmt_hostname(D), fmt_gateway(D)]), case client_state(D) of {selecting, ServerId} -> @@ -151,9 +180,9 @@ handle_dhcp(?DHCPREQUEST, D, Socket, State) -> IP = get_requested_ip(D), case dhcp_alloc:allocate(ClientId, IP) of {ok, IP, Options} -> - send_ack(State, Socket, D, IP, Options); - {error, Reason} -> - error_logger:error_msg(Reason) + ack(D, IP, Options, State); + Other -> + Other end; _ -> %% Client selected someone else, do nothing @@ -163,46 +192,48 @@ handle_dhcp(?DHCPREQUEST, D, Socket, State) -> Gateway = D#dhcp.giaddr, case dhcp_alloc:verify(ClientId, Gateway, RequestedIP) of {ok, IP, Options} -> - send_ack(State, Socket, D, IP, Options); + ack(D, IP, Options, State); noclient -> - error_logger:error_msg("Client ~s has no current bindings", - [fmt_clientid(D)]); + lager:error("Client ~s has no current bindings", + [fmt_clientid(D)]), + ok; {error, Reason} -> - send_nak(State, Socket, D, Reason) + nak(D, Reason, State) end; {ClientIs, IP} when ClientIs == renewing; ClientIs == rebinding -> case dhcp_alloc:extend(ClientId, IP) of {ok, IP, Options} -> - send_ack(State, Socket, D, IP, Options); + ack(D, IP, Options, State); {error, Reason} -> - send_nak(State, Socket, D, Reason) + nak(D, Reason, State) end end; -handle_dhcp(?DHCPDECLINE, D, _Socket, _State) -> +handle_dhcp(?DHCPDECLINE, D, _State) -> IP = get_requested_ip(D), - error_logger:info_msg("DHCPDECLINE of ~s from ~s ~s", + lager:info("DHCPDECLINE of ~s from ~s ~s", [fmt_ip(IP), fmt_clientid(D), fmt_hostname(D)]), dhcp_alloc:decline(IP); -handle_dhcp(?DHCPRELEASE, D, _Socket, _State) -> +handle_dhcp(?DHCPRELEASE, D, _State) -> ClientId = get_client_id(D), - error_logger:info_msg("DHCPRELEASE of ~s from ~s ~s ~s", + lager:info("DHCPRELEASE of ~s from ~s ~s ~s", [fmt_ip(D#dhcp.ciaddr), fmt_clientid(D), fmt_hostname(D), fmt_gateway(D)]), dhcp_alloc:release(ClientId, D#dhcp.ciaddr); -handle_dhcp(?DHCPINFORM, D, Socket, State) -> +handle_dhcp(?DHCPINFORM, D, State) -> Gateway = D#dhcp.giaddr, IP = D#dhcp.ciaddr, - error_logger:info_msg("DHCPINFORM from ~s", [fmt_ip(IP)]), + lager:info("DHCPINFORM from ~s", [fmt_ip(IP)]), case dhcp_alloc:local_conf(Gateway) of {ok, Opts} -> %% No Lease Time (RFC2131 sec. 4.3.5) OptsSansLease = lists:keydelete(?DHO_DHCP_LEASE_TIME, 1, Opts), - send_ack(State, Socket, D, IP, OptsSansLease); - {error, Reason} -> - error_logger:error_msg(Reason) + ack(D, IP, OptsSansLease, State); + Other -> + Other end; -handle_dhcp(MsgType, _D, _Socket, _State) -> - error_logger:error_msg("Invalid DHCP message type ~p", [MsgType]). +handle_dhcp(MsgType, _D, _State) -> + lager:error("Invalid DHCP message type ~p", [MsgType]), + ok. client_state(D) when is_record(D, dhcp) -> case optsearch(?DHO_DHCP_SERVER_IDENTIFIER, D) of @@ -213,7 +244,7 @@ client_state(D) when is_record(D, dhcp) -> {value, RequestedIP} -> {init_reboot, RequestedIP}; false -> - case is_broadcast(D) of + case ?is_broadcast(D) of false -> {renewing, D#dhcp.ciaddr}; _ -> @@ -222,79 +253,88 @@ client_state(D) when is_record(D, dhcp) -> end end. -send_offer(S, Socket, D, IP, Options) -> - DHCPOffer = D#dhcp{ - op = ?BOOTREPLY, - hops = 0, - secs = 0, - ciaddr = {0, 0, 0, 0}, - yiaddr = IP, - siaddr = S#state.next_server, - options = [{?DHO_DHCP_MESSAGE_TYPE, ?DHCPOFFER}, - {?DHO_DHCP_SERVER_IDENTIFIER, S#state.server_id} | - Options]}, - error_logger:info_msg("DHCPOFFER on ~s to ~s ~s ~s", - [fmt_ip(IP), fmt_clientid(D), - fmt_hostname(D), fmt_gateway(D)]), - {IP, Port} = get_dest(DHCPOffer), - gen_udp:send(Socket, IP, Port, dhcp_lib:encode(DHCPOffer)). - -send_ack(S, Socket, D, IP, Options) -> - DHCPAck = D#dhcp{ - op = ?BOOTREPLY, - hops = 0, - secs = 0, - yiaddr = IP, - siaddr = S#state.next_server, - options = [{?DHO_DHCP_MESSAGE_TYPE, ?DHCPACK}, - {?DHO_DHCP_SERVER_IDENTIFIER, S#state.server_id} | - Options]}, - error_logger:info_msg("DHCPACK on ~s to ~s ~s ~s", +-define(reply(DHCP), {reply, DHCP}). +reply(MsgType, D, Opts, #state{server_id = ServerId}) -> + {reply, D#dhcp{ + op = ?BOOTREPLY, + hops = 0, + secs = 0, + options = [{?DHO_DHCP_MESSAGE_TYPE, MsgType}, + {?DHO_DHCP_SERVER_IDENTIFIER, ServerId} | + Opts]}}. + +offer(D, IP, Options, State = #state{next_server = NextServer}) -> + lager:info("DHCPOFFER on ~s to ~s ~s ~s", + [fmt_ip(IP), fmt_clientid(D), + fmt_hostname(D), fmt_gateway(D)]), + reply(?DHCPOFFER, D#dhcp{ciaddr = ?INADDR_ANY, + yiaddr = IP, + siaddr = NextServer + }, + Options, State). + +ack(D, IP, Options, State = #state{next_server = NextServer}) -> + lager:info("DHCPACK on ~s to ~s ~s ~s", [fmt_ip(IP), fmt_clientid(D), fmt_hostname(D), fmt_gateway(D)]), - {IP, Port} = get_dest(DHCPAck), - gen_udp:send(Socket, IP, Port, dhcp_lib:encode(DHCPAck)). - -send_nak(S, Socket, D, Reason) -> - DHCPNak = D#dhcp{ - op = ?BOOTREPLY, - hops = 0, - secs = 0, - ciaddr = {0, 0, 0, 0}, - yiaddr = {0, 0, 0, 0}, - siaddr = {0, 0, 0, 0}, - flags = D#dhcp.flags bor 16#8000, %% set broadcast bit - options = [{?DHO_DHCP_MESSAGE_TYPE, ?DHCPNAK}, - {?DHO_DHCP_SERVER_IDENTIFIER, S#state.server_id}, - {?DHO_DHCP_MESSAGE, Reason}]}, - error_logger:info_msg("DHCPNAK to ~s ~s ~s. ~s", + + reply(?DHCPACK, D#dhcp{yiaddr = IP, + siaddr = NextServer + }, + Options, State). + +nak(D, Reason, State) -> + lager:info("DHCPNAK to ~s ~s ~s. ~s", [fmt_clientid(D), fmt_hostname(D), fmt_gateway(D), Reason]), - {IP, Port} = get_dest(D), - gen_udp:send(Socket, IP, Port, dhcp_lib:encode(DHCPNak)). + reply(?DHCPNAK, D#dhcp{ciaddr = ?INADDR_ANY, + yiaddr = ?INADDR_ANY, + siaddr = ?INADDR_ANY, + flags = D#dhcp.flags bor 16#8000 %% set broadcast bit + }, + [{?DHO_DHCP_MESSAGE, Reason}], State). + +send_reply(Source, MsgType, Reply, State) -> + {DstIP, DstPort} = get_dest(Source, MsgType, Reply, State), + lager:debug("Sending DHCP Reply to: ~s:~w", [fmt_ip(DstIP), DstPort]), + gen_udp:send(State#state.socket, DstIP, DstPort, dhcp_lib:encode(Reply)). + +arp_inject_nif(_IfName, _IP, _Type, _Addr, _FD) -> error(nif_not_loaded). + +arp_inject(IP, Type, Addr, #state{if_name = IfName, socket = Socket}) -> + {ok, FD} = inet:getfd(Socket), + lager:debug("FD: ~w", [FD]), + arp_inject_nif(IfName, dhcp_lib:ip_to_binary(IP), Type, dhcp_lib:eth_to_binary(Addr), FD). %%% Behaviour is described in RFC2131 sec. 4.1 -get_dest(D) when is_record(D, dhcp) -> - IP = case D#dhcp.giaddr of - {0, 0, 0, 0} -> - case D#dhcp.ciaddr of - {0, 0, 0, 0} -> - case is_broadcast(D) of - true -> {255, 255, 255, 255}; - _ -> D#dhcp.yiaddr - end; - CiAddr -> CiAddr - end; - GiAddr -> GiAddr - end, - Port = case D#dhcp.giaddr of - {0, 0, 0, 0} -> ?DHCP_CLIENT_PORT; - _ -> ?DHCP_SERVER_PORT - end, - {IP, Port}. - -is_broadcast(D) when is_record(D, dhcp) -> - (D#dhcp.flags bsr 15) == 1. +get_dest(Source = {SrcIP, SrcPort}, MsgType, Reply, State) + when is_record(Reply, dhcp) -> + if Reply#dhcp.giaddr =/= ?INADDR_ANY -> + lager:debug("get_dest: #1"), + {Reply#dhcp.giaddr, ?DHCP_SERVER_PORT}; + + Reply#dhcp.ciaddr =/= ?INADDR_ANY -> + lager:debug("get_dest: #2"), + if (MsgType =/= ?DHCPINFORM andalso SrcIP =/= Reply#dhcp.ciaddr) + orelse SrcIP == ?INADDR_ANY orelse SrcPort == 0 -> + {Reply#dhcp.ciaddr, ?DHCP_CLIENT_PORT}; + true -> + Source + end; + + ?is_broadcast(Reply) -> + lager:debug("get_dest: #3"), + {?INADDR_BROADCAST, ?DHCP_CLIENT_PORT}; + + Reply#dhcp.yiaddr =/= ?INADDR_ANY -> + lager:debug("get_dest: #4"), + arp_inject(Reply#dhcp.yiaddr, Reply#dhcp.htype, Reply#dhcp.chaddr, State), + {Reply#dhcp.yiaddr, ?DHCP_CLIENT_PORT}; + + true -> + lager:debug("get_dest: #5"), + Source + end. optsearch(Option, D) when is_record(D, dhcp) -> case lists:keysearch(Option, 1, D#dhcp.options) of @@ -303,7 +343,7 @@ optsearch(Option, D) when is_record(D, dhcp) -> false -> false end. - + get_client_id(D) when is_record(D, dhcp) -> case optsearch(?DHO_DHCP_CLIENT_IDENTIFIER, D) of {value, ClientId} -> @@ -317,7 +357,7 @@ get_requested_ip(D) when is_record(D, dhcp) -> {value, IP} -> IP; false -> - {0, 0, 0, 0} + ?INADDR_ANY end. fmt_clientid(D) when is_record(D, dhcp) -> @@ -331,8 +371,8 @@ fmt_clientid({E1, E2, E3, E4, E5, E6}) -> fmt_gateway(D) when is_record(D, dhcp) -> case D#dhcp.giaddr of - {0, 0, 0, 0} -> []; - IP -> lists:flatten(io_lib:format("via ~s", [fmt_ip(IP)])) + ?INADDR_ANY -> []; + IP -> lists:flatten(io_lib:format("via ~s", [fmt_ip(IP)])) end. fmt_hostname(D) when is_record(D, dhcp) -> @@ -346,17 +386,27 @@ fmt_hostname(D) when is_record(D, dhcp) -> fmt_ip({A1, A2, A3, A4}) -> io_lib:format("~w.~w.~w.~w", [A1, A2, A3, A4]). -get_nsopts(NetNameSpace) +get_nsopts(NetNameSpace, Opts) when is_binary(NetNameSpace); is_list(NetNameSpace) -> - [{netns, NetNameSpace}]; -get_nsopts(_) -> - []. + [{netns, NetNameSpace} | Opts]; +get_nsopts(_, Opts) -> + Opts. -get_sockopt(NetNameSpace) -> - NsOpts = get_nsopts(NetNameSpace), + +get_ifopts(Interface, Opts) when is_binary(Interface) -> + %% setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, nic, IF_NAMESIZE); + [{raw, 1, 25, Interface} | Opts]. + +get_fdopts(Opts) -> case init:get_argument(fd) of {ok, [[FD]]} -> - [binary, {broadcast, true}, {fd, list_to_integer(FD)}|NsOpts]; + [{fd, list_to_integer(FD)} | Opts]; error -> - [binary, {broadcast, true}|NsOpts] + Opts end. + +get_sockopt(NetNameSpace, Interface) -> + Opts = [binary, {broadcast, true}], + Opts0 = get_nsopts(NetNameSpace, Opts), + Opts1 = get_ifopts(Interface, Opts0), + get_fdopts(Opts1). diff --git a/src/dhcp_sup.erl b/src/dhcp_sup.erl index d52107d..08ee665 100644 --- a/src/dhcp_sup.erl +++ b/src/dhcp_sup.erl @@ -20,7 +20,6 @@ -include("dhcp_alloc.hrl"). -define(SERVER, ?MODULE). --define(DHCP_LOGFILE, "/var/log/dhcp.log"). -define(DHCP_LEASEFILE, "/var/run/dhcp_leases.dets"). %%==================================================================== @@ -47,9 +46,9 @@ start_link() -> %%-------------------------------------------------------------------- init([]) -> case get_config() of - {ok, ServerId, NextServer, LogFile, NetNameSpace, LeaseFile, Subnets, Hosts} -> + {ok, NetNameSpace, Interface, ServerId, NextServer, LeaseFile, Subnets, Hosts} -> DHCPServer = {dhcp_server, {dhcp_server, start_link, - [ServerId, NextServer, LogFile, NetNameSpace]}, + [NetNameSpace, Interface, ServerId, NextServer]}, permanent, 2000, worker, [dhcp_server]}, DHCPAlloc = {dhcp_alloc, {dhcp_alloc, start_link, [LeaseFile, Subnets, Hosts]}, @@ -69,14 +68,14 @@ get_config() -> end, case file:consult(filename:join(ConfDir, "dhcp.conf")) of {ok, Terms} -> + NetNameSpace = proplists:get_value(netns, Terms), + Interface = proplists:get_value(interface, Terms), ServerId = proplists:get_value(server_id, Terms, {0, 0, 0, 0}), NextServer = proplists:get_value(next_server, Terms, {0, 0, 0, 0}), - LogFile = proplists:get_value(logfile, Terms, ?DHCP_LOGFILE), LeaseFile = proplists:get_value(lease_file, Terms, ?DHCP_LEASEFILE), - NetNameSpace = proplists:get_value(netns, Terms), Subnets = [X || X <- Terms, is_record(X, subnet)], Hosts = [X || X <- Terms, is_record(X, host)], - {ok, ServerId, NextServer, LogFile, NetNameSpace, LeaseFile, Subnets, Hosts}; + {ok, NetNameSpace, Interface, ServerId, NextServer, LeaseFile, Subnets, Hosts}; {error, Reason} -> {error, Reason} end. diff --git a/tetrapak/build_nif.erl b/tetrapak/build_nif.erl new file mode 100644 index 0000000..d9c814e --- /dev/null +++ b/tetrapak/build_nif.erl @@ -0,0 +1,11 @@ +-task({"build:nif", "Build the ejson NIF library"}). +-task({"clean:nif", "Clean the ejson NIF library"}). + +run("build:nif", _) -> + tetrapak:outputcmd(tetrapak:subdir("c_src"), "make", [cflags(), "all"]); + +run("clean:nif", _) -> + tetrapak:outputcmd(tetrapak:subdir("c_src"), "make", [cflags(), "clean"]). + +cflags() -> + ["CFLAGS=", "-O2 ", ["-I", code:root_dir(), "/erts-", erlang:system_info(version), "/include"]]. diff --git a/tetrapak/config.ini b/tetrapak/config.ini new file mode 100644 index 0000000..fd56054 --- /dev/null +++ b/tetrapak/config.ini @@ -0,0 +1,2 @@ +[build] +erlc_options = [{parse_transform, lager_transform}]