/
message.ex
116 lines (104 loc) · 3.32 KB
/
message.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
defmodule OneDHCPD.Message do
alias OneDHCPD.Options
import OneDHCPD.Utility
@magic_cookie <<99, 130, 83, 99>>
@bootrequest 1
@bootreply 2
@htype_ether 1
# @htype_ieee802 6
# @htype_fddi 8
@moduledoc """
DHCP Message
See [RFC2131](https://tools.ietf.org/html/rfc2131) and associated docs
for details. This implementation is only intended to be complete enough
to support the OneDHCPD use case.
"""
defstruct op: @bootrequest,
htype: @htype_ether,
hops: 0,
xid: 0,
secs: 0,
broadcast_flag: 0,
ciaddr: {0, 0, 0, 0},
yiaddr: {0, 0, 0, 0},
siaddr: {0, 0, 0, 0},
giaddr: {0, 0, 0, 0},
chaddr: [0, 0, 0, 0, 0, 0],
options: []
@type t :: %__MODULE__{
op: integer(),
htype: integer(),
hops: integer(),
xid: integer(),
secs: integer(),
broadcast_flag: integer(),
ciaddr: :inet.ip4_address(),
yiaddr: :inet.ip4_address(),
siaddr: :inet.ip4_address(),
giaddr: :inet.ip4_address(),
chaddr: [byte()],
options: Keyword.t()
}
@doc """
Create a response to a request with some fields filled in.
See RFC 2131 Table 3 for requirements. The caller is responsible for
most of the fields.
"""
@spec response(t()) :: t()
def response(request = %__MODULE__{}) do
%__MODULE__{
op: @bootreply,
htype: request.htype,
hops: 0,
xid: request.xid,
secs: 0,
broadcast_flag: request.broadcast_flag,
giaddr: request.giaddr,
chaddr: request.chaddr
}
end
@doc """
Decode the contents of a UDP packet
"""
@spec decode(binary()) :: {:error, any()} | t()
def decode(
<<op, htype, _hlen, hops, xid::size(32), secs::size(16), broadcast_flag::size(1),
0::size(15), ciaddr::binary-size(4), yiaddr::binary-size(4), siaddr::binary-size(4),
giaddr::binary-size(4), chaddr::binary-size(6), 0::size(80), _sname::binary-size(64),
_file::binary-size(128), @magic_cookie, options::binary>>
) do
%__MODULE__{
op: op,
htype: htype,
hops: hops,
xid: xid,
secs: secs,
broadcast_flag: broadcast_flag,
ciaddr: decode_ip(ciaddr),
yiaddr: decode_ip(yiaddr),
siaddr: decode_ip(siaddr),
giaddr: decode_ip(giaddr),
chaddr: decode_hwaddr(chaddr),
options: Options.decode(options)
}
end
def decode(_), do: {:error, :not_dhcp}
@doc """
Encode a message so that it can be put in a UDP packet
"""
@spec encode(t()) :: binary()
def encode(message = %__MODULE__{}) do
options = Options.encode(message.options)
ciaddr = encode_ip_raw(message.ciaddr)
yiaddr = encode_ip_raw(message.yiaddr)
siaddr = encode_ip_raw(message.siaddr)
giaddr = encode_ip_raw(message.giaddr)
hlen = Enum.count(message.chaddr)
chaddr = encode_hwaddr_raw(message.chaddr)
<<message.op, message.htype, hlen, message.hops, message.xid::size(32),
message.secs::size(16), message.broadcast_flag::size(1), 0::size(15),
ciaddr::binary-size(4), yiaddr::binary-size(4), siaddr::binary-size(4),
giaddr::binary-size(4), chaddr::binary-size(6), 0::size(80), 0::unit(8)-size(192),
@magic_cookie, options::binary>>
end
end