1 # Copyright (C) 2016 Bouygues Telecom.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
17 DHCPv6 packet parser/serializer
19 [RFC 3315] DHCPv6 packet format:
21 The following diagram illustrates the format of DHCP messages sent
22 between clients and servers::
25 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
26 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
27 | msg_type | transaction_id |
28 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
33 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
35 There are two relay agent messages, which share the following format::
38 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
39 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
40 | msg_type | hop_count | |
41 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
45 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
47 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
51 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
53 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
55 . options (variable number and length) .... .
57 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
62 from . import packet_base
63 from ryu.lib import addrconv
64 from ryu.lib import stringify
66 # DHCPv6 message types
76 DHCPV6_RECONFIGURE = 10
77 DHCPV6_INFORMATION_REQUEST = 11
78 DHCPV6_RELAY_FORW = 12
79 DHCPV6_RELAY_REPL = 13
82 DHCPV6_OPTION_CLIENTID = 1
83 DHCPV6_OPTION_SERVERID = 2
84 DHCPV6_OPTION_IA_NA = 3
85 DHCPV6_OPTION_IA_TA = 4
86 DHCPV6_OPTION_IAADDR = 5
88 DHCPV6_OPTION_PREFERENCE = 7
89 DHCPV6_OPTION_ELAPSED_TIME = 8
90 DHCPV6_OPTION_RELAY_MSG = 9
91 DHCPV6_OPTION_AUTH = 11
92 DHCPV6_OPTION_UNICAST = 12
93 DHCPV6_OPTION_STATUS_CODE = 13
94 DHCPV6_OPTION_RAPID_COMMIT = 14
95 DHCPV6_OPTION_USER_CLASS = 15
96 DHCPV6_OPTION_VENDOR_CLASS = 16
97 DHCPV6_OPTION_VENDOR_OPTS = 17
98 DHCPV6_OPTION_INTERFACE_ID = 18
99 DHCPV6_OPTION_RECONF_MSG = 19
100 DHCPV6_OPTION_RECONF_ACCEPT = 20
103 class dhcp6(packet_base.PacketBase):
104 """DHCPv6 (RFC 3315) header encoder/decoder class.
106 The serialized packet would looks like the ones described
107 in the following sections.
109 * RFC 3315 DHCP packet format
111 An instance has the following attributes at least.
112 Most of them are same to the on-wire counterparts but in host byte order.
113 __init__ takes the corresponding args in this order.
116 ============== ====================
117 Attribute Description
118 ============== ====================
119 msg_type Identifies the DHCP message type
120 transaction_id For unrelayed messages only: the transaction ID for\
121 this message exchange.
122 hop_count For relayed messages only: number of relay agents that\
123 have relayed this message.
124 link_address For relayed messages only: a global or site-local address\
125 that will be used by the server to identify the link on\
126 which the client is located.
127 peer_address For relayed messages only: the address of the client or\
128 relay agent from which the message to be relayed was\
130 options Options carried in this message
131 ============== ====================
134 _DHCPV6_UNPACK_STR = '!I'
135 _DHCPV6_RELAY_UNPACK_STR = '!H16s16s'
136 _DHCPV6_UNPACK_STR_LEN = struct.calcsize(_DHCPV6_UNPACK_STR)
137 _DHCPV6_RELAY_UNPACK_STR_LEN = struct.calcsize(_DHCPV6_RELAY_UNPACK_STR)
138 _DHCPV6_PACK_STR = '!I'
139 _DHCPV6_RELAY_PACK_STR = '!H16s16s'
141 def __init__(self, msg_type, options, transaction_id=None, hop_count=0,
142 link_address='::', peer_address='::'):
143 super(dhcp6, self).__init__()
144 self.msg_type = msg_type
145 self.options = options
146 if transaction_id is None:
147 self.transaction_id = random.randint(0, 0xffffff)
149 self.transaction_id = transaction_id
150 self.hop_count = hop_count
151 self.link_address = link_address
152 self.peer_address = peer_address
155 def parser(cls, buf):
156 (msg_type, ) = struct.unpack_from('!B', buf)
158 buf = b'\x00' + buf[1:] # unpack xid as a 4-byte integer
159 if msg_type == DHCPV6_RELAY_FORW or msg_type == DHCPV6_RELAY_REPL:
160 (hop_count, link_address, peer_address) \
161 = struct.unpack_from(cls._DHCPV6_RELAY_UNPACK_STR, buf)
162 length = struct.calcsize(cls._DHCPV6_RELAY_UNPACK_STR)
165 = struct.unpack_from(cls._DHCPV6_UNPACK_STR, buf)
166 length = struct.calcsize(cls._DHCPV6_UNPACK_STR)
168 if len(buf) > length:
169 parse_opt = options.parser(buf[length:])
170 length += parse_opt.options_len
171 if msg_type == DHCPV6_RELAY_FORW or msg_type == DHCPV6_RELAY_REPL:
172 return (cls(msg_type, parse_opt, 0, hop_count,
173 addrconv.ipv6.bin_to_text(link_address),
174 addrconv.ipv6.bin_to_text(peer_address)),
177 return (cls(msg_type, parse_opt, transaction_id),
180 return None, None, buf
182 def serialize(self, payload=None, prev=None):
183 seri_opt = self.options.serialize()
184 if (self.msg_type == DHCPV6_RELAY_FORW or
185 self.msg_type == DHCPV6_RELAY_REPL):
186 pack_str = '%s%ds' % (self._DHCPV6_RELAY_PACK_STR,
187 self.options.options_len)
188 buf = struct.pack(pack_str, self.hop_count,
189 addrconv.ipv6.text_to_bin(self.link_address),
190 addrconv.ipv6.text_to_bin(self.peer_address),
193 pack_str = '%s%ds' % (self._DHCPV6_PACK_STR,
194 self.options.options_len)
195 buf = struct.pack(pack_str, self.transaction_id, seri_opt)
196 return struct.pack('!B', self.msg_type) + buf[1:]
199 class options(stringify.StringifyMixin):
200 """DHCP (RFC 3315) options encoder/decoder class.
202 This is used with ryu.lib.packet.dhcp6.dhcp6.
205 def __init__(self, option_list=None, options_len=0):
206 super(options, self).__init__()
207 if option_list is None:
208 self.option_list = []
210 self.option_list = option_list
211 self.options_len = options_len
214 def parser(cls, buf):
217 while len(buf) > offset:
218 opt_buf = buf[offset:]
219 opt = option.parser(opt_buf)
220 opt_parse_list.append(opt)
221 offset += opt.length + 4
222 return cls(opt_parse_list, len(buf))
226 for opt in self.option_list:
227 seri_opt += opt.serialize()
228 if self.options_len == 0:
229 self.options_len = len(seri_opt)
233 class option(stringify.StringifyMixin):
234 """DHCP (RFC 3315) options encoder/decoder class.
236 This is used with ryu.lib.packet.dhcp6.dhcp6.options.
238 An instance has the following attributes at least.
239 Most of them are same to the on-wire counterparts but in host byte order.
240 __init__ takes the corresponding args in this order.
242 The format of DHCP options is::
245 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
246 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
247 | option-code | option-len |
248 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
250 | (option-len octets) |
251 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
253 ============== ====================
254 Attribute Description
255 ============== ====================
256 option-code An unsigned integer identifying the specific option\
257 type carried in this option.
258 option-len An unsigned integer giving the length of the\
259 option-data field in this option in octets.
260 option-data The data for the option; the format of this data\
261 depends on the definition of the option.
262 ============== ====================
265 _UNPACK_STR_LEN = struct.calcsize(_UNPACK_STR)
268 def __init__(self, code, data, length=0):
269 super(option, self).__init__()
275 def parser(cls, buf):
276 code = struct.unpack_from(cls._UNPACK_STR, buf)[0]
277 buf = buf[cls._UNPACK_STR_LEN:]
278 length = struct.unpack_from(cls._UNPACK_STR, buf)[0]
279 buf = buf[cls._UNPACK_STR_LEN:]
280 value_unpack_str = '%ds' % length
281 data = struct.unpack_from(value_unpack_str, buf)[0]
282 return cls(code, data, length)
286 self.length = len(self.data)
287 options_pack_str = self._PACK_STR % self.length
288 return struct.pack(options_pack_str, self.code, self.length, self.data)