backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / lib / packet / icmpv6.py
1 # Copyright (C) 2012 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 import abc
17 import struct
18 import six
19 import sys
20 import array
21 import binascii
22
23 from . import packet_base
24 from . import packet_utils
25 from ryu.lib import addrconv
26 from ryu.lib import stringify
27
28 ICMPV6_DST_UNREACH = 1       # dest unreachable, codes:
29 ICMPV6_PACKET_TOO_BIG = 2       # packet too big
30 ICMPV6_TIME_EXCEEDED = 3       # time exceeded, code:
31 ICMPV6_PARAM_PROB = 4       # ip6 header bad
32
33 ICMPV6_ECHO_REQUEST = 128     # echo service
34 ICMPV6_ECHO_REPLY = 129     # echo reply
35 MLD_LISTENER_QUERY = 130     # multicast listener query
36 MLD_LISTENER_REPOR = 131     # multicast listener report
37 MLD_LISTENER_DONE = 132     # multicast listener done
38 MLDV2_LISTENER_REPORT = 143     # multicast listern report (v2)
39
40 # RFC2292 decls
41 ICMPV6_MEMBERSHIP_QUERY = 130     # group membership query
42 ICMPV6_MEMBERSHIP_REPORT = 131     # group membership report
43 ICMPV6_MEMBERSHIP_REDUCTION = 132     # group membership termination
44
45 ND_ROUTER_SOLICIT = 133     # router solicitation
46 ND_ROUTER_ADVERT = 134     # router advertisment
47 ND_NEIGHBOR_SOLICIT = 135     # neighbor solicitation
48 ND_NEIGHBOR_ADVERT = 136     # neighbor advertisment
49 ND_REDIREC = 137     # redirect
50
51 ICMPV6_ROUTER_RENUMBERING = 138     # router renumbering
52
53 ICMPV6_WRUREQUEST = 139     # who are you request
54 ICMPV6_WRUREPLY = 140     # who are you reply
55 ICMPV6_FQDN_QUERY = 139     # FQDN query
56 ICMPV6_FQDN_REPLY = 140     # FQDN reply
57 ICMPV6_NI_QUERY = 139     # node information request
58 ICMPV6_NI_REPLY = 140     # node information reply
59
60 ICMPV6_MAXTYPE = 201
61
62 # ND_OPTIONS from RFC 4861
63 ND_OPTION_SLA = 1  # Source Link-Layer Address
64 ND_OPTION_TLA = 2  # Target Link-Layer Address
65 ND_OPTION_PI = 3   # Prefix Information
66 ND_OPTION_RH = 4   # Redirected Header
67 ND_OPTION_MTU = 5  # MTU
68
69 MODE_IS_INCLUDE = 1
70 MODE_IS_EXCLUDE = 2
71 CHANGE_TO_INCLUDE_MODE = 3
72 CHANGE_TO_EXCLUDE_MODE = 4
73 ALLOW_NEW_SOURCES = 5
74 BLOCK_OLD_SOURCES = 6
75
76
77 class icmpv6(packet_base.PacketBase):
78     r"""ICMPv6 (RFC 2463) header encoder/decoder class.
79
80     An instance has the following attributes at least.
81     Most of them are same to the on-wire counterparts but in host byte order.
82     __init__ takes the corresponding args in this order.
83
84     .. tabularcolumns:: |l|p{35em}|
85
86     ============== ====================
87     Attribute      Description
88     ============== ====================
89     type\_         Type
90     code           Code
91     csum           CheckSum
92                    (0 means automatically-calculate when encoding)
93     data           Payload.
94
95                    ryu.lib.packet.icmpv6.echo object, \
96                    ryu.lib.packet.icmpv6.nd_neighbor object, \
97                    ryu.lib.packet.icmpv6.nd_router_solicit object, \
98                    ryu.lib.packet.icmpv6.nd_router_advert object, \
99                    ryu.lib.packet.icmpv6.mld object, \
100                    or a bytearray.
101     ============== ====================
102     """
103     _PACK_STR = '!BBH'
104     _MIN_LEN = struct.calcsize(_PACK_STR)
105     _ICMPV6_TYPES = {}
106
107     @staticmethod
108     def register_icmpv6_type(*args):
109         def _register_icmpv6_type(cls):
110             for type_ in args:
111                 icmpv6._ICMPV6_TYPES[type_] = cls
112             return cls
113         return _register_icmpv6_type
114
115     def __init__(self, type_=0, code=0, csum=0, data=b''):
116         super(icmpv6, self).__init__()
117         self.type_ = type_
118         self.code = code
119         self.csum = csum
120         self.data = data
121
122     @classmethod
123     def parser(cls, buf):
124         (type_, code, csum) = struct.unpack_from(cls._PACK_STR, buf)
125         msg = cls(type_, code, csum)
126         offset = cls._MIN_LEN
127         if len(buf) > offset:
128             cls_ = cls._ICMPV6_TYPES.get(type_, None)
129             if cls_:
130                 msg.data = cls_.parser(buf, offset)
131             else:
132                 msg.data = buf[offset:]
133
134         return msg, None, None
135
136     def serialize(self, payload, prev):
137         hdr = bytearray(struct.pack(icmpv6._PACK_STR, self.type_,
138                                     self.code, self.csum))
139
140         if self.data:
141             if self.type_ in icmpv6._ICMPV6_TYPES:
142                 assert isinstance(self.data, _ICMPv6Payload)
143                 hdr += self.data.serialize()
144             else:
145                 hdr += self.data
146         if self.csum == 0:
147             self.csum = packet_utils.checksum_ip(prev, len(hdr), hdr + payload)
148             struct.pack_into('!H', hdr, 2, self.csum)
149
150         return hdr
151
152     def __len__(self):
153         return self._MIN_LEN + len(self.data)
154
155
156 @six.add_metaclass(abc.ABCMeta)
157 class _ICMPv6Payload(stringify.StringifyMixin):
158     """
159     Base class for the payload of ICMPv6 packet.
160     """
161
162
163 @icmpv6.register_icmpv6_type(ND_NEIGHBOR_SOLICIT, ND_NEIGHBOR_ADVERT)
164 class nd_neighbor(_ICMPv6Payload):
165     """ICMPv6 sub encoder/decoder class for Neighbor Solicitation and
166     Neighbor Advertisement messages. (RFC 4861)
167
168     This is used with ryu.lib.packet.icmpv6.icmpv6.
169
170     An instance has the following attributes at least.
171     Most of them are same to the on-wire counterparts but in host byte order.
172     __init__ takes the corresponding args in this order.
173
174     .. tabularcolumns:: |l|p{35em}|
175
176     ============== ====================
177     Attribute      Description
178     ============== ====================
179     res            R,S,O Flags for Neighbor Advertisement. \
180                    The 3 MSBs of "Reserved" field for Neighbor Solicitation.
181     dst            Target Address
182     option         a derived object of ryu.lib.packet.icmpv6.nd_option \
183                    or a bytearray. None if no options.
184     ============== ====================
185     """
186
187     _PACK_STR = '!I16s'
188     _MIN_LEN = struct.calcsize(_PACK_STR)
189     _ND_OPTION_TYPES = {}
190     _TYPE = {
191         'ascii': [
192             'dst'
193         ]
194     }
195
196     @staticmethod
197     def register_nd_option_type(*args):
198         def _register_nd_option_type(cls):
199             nd_neighbor._ND_OPTION_TYPES[cls.option_type()] = cls
200             return cls
201         return _register_nd_option_type(args[0])
202
203     def __init__(self, res=0, dst='::', option=None):
204         self.res = res
205         self.dst = dst
206         self.option = option
207
208     @classmethod
209     def parser(cls, buf, offset):
210         (res, dst) = struct.unpack_from(cls._PACK_STR, buf, offset)
211         offset += cls._MIN_LEN
212         option = None
213         if len(buf) > offset:
214             (type_, length) = struct.unpack_from('!BB', buf, offset)
215             if length == 0:
216                 raise struct.error('Invalid length: {len}'.format(len=length))
217             cls_ = cls._ND_OPTION_TYPES.get(type_)
218             if cls_ is not None:
219                 option = cls_.parser(buf, offset)
220             else:
221                 option = buf[offset:]
222         msg = cls(res >> 29, addrconv.ipv6.bin_to_text(dst), option)
223         return msg
224
225     def serialize(self):
226         res = self.res << 29
227         hdr = bytearray(struct.pack(
228             nd_neighbor._PACK_STR, res,
229             addrconv.ipv6.text_to_bin(self.dst)))
230         if self.option is not None:
231             if isinstance(self.option, nd_option):
232                 hdr.extend(self.option.serialize())
233             else:
234                 hdr.extend(self.option)
235         return six.binary_type(hdr)
236
237     def __len__(self):
238         length = self._MIN_LEN
239         if self.option is not None:
240             length += len(self.option)
241         return length
242
243
244 @icmpv6.register_icmpv6_type(ND_ROUTER_SOLICIT)
245 class nd_router_solicit(_ICMPv6Payload):
246     """ICMPv6 sub encoder/decoder class for Router Solicitation messages.
247     (RFC 4861)
248
249     This is used with ryu.lib.packet.icmpv6.icmpv6.
250
251     An instance has the following attributes at least.
252     Most of them are same to the on-wire counterparts but in host byte order.
253     __init__ takes the corresponding args in this order.
254
255     .. tabularcolumns:: |l|p{35em}|
256
257     ============== ====================
258     Attribute      Description
259     ============== ====================
260     res            This field is unused.  It MUST be initialized to zero.
261     option         a derived object of ryu.lib.packet.icmpv6.nd_option \
262                    or a bytearray. None if no options.
263     ============== ====================
264     """
265
266     _PACK_STR = '!I'
267     _MIN_LEN = struct.calcsize(_PACK_STR)
268     _ND_OPTION_TYPES = {}
269
270     @staticmethod
271     def register_nd_option_type(*args):
272         def _register_nd_option_type(cls):
273             nd_router_solicit._ND_OPTION_TYPES[cls.option_type()] = cls
274             return cls
275         return _register_nd_option_type(args[0])
276
277     def __init__(self, res=0, option=None):
278         self.res = res
279         self.option = option
280
281     @classmethod
282     def parser(cls, buf, offset):
283         (res, ) = struct.unpack_from(cls._PACK_STR, buf, offset)
284         offset += cls._MIN_LEN
285         option = None
286         if len(buf) > offset:
287             (type_, length) = struct.unpack_from('!BB', buf, offset)
288             if length == 0:
289                 raise struct.error('Invalid length: {len}'.format(len=length))
290             cls_ = cls._ND_OPTION_TYPES.get(type_)
291             if cls_ is not None:
292                 option = cls_.parser(buf, offset)
293             else:
294                 option = buf[offset:]
295         msg = cls(res, option)
296         return msg
297
298     def serialize(self):
299         hdr = bytearray(struct.pack(
300             nd_router_solicit._PACK_STR, self.res))
301         if self.option is not None:
302             if isinstance(self.option, nd_option):
303                 hdr.extend(self.option.serialize())
304             else:
305                 hdr.extend(self.option)
306         return six.binary_type(hdr)
307
308     def __len__(self):
309         length = self._MIN_LEN
310         if self.option is not None:
311             length += len(self.option)
312         return length
313
314
315 @icmpv6.register_icmpv6_type(ND_ROUTER_ADVERT)
316 class nd_router_advert(_ICMPv6Payload):
317     """ICMPv6 sub encoder/decoder class for Router Advertisement messages.
318     (RFC 4861)
319
320     This is used with ryu.lib.packet.icmpv6.icmpv6.
321
322     An instance has the following attributes at least.
323     Most of them are same to the on-wire counterparts but in host byte order.
324     __init__ takes the corresponding args in this order.
325
326     .. tabularcolumns:: |l|p{35em}|
327
328     ============== ====================
329     Attribute      Description
330     ============== ====================
331     ch_l           Cur Hop Limit.
332     res            M,O Flags for Router Advertisement.
333     rou_l          Router Lifetime.
334     rea_t          Reachable Time.
335     ret_t          Retrans Timer.
336     options        List of a derived object of \
337                    ryu.lib.packet.icmpv6.nd_option or a bytearray. \
338                    None if no options.
339     ============== ====================
340     """
341
342     _PACK_STR = '!BBHII'
343     _MIN_LEN = struct.calcsize(_PACK_STR)
344     _ND_OPTION_TYPES = {}
345
346     @staticmethod
347     def register_nd_option_type(*args):
348         def _register_nd_option_type(cls):
349             nd_router_advert._ND_OPTION_TYPES[cls.option_type()] = cls
350             return cls
351         return _register_nd_option_type(args[0])
352
353     def __init__(self, ch_l=0, res=0, rou_l=0, rea_t=0, ret_t=0, options=None):
354         self.ch_l = ch_l
355         self.res = res
356         self.rou_l = rou_l
357         self.rea_t = rea_t
358         self.ret_t = ret_t
359         options = options or []
360         assert isinstance(options, list)
361         self.options = options
362
363     @classmethod
364     def parser(cls, buf, offset):
365         (ch_l, res, rou_l, rea_t, ret_t
366          ) = struct.unpack_from(cls._PACK_STR, buf, offset)
367         offset += cls._MIN_LEN
368         options = []
369         while len(buf) > offset:
370             (type_, length) = struct.unpack_from('!BB', buf, offset)
371             if length == 0:
372                 raise struct.error('Invalid length: {len}'.format(len=length))
373             cls_ = cls._ND_OPTION_TYPES.get(type_)
374             if cls_ is not None:
375                 option = cls_.parser(buf, offset)
376             else:
377                 option = buf[offset:offset + (length * 8)]
378             options.append(option)
379             offset += len(option)
380         msg = cls(ch_l, res >> 6, rou_l, rea_t, ret_t, options)
381         return msg
382
383     def serialize(self):
384         res = self.res << 6
385         hdr = bytearray(struct.pack(
386             nd_router_advert._PACK_STR, self.ch_l, res, self.rou_l,
387             self.rea_t, self.ret_t))
388         for option in self.options:
389             if isinstance(option, nd_option):
390                 hdr.extend(option.serialize())
391             else:
392                 hdr.extend(option)
393         return six.binary_type(hdr)
394
395     def __len__(self):
396         length = self._MIN_LEN
397         for option in self.options:
398             length += len(option)
399         return length
400
401
402 @six.add_metaclass(abc.ABCMeta)
403 class nd_option(stringify.StringifyMixin):
404     @classmethod
405     @abc.abstractmethod
406     def option_type(cls):
407         pass
408
409     @abc.abstractmethod
410     def __init__(self, _type, length):
411         self._type = _type
412         self.length = length
413
414     @classmethod
415     @abc.abstractmethod
416     def parser(cls, buf):
417         pass
418
419     @abc.abstractmethod
420     def serialize(self):
421         pass
422
423     def __len__(self):
424         return self._MIN_LEN
425
426
427 class nd_option_la(nd_option):
428
429     _PACK_STR = '!BB6s'
430     _MIN_LEN = struct.calcsize(_PACK_STR)
431     _TYPE = {
432         'ascii': [
433             'hw_src'
434         ]
435     }
436
437     @abc.abstractmethod
438     def __init__(self, length, hw_src, data):
439         super(nd_option_la, self).__init__(self.option_type(), length)
440         self.hw_src = hw_src
441         self.data = data
442
443     @classmethod
444     def parser(cls, buf, offset):
445         (_, length, hw_src) = struct.unpack_from(cls._PACK_STR, buf, offset)
446         msg = cls(length, addrconv.mac.bin_to_text(hw_src))
447         offset += cls._MIN_LEN
448         if len(buf) > offset:
449             msg.data = buf[offset:]
450
451         return msg
452
453     def serialize(self):
454         buf = bytearray(struct.pack(
455             self._PACK_STR, self.option_type(), self.length,
456             addrconv.mac.text_to_bin(self.hw_src)))
457         if self.data is not None:
458             buf.extend(self.data)
459         mod = len(buf) % 8
460         if mod:
461             buf.extend(bytearray(8 - mod))
462         if 0 == self.length:
463             self.length = len(buf) // 8
464             struct.pack_into('!B', buf, 1, self.length)
465         return six.binary_type(buf)
466
467     def __len__(self):
468         length = self._MIN_LEN
469         if self.data is not None:
470             length += len(self.data)
471         return length
472
473
474 @nd_neighbor.register_nd_option_type
475 @nd_router_solicit.register_nd_option_type
476 @nd_router_advert.register_nd_option_type
477 class nd_option_sla(nd_option_la):
478     """ICMPv6 sub encoder/decoder class for Neighbor discovery
479     Source Link-Layer Address Option. (RFC 4861)
480
481     This is used with ryu.lib.packet.icmpv6.nd_neighbor,
482     ryu.lib.packet.icmpv6.nd_router_solicit or
483     ryu.lib.packet.icmpv6.nd_router_advert.
484
485     An instance has the following attributes at least.
486     Most of them are same to the on-wire counterparts but in host byte order.
487     __init__ takes the corresponding args in this order.
488
489     .. tabularcolumns:: |l|p{35em}|
490
491     ============== ====================
492     Attribute      Description
493     ============== ====================
494     length         length of the option. \
495                    (0 means automatically-calculate when encoding)
496     hw_src         Link-Layer Address. \
497                    NOTE: If the address is longer than 6 octets this contains \
498                    the first 6 octets in the address. \
499                    This implementation assumes the address has at least \
500                    6 octets.
501     data           A bytearray which contains the rest of Link-Layer Address \
502                    and padding.  When encoding a packet, it's user's \
503                    responsibility to provide necessary padding for 8-octets \
504                    alignment required by the protocol.
505     ============== ====================
506     """
507
508     @classmethod
509     def option_type(cls):
510         return ND_OPTION_SLA
511
512     def __init__(self, length=0, hw_src='00:00:00:00:00:00', data=None):
513         super(nd_option_sla, self).__init__(length, hw_src, data)
514
515
516 @nd_neighbor.register_nd_option_type
517 class nd_option_tla(nd_option_la):
518     """ICMPv6 sub encoder/decoder class for Neighbor discovery
519     Target Link-Layer Address Option. (RFC 4861)
520
521     This is used with ryu.lib.packet.icmpv6.nd_neighbor.
522
523     An instance has the following attributes at least.
524     Most of them are same to the on-wire counterparts but in host byte order.
525     __init__ takes the corresponding args in this order.
526
527     .. tabularcolumns:: |l|p{35em}|
528
529     ============== ====================
530     Attribute      Description
531     ============== ====================
532     length         length of the option. \
533                    (0 means automatically-calculate when encoding)
534     hw_src         Link-Layer Address. \
535                    NOTE: If the address is longer than 6 octets this contains \
536                    the first 6 octets in the address. \
537                    This implementation assumes the address has at least \
538                    6 octets.
539     data           A bytearray which contains the rest of Link-Layer Address \
540                    and padding.  When encoding a packet, it's user's \
541                    responsibility to provide necessary padding for 8-octets \
542                    alignment required by the protocol.
543     ============== ====================
544     """
545
546     @classmethod
547     def option_type(cls):
548         return ND_OPTION_TLA
549
550     def __init__(self, length=0, hw_src='00:00:00:00:00:00', data=None):
551         super(nd_option_tla, self).__init__(length, hw_src, data)
552
553
554 @nd_router_advert.register_nd_option_type
555 class nd_option_pi(nd_option):
556     r"""ICMPv6 sub encoder/decoder class for Neighbor discovery
557     Prefix Information Option. (RFC 4861)
558
559     This is used with ryu.lib.packet.icmpv6.nd_router_advert.
560
561     An instance has the following attributes at least.
562     Most of them are same to the on-wire counterparts but in host byte order.
563     __init__ takes the corresponding args in this order.
564
565     .. tabularcolumns:: |l|p{35em}|
566
567     ============== ====================
568     Attribute      Description
569     ============== ====================
570     length         length of the option. \
571                    (0 means automatically-calculate when encoding)
572     pl             Prefix Length.
573     res1           L,A,R\* Flags for Prefix Information.
574     val_l          Valid Lifetime.
575     pre_l          Preferred Lifetime.
576     res2           This field is unused. It MUST be initialized to zero.
577     prefix         An IP address or a prefix of an IP address.
578     ============== ====================
579
580     \*R flag is defined in (RFC 3775)
581     """
582
583     _PACK_STR = '!BBBBIII16s'
584     _MIN_LEN = struct.calcsize(_PACK_STR)
585     _TYPE = {
586         'ascii': [
587             'prefix'
588         ]
589     }
590
591     @classmethod
592     def option_type(cls):
593         return ND_OPTION_PI
594
595     def __init__(self, length=0, pl=0, res1=0, val_l=0, pre_l=0, res2=0,
596                  prefix='::'):
597         super(nd_option_pi, self).__init__(self.option_type(), length)
598         self.pl = pl
599         self.res1 = res1
600         self.val_l = val_l
601         self.pre_l = pre_l
602         self.res2 = res2
603         self.prefix = prefix
604
605     @classmethod
606     def parser(cls, buf, offset):
607         (_, length, pl, res1, val_l, pre_l, res2, prefix
608          ) = struct.unpack_from(cls._PACK_STR, buf, offset)
609         msg = cls(length, pl, res1 >> 5, val_l, pre_l, res2,
610                   addrconv.ipv6.bin_to_text(prefix))
611
612         return msg
613
614     def serialize(self):
615         res1 = self.res1 << 5
616         hdr = bytearray(struct.pack(
617             self._PACK_STR, self.option_type(), self.length, self.pl,
618             res1, self.val_l, self.pre_l, self.res2,
619             addrconv.ipv6.text_to_bin(self.prefix)))
620         if 0 == self.length:
621             self.length = len(hdr) // 8
622             struct.pack_into('!B', hdr, 1, self.length)
623         return six.binary_type(hdr)
624
625
626 @icmpv6.register_icmpv6_type(ICMPV6_ECHO_REPLY, ICMPV6_ECHO_REQUEST)
627 class echo(_ICMPv6Payload):
628     """ICMPv6 sub encoder/decoder class for Echo Request and Echo Reply
629     messages.
630
631     This is used with ryu.lib.packet.icmpv6.icmpv6 for
632     ICMPv6 Echo Request and Echo Reply messages.
633
634     An instance has the following attributes at least.
635     Most of them are same to the on-wire counterparts but in host byte order.
636     __init__ takes the corresponding args in this order.
637
638     ============== ====================
639     Attribute      Description
640     ============== ====================
641     id             Identifier
642     seq            Sequence Number
643     data           Data
644     ============== ====================
645     """
646
647     _PACK_STR = '!HH'
648     _MIN_LEN = struct.calcsize(_PACK_STR)
649
650     def __init__(self, id_=0, seq=0, data=None):
651         self.id = id_
652         self.seq = seq
653         self.data = data
654
655     @classmethod
656     def parser(cls, buf, offset):
657         (id_, seq) = struct.unpack_from(cls._PACK_STR, buf, offset)
658         msg = cls(id_, seq)
659         offset += cls._MIN_LEN
660
661         if len(buf) > offset:
662             msg.data = buf[offset:]
663
664         return msg
665
666     def serialize(self):
667         hdr = bytearray(struct.pack(echo._PACK_STR, self.id,
668                                     self.seq))
669         if self.data is not None:
670             hdr += bytearray(self.data)
671
672         return hdr
673
674     def __len__(self):
675         length = self._MIN_LEN
676         if self.data is not None:
677             length += len(self.data)
678         return length
679
680
681 @icmpv6.register_icmpv6_type(
682     MLD_LISTENER_QUERY, MLD_LISTENER_REPOR, MLD_LISTENER_DONE)
683 class mld(_ICMPv6Payload):
684     """ICMPv6 sub encoder/decoder class for MLD Lister Query,
685     MLD Listener Report, and MLD Listener Done messages. (RFC 2710)
686
687     http://www.ietf.org/rfc/rfc2710.txt
688
689     This is used with ryu.lib.packet.icmpv6.icmpv6.
690
691     An instance has the following attributes at least.
692     Most of them are same to the on-wire counterparts but in host byte order.
693     __init__ takes the corresponding args in this order.
694
695     ============== =========================================
696     Attribute      Description
697     ============== =========================================
698     maxresp        max response time in millisecond. it is
699                    meaningful only in Query Message.
700     address        a group address value.
701     ============== =========================================
702     """
703
704     _PACK_STR = '!H2x16s'
705     _MIN_LEN = struct.calcsize(_PACK_STR)
706     _TYPE = {
707         'ascii': [
708             'address'
709         ]
710     }
711
712     def __init__(self, maxresp=0, address='::'):
713         self.maxresp = maxresp
714         self.address = address
715
716     @classmethod
717     def parser(cls, buf, offset):
718         if cls._MIN_LEN < len(buf[offset:]):
719             msg = mldv2_query.parser(buf[offset:])
720         else:
721             (maxresp, address) = struct.unpack_from(
722                 cls._PACK_STR, buf, offset)
723             msg = cls(maxresp, addrconv.ipv6.bin_to_text(address))
724
725         return msg
726
727     def serialize(self):
728         buf = struct.pack(mld._PACK_STR, self.maxresp,
729                           addrconv.ipv6.text_to_bin(self.address))
730         return buf
731
732     def __len__(self):
733         return self._MIN_LEN
734
735
736 class mldv2_query(mld):
737     """
738     ICMPv6 sub encoder/decoder class for MLD v2 Lister Query messages.
739     (RFC 3810)
740
741     http://www.ietf.org/rfc/rfc3810.txt
742
743     This is used with ryu.lib.packet.icmpv6.icmpv6.
744
745     An instance has the following attributes at least.
746     Most of them are same to the on-wire counterparts but in host byte order.
747     __init__ takes the corresponding args in this order.
748
749     ============== =========================================
750     Attribute      Description
751     ============== =========================================
752     maxresp        max response time in millisecond. it is
753                    meaningful only in Query Message.
754     address        a group address value.
755     s_flg          when set to 1, routers suppress the timer
756                    process.
757     qrv            robustness variable for a querier.
758     qqic           an interval time for a querier in unit of
759                    seconds.
760     num            a number of the multicast servers.
761     srcs           a list of IPv6 addresses of the multicast
762                    servers.
763     ============== =========================================
764     """
765
766     _PACK_STR = '!H2x16sBBH'
767     _MIN_LEN = struct.calcsize(_PACK_STR)
768     _TYPE = {
769         'ascii': [
770             'address'
771         ],
772         'asciilist': [
773             'srcs'
774         ]
775     }
776
777     def __init__(self, maxresp=0, address='::', s_flg=0, qrv=2,
778                  qqic=0, num=0, srcs=None):
779         super(mldv2_query, self).__init__(maxresp, address)
780         self.s_flg = s_flg
781         self.qrv = qrv
782         self.qqic = qqic
783         self.num = num
784         srcs = srcs or []
785         assert isinstance(srcs, list)
786         for src in srcs:
787             assert isinstance(src, str)
788         self.srcs = srcs
789
790     @classmethod
791     def parser(cls, buf):
792         (maxresp, address, s_qrv, qqic, num
793          ) = struct.unpack_from(cls._PACK_STR, buf)
794         s_flg = (s_qrv >> 3) & 0b1
795         qrv = s_qrv & 0b111
796         offset = cls._MIN_LEN
797         srcs = []
798         while 0 < len(buf[offset:]) and num > len(srcs):
799             assert 16 <= len(buf[offset:])
800             (src, ) = struct.unpack_from('16s', buf, offset)
801             srcs.append(addrconv.ipv6.bin_to_text(src))
802             offset += 16
803         assert num == len(srcs)
804         return cls(maxresp, addrconv.ipv6.bin_to_text(address), s_flg,
805                    qrv, qqic, num, srcs)
806
807     def serialize(self):
808         s_qrv = self.s_flg << 3 | self.qrv
809         buf = bytearray(struct.pack(self._PACK_STR, self.maxresp,
810                                     addrconv.ipv6.text_to_bin(self.address), s_qrv,
811                                     self.qqic, self.num))
812         for src in self.srcs:
813             buf.extend(struct.pack('16s', addrconv.ipv6.text_to_bin(src)))
814         if 0 == self.num:
815             self.num = len(self.srcs)
816             struct.pack_into('!H', buf, 22, self.num)
817         return six.binary_type(buf)
818
819     def __len__(self):
820         return self._MIN_LEN + len(self.srcs) * 16
821
822
823 @icmpv6.register_icmpv6_type(MLDV2_LISTENER_REPORT)
824 class mldv2_report(mld):
825     """
826     ICMPv6 sub encoder/decoder class for MLD v2 Lister Report messages.
827     (RFC 3810)
828
829     http://www.ietf.org/rfc/rfc3810.txt
830
831     This is used with ryu.lib.packet.icmpv6.icmpv6.
832
833     An instance has the following attributes at least.
834     Most of them are same to the on-wire counterparts but in host byte order.
835     __init__ takes the corresponding args in this order.
836
837     ============== =========================================
838     Attribute      Description
839     ============== =========================================
840     record_num     a number of the group records.
841     records        a list of ryu.lib.packet.icmpv6.mldv2_report_group.
842                    None if no records.
843     ============== =========================================
844     """
845
846     _PACK_STR = '!2xH'
847     _MIN_LEN = struct.calcsize(_PACK_STR)
848     _class_prefixes = ['mldv2_report_group']
849
850     def __init__(self, record_num=0, records=None):
851         self.record_num = record_num
852         records = records or []
853         assert isinstance(records, list)
854         for record in records:
855             assert isinstance(record, mldv2_report_group)
856         self.records = records
857
858     @classmethod
859     def parser(cls, buf, offset):
860         (record_num, ) = struct.unpack_from(cls._PACK_STR, buf, offset)
861         offset += cls._MIN_LEN
862         records = []
863         while 0 < len(buf[offset:]) and record_num > len(records):
864             record = mldv2_report_group.parser(buf[offset:])
865             records.append(record)
866             offset += len(record)
867         assert record_num == len(records)
868         return cls(record_num, records)
869
870     def serialize(self):
871         buf = bytearray(struct.pack(self._PACK_STR, self.record_num))
872         for record in self.records:
873             buf.extend(record.serialize())
874         if 0 == self.record_num:
875             self.record_num = len(self.records)
876             struct.pack_into('!H', buf, 2, self.record_num)
877         return six.binary_type(buf)
878
879     def __len__(self):
880         records_len = 0
881         for record in self.records:
882             records_len += len(record)
883         return self._MIN_LEN + records_len
884
885
886 class mldv2_report_group(stringify.StringifyMixin):
887     r"""
888     ICMPv6 sub encoder/decoder class for MLD v2 Lister Report Group
889     Record messages. (RFC 3810)
890
891     This is used with ryu.lib.packet.icmpv6.mldv2_report.
892
893     An instance has the following attributes at least.
894     Most of them are same to the on-wire counterparts but in host byte
895     order.
896     __init__ takes the corresponding args in this order.
897
898     =============== ====================================================
899     Attribute       Description
900     =============== ====================================================
901     type\_          a group record type for v3.
902     aux_len         the length of the auxiliary data in 32-bit words.
903     num             a number of the multicast servers.
904     address         a group address value.
905     srcs            a list of IPv6 addresses of the multicast servers.
906     aux             the auxiliary data.
907     =============== ====================================================
908     """
909     _PACK_STR = '!BBH16s'
910     _MIN_LEN = struct.calcsize(_PACK_STR)
911     _TYPE = {
912         'ascii': [
913             'address'
914         ],
915         'asciilist': [
916             'srcs'
917         ]
918     }
919
920     def __init__(self, type_=0, aux_len=0, num=0, address='::',
921                  srcs=None, aux=None):
922         self.type_ = type_
923         self.aux_len = aux_len
924         self.num = num
925         self.address = address
926         srcs = srcs or []
927         assert isinstance(srcs, list)
928         for src in srcs:
929             assert isinstance(src, str)
930         self.srcs = srcs
931         self.aux = aux
932
933     @classmethod
934     def parser(cls, buf):
935         (type_, aux_len, num, address
936          ) = struct.unpack_from(cls._PACK_STR, buf)
937         offset = cls._MIN_LEN
938         srcs = []
939         while 0 < len(buf[offset:]) and num > len(srcs):
940             assert 16 <= len(buf[offset:])
941             (src, ) = struct.unpack_from('16s', buf, offset)
942             srcs.append(addrconv.ipv6.bin_to_text(src))
943             offset += 16
944         assert num == len(srcs)
945         aux = None
946         if aux_len:
947             (aux, ) = struct.unpack_from('%ds' % (aux_len * 4), buf, offset)
948         msg = cls(type_, aux_len, num, addrconv.ipv6.bin_to_text(address),
949                   srcs, aux)
950         return msg
951
952     def serialize(self):
953         buf = bytearray(struct.pack(self._PACK_STR, self.type_,
954                                     self.aux_len, self.num,
955                                     addrconv.ipv6.text_to_bin(self.address)))
956         for src in self.srcs:
957             buf.extend(struct.pack('16s', addrconv.ipv6.text_to_bin(src)))
958         if 0 == self.num:
959             self.num = len(self.srcs)
960             struct.pack_into('!H', buf, 2, self.num)
961         if self.aux is not None:
962             mod = len(self.aux) % 4
963             if mod:
964                 self.aux += bytearray(4 - mod)
965                 self.aux = six.binary_type(self.aux)
966             buf.extend(self.aux)
967             if 0 == self.aux_len:
968                 self.aux_len = len(self.aux) // 4
969                 struct.pack_into('!B', buf, 1, self.aux_len)
970         return six.binary_type(buf)
971
972     def __len__(self):
973         return self._MIN_LEN + len(self.srcs) * 16 + self.aux_len * 4
974
975
976 icmpv6.set_classes(icmpv6._ICMPV6_TYPES)
977 nd_neighbor.set_classes(nd_neighbor._ND_OPTION_TYPES)
978 nd_router_solicit.set_classes(nd_router_solicit._ND_OPTION_TYPES)
979 nd_router_advert.set_classes(nd_router_advert._ND_OPTION_TYPES)