File: vrrp.py

package info (click to toggle)
python-os-ken 4.1.1-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 21,396 kB
  • sloc: python: 100,059; erlang: 14,517; ansic: 594; sh: 338; makefile: 136
file content (644 lines) | stat: -rw-r--r-- 24,948 bytes parent folder | download | duplicates (4)
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
# Copyright (C) 2013 Isaku Yamahata <yamahata at private email ne jp>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
VRRP packet parser/serializer

[RFC 3768] VRRP v2 packet format::

    0                   1                   2                   3
    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
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |Version| Type  | Virtual Rtr ID|   Priority    | Count IP Addrs|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |   Auth Type   |   Adver Int   |          Checksum             |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                         IP Address (1)                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                            .                                  |
   |                            .                                  |
   |                            .                                  |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                         IP Address (n)                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                     Authentication Data (1)                   |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                     Authentication Data (2)                   |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


[RFC 5798] VRRP v3 packet format::

     0                   1                   2                   3
     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
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                    IPv4 Fields or IPv6 Fields                 |
   ...                                                             ...
    |                                                               |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |Version| Type  | Virtual Rtr ID|   Priority    |Count IPvX Addr|
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |(rsvd) |     Max Adver Int     |          Checksum             |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                                                               |
    +                                                               +
    |                       IPvX Address(es)                        |
    +                                                               +
    +                                                               +
    +                                                               +
    +                                                               +
    |                                                               |
    +                                                               +
    |                                                               |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
"""

import struct

from os_ken.lib.packet import ethernet
from os_ken.lib.packet import ether_types as ether
from os_ken.lib.packet import in_proto as inet
from os_ken.lib.packet import ipv4
from os_ken.lib.packet import ipv6
from os_ken.lib.packet import packet
from os_ken.lib.packet import packet_base
from os_ken.lib.packet import packet_utils
from os_ken.lib.packet import vlan
from os_ken.lib import addrconv


# IPv4
# the LSB 8 bits is used for VRID
VRRP_IPV4_SRC_MAC_ADDRESS_FMT = '00:00:5E:00:01:%02x'
VRRP_IPV4_DST_MAC_ADDRESS = '01:00:5E:00:00:12'
VRRP_IPV4_DST_ADDRESS = '224.0.0.18'
VRRP_IPV4_TTL = 255


def vrrp_ipv4_src_mac_address(vrid):
    return VRRP_IPV4_SRC_MAC_ADDRESS_FMT % vrid


# IPv6
# the LSB 8 bits is used for VRID
VRRP_IPV6_SRC_MAC_ADDRESS_FMT = '00:00:5E:00:02:%02x'
VRRP_IPV6_DST_MAC_ADDRESS = '33:33:00:00:00:12'
VRRP_IPV6_DST_ADDRESS = 'ff02::12'
VRRP_IPV6_HOP_LIMIT = 255


def vrrp_ipv6_src_mac_address(vrid):
    return VRRP_IPV6_SRC_MAC_ADDRESS_FMT % vrid


VRRP_VERSION_SHIFT = 4
VRRP_TYPE_MASK = 0xf


def vrrp_from_version_type(version_type):
    return (version_type >> VRRP_VERSION_SHIFT, version_type & VRRP_TYPE_MASK)


def vrrp_to_version_type(version, type_):
    return (version << VRRP_VERSION_SHIFT) | type_


# VRRP version
VRRP_VERSION_V2 = 2
VRRP_VERSION_V3 = 3

# VRRP type
VRRP_TYPE_ADVERTISEMENT = 1

# VRRP VRID: 0 isn't used
VRRP_VRID_MIN = 1
VRRP_VRID_MAX = 255

# VRRP priority
VRRP_PRIORITY_MIN = 0
VRRP_PRIORITY_MAX = 255
VRRP_PRIORITY_RELEASE_RESPONSIBILITY = 0
VRRP_PRIORITY_BACKUP_MIN = 1
VRRP_PRIORITY_BACKUP_DEFAULT = 100
VRRP_PRIORITY_BACKUP_MAX = 254
VRRP_PRIORITY_ADDRESS_OWNER = 255

# VRRP auth type (VRRP v2 only)
VRRP_AUTH_NO_AUTH = 0
VRRP_AUTH_RESERVED1 = 1
VRRP_AUTH_RESERVED2 = 2
VRRP_AUTH_DATA1 = 0
VRRP_AUTH_DATA2 = 0
VRRP_AUTH_DATA = (VRRP_AUTH_DATA1, VRRP_AUTH_DATA2)

# VRRP Max advertisement interval
VRRP_MAX_ADVER_INT_DEFAULT_IN_SEC = 1   # 1 second

VRRP_V3_MAX_ADVER_INT_MASK = 0xfff      # in centiseconds
VRRP_V3_MAX_ADVER_INT_DEFAULT = 100     # = 1 second
VRRP_V3_MAX_ADVER_INT_MIN = 1           # don't allow 0
VRRP_V3_MAX_ADVER_INT_MAX = 0xfff

VRRP_V2_MAX_ADVER_INT_MASK = 0xff       # in seconds
VRRP_V2_MAX_ADVER_INT_DEFAULT = 1       # 1 second
VRRP_V2_MAX_ADVER_INT_MIN = 1           # don't allow 0
VRRP_V2_MAX_ADVER_INT_MAX = 0xff


def is_ipv6(ip_address):
    assert isinstance(ip_address, str)
    try:
        addrconv.ipv4.text_to_bin(ip_address)
    except:
        addrconv.ipv6.text_to_bin(ip_address)  # sanity
        return True
    return False


def ip_text_to_bin(ip_text):
    if is_ipv6(ip_text):
        return addrconv.ipv6.text_to_bin(ip_text)
    else:
        return addrconv.ipv4.text_to_bin(ip_text)


# This is used for master selection
def ip_address_lt(ip1, ip2):
    return ip_text_to_bin(ip1) < ip_text_to_bin(ip2)


class vrrp(packet_base.PacketBase):
    """The base class for VRRPv2 (RFC 3768) and VRRPv3 (RFC 5798)
    header encoder/decoder classes.

    Unlike other os_ken.lib.packet.packet_base.PacketBase derived classes,
    This class should not be directly instantiated by user.

    An instance has the following attributes at least.
    Most of them are same to the on-wire counterparts but in host byte order.

    ============== ====================
    Attribute      Description
    ============== ====================
    version        Version
    type           Type
    vrid           Virtual Rtr ID (VRID)
    priority       Priority
    count_ip       Count IPvX Addr. \
                   Calculated automatically when encoding.
    max_adver_int  Maximum Advertisement Interval (Max Adver Int)
    checksum       Checksum. \
                   Calculated automatically when encoding.
    ip_addresses   IPvX Address(es).  A python list of IP addresses.
    auth_type      Authentication Type (only for VRRPv2)
    auth_data      Authentication Data (only for VRRPv2)
    ============== ====================
    """

    _VERSION_PACK_STR = '!B'
    _IPV4_ADDRESS_PACK_STR_RAW = '4s'
    _IPV4_ADDRESS_PACK_STR = '!' + _IPV4_ADDRESS_PACK_STR_RAW
    _IPV4_ADDRESS_LEN = struct.calcsize(_IPV4_ADDRESS_PACK_STR)
    _IPV6_ADDRESS_LEN = 16
    _IPV6_ADDRESS_PACK_STR_RAW = '%ds' % _IPV6_ADDRESS_LEN
    _IPV6_ADDRESS_PACK_STR = '!' + _IPV6_ADDRESS_PACK_STR_RAW
    _IPV6_ADDRESS_LEN = struct.calcsize(_IPV6_ADDRESS_PACK_STR)

    _VRRP_VERSIONS = {}
    _SEC_IN_MAX_ADVER_INT_UNIT = {}

    @staticmethod
    def get_payload(packet_):
        may_ip = None
        may_vrrp = None

        idx = 0
        for protocol in packet_:
            if isinstance(protocol, ipv4.ipv4) or isinstance(protocol,
                                                             ipv6.ipv6):
                may_ip = protocol
                try:
                    if isinstance(packet_.protocols[idx + 1], vrrp):
                        may_vrrp = packet_.protocols[idx + 1]
                finally:
                    break
            idx += 1

        if may_ip and may_vrrp:
            return may_ip, may_vrrp
        else:
            return None, None

    @classmethod
    def register_vrrp_version(cls, version,
                              sec_in_max_adver_int_unit):
        def _register_vrrp_version(cls_):
            cls._VRRP_VERSIONS[version] = cls_
            cls._SEC_IN_MAX_ADVER_INT_UNIT[version] = sec_in_max_adver_int_unit
            return cls_
        return _register_vrrp_version

    @staticmethod
    def sec_to_max_adver_int(version, seconds):
        return int(seconds * vrrp._SEC_IN_MAX_ADVER_INT_UNIT[version])

    @staticmethod
    def max_adver_int_to_sec(version, max_adver_int):
        return float(max_adver_int) / vrrp._SEC_IN_MAX_ADVER_INT_UNIT[version]

    def __init__(self, version, type_, vrid, priority, count_ip,
                 max_adver_int, checksum, ip_addresses,

                 # auth_type/auth_data is for vrrp v2
                 auth_type=None, auth_data=None):
        super(vrrp, self).__init__()
        self.version = version
        self.type = type_
        self.vrid = vrid
        self.priority = priority
        self.count_ip = count_ip
        self.max_adver_int = max_adver_int

        self.checksum = checksum
        self.ip_addresses = ip_addresses
        assert len(list(ip_addresses)) == self.count_ip

        self.auth_type = auth_type
        self.auth_data = auth_data

        self._is_ipv6 = is_ipv6(list(self.ip_addresses)[0])
        self.identification = 0         # used for ipv4 identification

    def checksum_ok(self, ipvx, vrrp_buf):
        cls_ = self._VRRP_VERSIONS[self.version]
        return cls_.checksum_ok(self, ipvx, vrrp_buf)

    @property
    def max_adver_int_in_sec(self):
        # return seconds of float as time.sleep() accepts such type.
        return self.max_adver_int_to_sec(self.version, self.max_adver_int)

    @property
    def is_ipv6(self):
        return self._is_ipv6

    def __len__(self):
        cls_ = self._VRRP_VERSIONS[self.version]
        return cls_.__len__(self)

    @staticmethod
    def create_version(version, type_, vrid, priority, max_adver_int,
                       ip_addresses, auth_type=None, auth_data=None):
        cls_ = vrrp._VRRP_VERSIONS.get(version, None)
        if not cls_:
            raise ValueError('unknown VRRP version %d' % version)

        if priority is None:
            priority = VRRP_PRIORITY_BACKUP_DEFAULT
        count_ip = len(ip_addresses)
        if max_adver_int is None:
            max_adver_int = cls_.sec_to_max_adver_int(
                VRRP_MAX_ADVER_INT_DEFAULT_IN_SEC)
        return cls_(version, type_, vrid, priority, count_ip, max_adver_int,
                    None, ip_addresses,
                    auth_type=auth_type, auth_data=auth_data)

    def get_identification(self):
        self.identification += 1
        self.identification &= 0xffff
        if self.identification == 0:
            self.identification += 1
            self.identification &= 0xffff
        return self.identification

    def create_packet(self, primary_ip_address, vlan_id=None):
        """Prepare a VRRP packet.

        Returns a newly created os_ken.lib.packet.packet.Packet object
        with appropriate protocol header objects added by add_protocol().
        It's caller's responsibility to serialize().
        The serialized packet would looks like the ones described in
        the following sections.

        * RFC 3768 5.1. VRRP Packet Format
        * RFC 5798 5.1. VRRP Packet Format

        ================== ====================
        Argument           Description
        ================== ====================
        primary_ip_address Source IP address
        vlan_id            VLAN ID.  None for no VLAN.
        ================== ====================
        """
        if self.is_ipv6:
            traffic_class = 0xc0        # set tos to internetwork control
            flow_label = 0
            payload_length = ipv6.ipv6._MIN_LEN + len(self)     # XXX _MIN_LEN
            e = ethernet.ethernet(VRRP_IPV6_DST_MAC_ADDRESS,
                                  vrrp_ipv6_src_mac_address(self.vrid),
                                  ether.ETH_TYPE_IPV6)
            ip = ipv6.ipv6(6, traffic_class, flow_label, payload_length,
                           inet.IPPROTO_VRRP, VRRP_IPV6_HOP_LIMIT,
                           primary_ip_address, VRRP_IPV6_DST_ADDRESS)
        else:
            header_length = ipv4.ipv4._MIN_LEN // 4      # XXX _MIN_LEN
            total_length = 0
            tos = 0xc0  # set tos to internetwork control
            identification = self.get_identification()
            e = ethernet.ethernet(VRRP_IPV4_DST_MAC_ADDRESS,
                                  vrrp_ipv4_src_mac_address(self.vrid),
                                  ether.ETH_TYPE_IP)
            ip = ipv4.ipv4(4, header_length, tos, total_length, identification,
                           0, 0, VRRP_IPV4_TTL, inet.IPPROTO_VRRP, 0,
                           primary_ip_address, VRRP_IPV4_DST_ADDRESS)

        p = packet.Packet()
        p.add_protocol(e)
        if vlan_id is not None:
            vlan_ = vlan.vlan(0, 0, vlan_id, e.ethertype)
            e.ethertype = ether.ETH_TYPE_8021Q
            p.add_protocol(vlan_)
        p.add_protocol(ip)
        p.add_protocol(self)
        return p

    @classmethod
    def parser(cls, buf):
        (version_type,) = struct.unpack_from(cls._VERSION_PACK_STR, buf)
        version, _type = vrrp_from_version_type(version_type)
        cls_ = cls._VRRP_VERSIONS[version]
        return cls_.parser(buf)

    @staticmethod
    def serialize_static(vrrp_, prev):
        # self can be a instance of vrrpv2 or vrrpv3.
        assert isinstance(vrrp_, vrrp)
        cls = vrrp._VRRP_VERSIONS[vrrp_.version]
        return cls.serialize_static(vrrp_, prev)

    def serialize(self, payload, prev):
        return self.serialize_static(self, prev)

    @staticmethod
    def is_valid_ttl(ipvx):
        version = ipvx.version
        if version == 4:
            return ipvx.ttl == VRRP_IPV4_TTL
        if version == 6:
            return ipvx.hop_limit == VRRP_IPV6_HOP_LIMIT

        raise ValueError('invalid ip version %d' % version)

    def is_valid(self):
        cls = self._VRRP_VERSIONS.get(self.version, None)
        if cls is None:
            return False
        return cls.is_valid(self)


# max_adver_int is in seconds
@vrrp.register_vrrp_version(VRRP_VERSION_V2, 1)
class vrrpv2(vrrp):
    """VRRPv2 (RFC 3768) header encoder/decoder class.

    Unlike other os_ken.lib.packet.packet_base.PacketBase derived classes,
    *create* method should be used to instantiate an object of this class.
    """

    _PACK_STR = '!BBBBBBH'
    _MIN_LEN = struct.calcsize(_PACK_STR)
    _CHECKSUM_PACK_STR = '!H'
    _CHECKSUM_OFFSET = 6
    _AUTH_DATA_PACK_STR = '!II'
    _AUTH_DATA_LEN = struct.calcsize('!II')

    def __len__(self):
        return (self._MIN_LEN + self._IPV4_ADDRESS_LEN * self.count_ip +
                self._AUTH_DATA_LEN)

    def checksum_ok(self, ipvx, vrrp_buf):
        return packet_utils.checksum(vrrp_buf) == 0

    @staticmethod
    def create(type_, vrid, priority, max_adver_int, ip_addresses):
        """Unlike other os_ken.lib.packet.packet_base.PacketBase derived classes,
        this method should be used to instantiate an object of this class.

        This method's arguments are same as os_ken.lib.packet.vrrp.vrrp object's
        attributes of the same name.  (except that *type_* corresponds to
        *type* attribute.)
        """

        return vrrp.create_version(VRRP_VERSION_V2, type_, vrid, priority,
                                   max_adver_int,
                                   ip_addresses,
                                   auth_type=VRRP_AUTH_NO_AUTH,
                                   auth_data=VRRP_AUTH_DATA)

    @staticmethod
    def _ip_addresses_pack_str(count_ip):
        return '!' + vrrpv2._IPV4_ADDRESS_PACK_STR_RAW * count_ip

    @classmethod
    def parser(cls, buf):
        (version_type, vrid, priority, count_ip, auth_type, adver_int,
         checksum) = struct.unpack_from(cls._PACK_STR, buf)
        (version, type_) = vrrp_from_version_type(version_type)

        offset = cls._MIN_LEN
        ip_addresses_pack_str = cls._ip_addresses_pack_str(count_ip)
        ip_addresses_bin = struct.unpack_from(ip_addresses_pack_str, buf,
                                              offset)
        ip_addresses = [addrconv.ipv4.bin_to_text(x) for x in ip_addresses_bin]

        offset += struct.calcsize(ip_addresses_pack_str)
        auth_data = struct.unpack_from(cls._AUTH_DATA_PACK_STR, buf, offset)

        msg = cls(version, type_, vrid, priority, count_ip, adver_int,
                  checksum, ip_addresses, auth_type, auth_data)
        return msg, None, buf[len(msg):]

    @staticmethod
    def serialize_static(vrrp_, prev):
        assert not vrrp_.is_ipv6        # vrrpv2 defines only IPv4
        ip_addresses_pack_str = vrrpv2._ip_addresses_pack_str(vrrp_.count_ip)
        ip_addresses_len = struct.calcsize(ip_addresses_pack_str)
        vrrp_len = vrrpv2._MIN_LEN + ip_addresses_len + vrrpv2._AUTH_DATA_LEN

        checksum = False
        if vrrp_.checksum is None:
            checksum = True
            vrrp_.checksum = 0

        if vrrp_.auth_type is None:
            vrrp_.auth_type = VRRP_AUTH_NO_AUTH
        if vrrp_.auth_data is None:
            vrrp_.auth_data = VRRP_AUTH_DATA

        buf = bytearray(vrrp_len)
        offset = 0
        struct.pack_into(vrrpv2._PACK_STR, buf, offset,
                         vrrp_to_version_type(vrrp_.version, vrrp_.type),
                         vrrp_.vrid, vrrp_.priority,
                         vrrp_.count_ip, vrrp_.auth_type, vrrp_.max_adver_int,
                         vrrp_.checksum)
        offset += vrrpv2._MIN_LEN
        struct.pack_into(ip_addresses_pack_str, buf, offset,
                         *[addrconv.ipv4.text_to_bin(x) for x in vrrp_.ip_addresses])
        offset += ip_addresses_len
        struct.pack_into(vrrpv2._AUTH_DATA_PACK_STR, buf, offset,
                         *vrrp_.auth_data)
        if checksum:
            vrrp_.checksum = packet_utils.checksum(buf)
            struct.pack_into(vrrpv2._CHECKSUM_PACK_STR, buf,
                             vrrpv2._CHECKSUM_OFFSET, vrrp_.checksum)
        return buf

    def is_valid(self):
        return (self.version == VRRP_VERSION_V2 and
                self.type == VRRP_TYPE_ADVERTISEMENT and
                VRRP_VRID_MIN <= self.vrid and self.vrid <= VRRP_VRID_MAX and
                VRRP_PRIORITY_MIN <= self.priority and
                self.priority <= VRRP_PRIORITY_MAX and
                self.auth_type == VRRP_AUTH_NO_AUTH and
                VRRP_V2_MAX_ADVER_INT_MIN <= self.max_adver_int and
                self.max_adver_int <= VRRP_V2_MAX_ADVER_INT_MAX and
                self.count_ip == len(self.ip_addresses))


# max_adver_int is in centi seconds: 1 second = 100 centiseconds
@vrrp.register_vrrp_version(VRRP_VERSION_V3, 100)
class vrrpv3(vrrp):
    """VRRPv3 (RFC 5798) header encoder/decoder class.

    Unlike other os_ken.lib.packet.packet_base.PacketBase derived classes,
    *create* method should be used to instantiate an object of this class.
    """

    _PACK_STR = '!BBBBHH'
    _MIN_LEN = struct.calcsize(_PACK_STR)
    _CHECKSUM_PACK_STR = '!H'
    _CHECKSUM_OFFSET = 6

    def __len__(self):
        if self.is_ipv6:
            address_len = self._IPV6_ADDRESS_LEN
        else:
            address_len = self._IPV4_ADDRESS_LEN
        return self._MIN_LEN + address_len * self.count_ip

    def checksum_ok(self, ipvx, vrrp_buf):
        # There are two interpretation of IPv4 checksum
        # include IPv4 pseudo header or not.
        # http://www.ietf.org/mail-archive/web/vrrp/current/msg01473.html
        # if not self.is_ipv6:
        #     return packet_utils.checksum(vrrp_buf) == 0
        return packet_utils.checksum_ip(ipvx, len(self), vrrp_buf) == 0

    @staticmethod
    def create(type_, vrid, priority, max_adver_int, ip_addresses):
        """Unlike other os_ken.lib.packet.packet_base.PacketBase derived classes,
        this method should be used to instantiate an object of this class.

        This method's arguments are same as os_ken.lib.packet.vrrp.vrrp object's
        attributes of the same name.  (except that *type_* corresponds to
        *type* attribute.)
        """
        return vrrp.create_version(VRRP_VERSION_V3, type_, vrid, priority,
                                   max_adver_int, ip_addresses)

    @classmethod
    def parser(cls, buf):
        (version_type, vrid, priority, count_ip, max_adver_int,
         checksum) = struct.unpack_from(cls._PACK_STR, buf)
        (version, type_) = vrrp_from_version_type(version_type)

        # _rsvd = (max_adver_int & ~VRRP_V3_MAX_ADVER_INT_MASK) >> 12
        # asssert _rsvd == 0
        max_adver_int &= VRRP_V3_MAX_ADVER_INT_MASK

        offset = cls._MIN_LEN
        address_len = (len(buf) - offset) // count_ip
        # Address version (IPv4 or IPv6) is determined by network layer
        # header type.
        # Unfortunately it isn't available. Guess it by vrrp packet length.
        if address_len == cls._IPV4_ADDRESS_LEN:
            pack_str = '!' + cls._IPV4_ADDRESS_PACK_STR_RAW * count_ip
            conv = addrconv.ipv4.bin_to_text
        elif address_len == cls._IPV6_ADDRESS_LEN:
            pack_str = '!' + cls._IPV6_ADDRESS_PACK_STR_RAW * count_ip
            conv = addrconv.ipv6.bin_to_text
        else:
            raise ValueError(
                'unknown address version address_len %d count_ip %d' % (
                    address_len, count_ip))

        ip_addresses_bin = struct.unpack_from(pack_str, buf, offset)
        ip_addresses = [conv(x) for x in ip_addresses_bin]
        msg = cls(version, type_, vrid, priority,
                  count_ip, max_adver_int, checksum, ip_addresses)
        return msg, None, buf[len(msg):]

    @staticmethod
    def serialize_static(vrrp_, prev):
        if isinstance(prev, ipv4.ipv4):
            assert isinstance(vrrp_.ip_addresses[0], str)
            conv = addrconv.ipv4.text_to_bin
            ip_address_pack_raw = vrrpv3._IPV4_ADDRESS_PACK_STR_RAW
        elif isinstance(prev, ipv6.ipv6):
            assert isinstance(vrrp_.ip_addresses[0], str)
            conv = addrconv.ipv6.text_to_bin
            ip_address_pack_raw = vrrpv3._IPV6_ADDRESS_PACK_STR_RAW
        else:
            raise ValueError('Unkown network layer %s' % type(prev))

        ip_addresses_pack_str = '!' + ip_address_pack_raw * vrrp_.count_ip
        ip_addresses_len = struct.calcsize(ip_addresses_pack_str)
        vrrp_len = vrrpv3._MIN_LEN + ip_addresses_len

        checksum = False
        if vrrp_.checksum is None:
            checksum = True
            vrrp_.checksum = 0

        buf = bytearray(vrrp_len)
        assert vrrp_.max_adver_int <= VRRP_V3_MAX_ADVER_INT_MASK
        struct.pack_into(vrrpv3._PACK_STR, buf, 0,
                         vrrp_to_version_type(vrrp_.version, vrrp_.type),
                         vrrp_.vrid, vrrp_.priority,
                         vrrp_.count_ip, vrrp_.max_adver_int, vrrp_.checksum)
        struct.pack_into(ip_addresses_pack_str, buf, vrrpv3._MIN_LEN,
                         *[conv(x) for x in vrrp_.ip_addresses])

        if checksum:
            vrrp_.checksum = packet_utils.checksum_ip(prev, len(buf), buf)
            struct.pack_into(vrrpv3._CHECKSUM_PACK_STR, buf,
                             vrrpv3._CHECKSUM_OFFSET, vrrp_.checksum)
        return buf

    def is_valid(self):
        return (self.version == VRRP_VERSION_V3 and
                self.type == VRRP_TYPE_ADVERTISEMENT and
                VRRP_VRID_MIN <= self.vrid and self.vrid <= VRRP_VRID_MAX and
                VRRP_PRIORITY_MIN <= self.priority and
                self.priority <= VRRP_PRIORITY_MAX and
                VRRP_V3_MAX_ADVER_INT_MIN <= self.max_adver_int and
                self.max_adver_int <= VRRP_V3_MAX_ADVER_INT_MAX and
                self.count_ip == len(self.ip_addresses))


ipv4.ipv4.register_packet_type(vrrp, inet.IPPROTO_VRRP)
ipv6.ipv6.register_packet_type(vrrp, inet.IPPROTO_VRRP)