backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / lib / packet / dhcp.py
1 # Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
2 #
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
6 #
7 #    http://www.apache.org/licenses/LICENSE-2.0
8 #
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
12 # implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 """
17 DHCP packet parser/serializer
18 """
19 # RFC 2131
20 # DHCP packet format
21 #  0                   1                   2                   3
22 #  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
23 #  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
24 #  |     op (1)    |   htype (1)   |   hlen (1)    |   hops (1)    |
25 #  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
26 #  |                            xid (4)                            |
27 #  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
28 #  |           secs (2)            |           flags (2)           |
29 #  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
30 #  |                          ciaddr  (4)                          |
31 #  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
32 #  |                          yiaddr  (4)                          |
33 #  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
34 #  |                          siaddr  (4)                          |
35 #  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
36 #  |                          giaddr  (4)                          |
37 #  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
38 #  |                                                               |
39 #  |                          chaddr  (16)                         |
40 #  |                                                               |
41 #  |                                                               |
42 #  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
43 #  |                                                               |
44 #  |                          sname   (64)                         |
45 #  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
46 #  |                                                               |
47 #  |                          file    (128)                        |
48 #  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
49 #  |                                                               |
50 #  |                          options (variable)                   |
51 #  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
52
53 import random
54 import struct
55
56 import netaddr
57
58 from ryu.lib import addrconv
59 from ryu.lib import stringify
60 from . import packet_base
61
62 DHCP_BOOT_REQUEST = 1
63 DHCP_BOOT_REPLY = 2
64
65 # DHCP message type code
66 DHCP_DISCOVER = 1
67 DHCP_OFFER = 2
68 DHCP_REQUEST = 3
69 DHCP_ACK = 5
70
71 # DHCP options tag code
72 DHCP_PAD_OPT = 0
73 DHCP_SUBNET_MASK_OPT = 1
74 DHCP_GATEWAY_ADDR_OPT = 3
75 DHCP_DNS_SERVER_ADDR_OPT = 6
76 DHCP_HOST_NAME_OPT = 12
77 DHCP_DOMAIN_NAME_OPT = 15
78 DHCP_INTERFACE_MTU_OPT = 26
79 DHCP_REQUESTED_IP_ADDR_OPT = 50
80 DHCP_IP_ADDR_LEASE_TIME_OPT = 51
81 DHCP_MESSAGE_TYPE_OPT = 53
82 DHCP_SERVER_IDENTIFIER_OPT = 54
83 DHCP_PARAMETER_REQUEST_LIST_OPT = 55
84 DHCP_RENEWAL_TIME_OPT = 58
85 DHCP_REBINDING_TIME_OPT = 59
86 DHCP_CLASSLESS_ROUTE_OPT = 121
87 DHCP_END_OPT = 255
88
89
90 class dhcp(packet_base.PacketBase):
91     """DHCP (RFC 2131) header encoder/decoder class.
92
93     The serialized packet would looks like the ones described
94     in the following sections.
95
96     * RFC 2131 DHCP packet format
97
98     An instance has the following attributes at least.
99     Most of them are same to the on-wire counterparts but in host byte order.
100     __init__ takes the corresponding args in this order.
101
102     .. tabularcolumns:: |l|L|
103
104     ============== ====================
105     Attribute      Description
106     ============== ====================
107     op             Message op code / message type.\
108                    1 = BOOTREQUEST, 2 = BOOTREPLY
109     htype          Hardware address type (e.g.  '1' = 10mb ethernet).
110     hlen           Hardware address length (e.g.  '6' = 10mb ethernet).
111     hops           Client sets to zero, optionally used by relay agent\
112                    when booting via a relay agent.
113     xid            Transaction ID, a random number chosen by the client,\
114                    used by the client and serverto associate messages\
115                    and responses between a client and a server.
116     secs           Filled in by client, seconds elapsed since client\
117                    began address acquisition or renewal process.
118     flags          Flags.
119     ciaddr         Client IP address; only filled in if client is in\
120                    BOUND, RENEW or REBINDING state and can respond\
121                    to ARP requests.
122     yiaddr         'your' (client) IP address.
123     siaddr         IP address of next server to use in bootstrap;\
124                    returned in DHCPOFFER, DHCPACK by server.
125     giaddr         Relay agent IP address, used in booting via a\
126                    relay agent.
127     chaddr         Client hardware address.
128     sname          Optional server host name, null terminated string.
129     boot_file      Boot file name, null terminated string; "generic"\
130                    name or null in DHCPDISCOVER, fully qualified\
131                    directory-path name in DHCPOFFER.
132     options        Optional parameters field\
133                    ('DHCP message type' option must be included in\
134                     every DHCP message).
135     ============== ====================
136     """
137     _DHCP_PACK_STR = '!BBBBIHH4s4s4s4s16s64s128s'
138     _MIN_LEN = struct.calcsize(_DHCP_PACK_STR)
139     _MAC_ADDRESS_LEN = 6
140     _HARDWARE_TYPE_ETHERNET = 1
141     _class_prefixes = ['options']
142     _TYPE = {
143         'ascii': [
144             'ciaddr', 'yiaddr', 'siaddr', 'giaddr', 'chaddr',
145             'sname', 'boot_file'
146         ]
147     }
148
149     def __init__(self, op, chaddr, options=None, htype=_HARDWARE_TYPE_ETHERNET,
150                  hlen=0, hops=0, xid=None, secs=0, flags=0,
151                  ciaddr='0.0.0.0', yiaddr='0.0.0.0', siaddr='0.0.0.0',
152                  giaddr='0.0.0.0', sname='', boot_file=''):
153         super(dhcp, self).__init__()
154         self.op = op
155         self.htype = htype
156         self.hlen = hlen
157         self.hops = hops
158         if xid is None:
159             self.xid = random.randint(0, 0xffffffff)
160         else:
161             self.xid = xid
162         self.secs = secs
163         self.flags = flags
164         self.ciaddr = ciaddr
165         self.yiaddr = yiaddr
166         self.siaddr = siaddr
167         self.giaddr = giaddr
168         self.chaddr = chaddr
169         self.sname = sname
170         self.boot_file = boot_file
171         self.options = options
172
173     @classmethod
174     def parser(cls, buf):
175         (op, htype, hlen, hops, xid, secs, flags,
176          ciaddr, yiaddr, siaddr, giaddr, chaddr, sname,
177          boot_file) = struct.unpack_from(cls._DHCP_PACK_STR, buf)
178
179         if hlen == cls._MAC_ADDRESS_LEN:
180             chaddr = addrconv.mac.bin_to_text(chaddr[:cls._MAC_ADDRESS_LEN])
181
182         length = cls._MIN_LEN
183         parse_opt = None
184         if len(buf) > length:
185             parse_opt = options.parser(buf[length:])
186             length += parse_opt.options_len
187         return (cls(op, chaddr, parse_opt,
188                     htype, hlen, hops, xid, secs, flags,
189                     addrconv.ipv4.bin_to_text(ciaddr),
190                     addrconv.ipv4.bin_to_text(yiaddr),
191                     addrconv.ipv4.bin_to_text(siaddr),
192                     addrconv.ipv4.bin_to_text(giaddr),
193                     sname.decode('ascii'), boot_file.decode('ascii')),
194                 None, buf[length:])
195
196     def serialize(self, _payload=None, _prev=None):
197         opt_buf = bytearray()
198         if self.options is not None:
199             opt_buf = self.options.serialize()
200         if netaddr.valid_mac(self.chaddr):
201             chaddr = addrconv.mac.text_to_bin(self.chaddr)
202         else:
203             chaddr = self.chaddr
204         self.hlen = len(chaddr)
205         return struct.pack(self._DHCP_PACK_STR, self.op, self.htype, self.hlen,
206                            self.hops, self.xid, self.secs, self.flags,
207                            addrconv.ipv4.text_to_bin(self.ciaddr),
208                            addrconv.ipv4.text_to_bin(self.yiaddr),
209                            addrconv.ipv4.text_to_bin(self.siaddr),
210                            addrconv.ipv4.text_to_bin(self.giaddr),
211                            chaddr,
212                            self.sname.encode('ascii'),
213                            self.boot_file.encode('ascii')) + opt_buf
214
215
216 class options(stringify.StringifyMixin):
217     """DHCP (RFC 2132) options encoder/decoder class.
218
219     This is used with ryu.lib.packet.dhcp.dhcp.
220
221     An instance has the following attributes at least.
222     Most of them are same to the on-wire counterparts but in host byte order.
223     __init__ takes the corresponding args in this order.
224
225     .. tabularcolumns:: |l|L|
226
227     ============== ====================
228     Attribute      Description
229     ============== ====================
230     option_list    'end option' and 'pad option' are added automatically\
231                    after the option class is stored in array.
232     options_len    Option's byte length.\
233                    ('magic cookie', 'end option' and 'pad option'\
234                     length including.)
235     magic_cookie   The first four octets contain the decimal values\
236                    99, 130, 83 and 99.
237     ============== ====================
238     """
239     _MAGIC_COOKIE_UNPACK_STR = '!4s'
240     # same magic cookie as is defined in RFC 1497
241     _MAGIC_COOKIE = '99.130.83.99'
242     _OPT_TAG_LEN_BYTE = 2
243     _class_prefixes = ['option']
244     _TYPE = {
245         'ascii': [
246             'magic_cookie'
247         ]
248     }
249
250     def __init__(self, option_list=None, options_len=0,
251                  magic_cookie=_MAGIC_COOKIE):
252         super(options, self).__init__()
253         self.option_list = option_list or []
254         self.options_len = options_len
255         self.magic_cookie = magic_cookie
256
257     @classmethod
258     def parser(cls, buf):
259         opt_parse_list = []
260         offset = struct.calcsize(cls._MAGIC_COOKIE_UNPACK_STR)
261         magic_cookie = struct.unpack_from(cls._MAGIC_COOKIE_UNPACK_STR, buf)[0]
262         while len(buf) > offset:
263             opt_buf = buf[offset:]
264             try:
265                 opt = option.parser(opt_buf)
266             except struct.error:
267                 opt_parse_list.append(opt_buf)
268                 break
269             if opt is None:
270                 break
271             opt_parse_list.append(opt)
272             offset += opt.length + cls._OPT_TAG_LEN_BYTE
273         return cls(opt_parse_list, len(buf),
274                    addrconv.ipv4.bin_to_text(magic_cookie))
275
276     def serialize(self):
277         seri_opt = addrconv.ipv4.text_to_bin(self.magic_cookie)
278         for opt in self.option_list:
279             if isinstance(opt, option):
280                 seri_opt += opt.serialize()
281             else:
282                 seri_opt += opt
283         if isinstance(self.option_list[-1], option):
284             seri_opt += b'\xff'
285         self.options_len = len(seri_opt)
286         return seri_opt
287
288
289 class option(stringify.StringifyMixin):
290     """DHCP (RFC 2132) options encoder/decoder class.
291
292     This is used with ryu.lib.packet.dhcp.dhcp.options.
293
294     An instance has the following attributes at least.
295     Most of them are same to the on-wire counterparts but in host byte order.
296     __init__ takes the corresponding args in this order.
297
298     ============== ====================
299     Attribute      Description
300     ============== ====================
301     tag            Option type.\
302                    (except for the 'magic cookie', 'pad option'\
303                     and 'end option'.)
304     value          Option's value.\
305                    (set the value that has been converted to hexadecimal.)
306     length         Option's value length.\
307                    (calculated automatically from the length of value.)
308     ============== ====================
309     """
310     _UNPACK_STR = '!B'
311     _MIN_LEN = struct.calcsize(_UNPACK_STR)
312
313     def __init__(self, tag, value, length=0):
314         super(option, self).__init__()
315         self.tag = tag
316         self.value = value
317         self.length = length
318
319     @classmethod
320     def parser(cls, buf):
321         tag = struct.unpack_from(cls._UNPACK_STR, buf)[0]
322         if tag == DHCP_END_OPT or tag == DHCP_PAD_OPT:
323             return None
324         buf = buf[cls._MIN_LEN:]
325         length = struct.unpack_from(cls._UNPACK_STR, buf)[0]
326         buf = buf[cls._MIN_LEN:]
327         value_unpack_str = '%ds' % length
328         value = struct.unpack_from(value_unpack_str, buf)[0]
329         return cls(tag, value, length)
330
331     def serialize(self):
332         self.length = len(self.value)
333         options_pack_str = '!BB%ds' % self.length
334         return struct.pack(options_pack_str, self.tag, self.length, self.value)