diff options
author | Isaku Yamahata <yamahata@valinux.co.jp> | 2013-04-13 23:47:39 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2013-04-16 02:48:01 +0900 |
commit | af97e2b26f0b05396ad9fac2b044dbe7c92d4af1 (patch) | |
tree | 2472c9314334ad7953a3f717d8516943dd4d60c0 | |
parent | 1a36bbd667ac113f42e7a26b3491d25f36f22edd (diff) |
lib/packet: VRRP packet parser/serializer
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
-rw-r--r-- | ryu/lib/packet/vrrp.py | 555 |
1 files changed, 555 insertions, 0 deletions
diff --git a/ryu/lib/packet/vrrp.py b/ryu/lib/packet/vrrp.py new file mode 100644 index 00000000..f98d6aeb --- /dev/null +++ b/ryu/lib/packet/vrrp.py @@ -0,0 +1,555 @@ +# 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 netaddr +import struct + +from ryu.lib.packet import ethernet +from ryu.lib.packet import ipv4 +from ryu.lib.packet import ipv6 +from ryu.lib.packet import packet +from ryu.lib.packet import packet_base +from ryu.lib.packet import packet_utils +from ryu.lib.packet import vlan +from ryu.ofproto import ether +from ryu.ofproto import inet + + +# IPv4 +# the LSB 8 bits is used for VRID +VRRP_IPV4_SRC_MAC_ADDRESS_STR = '00:00:5E:00:01:00' +VRRP_IPV4_SRC_MAC_ADDRESS = netaddr.EUI(VRRP_IPV4_SRC_MAC_ADDRESS_STR).packed +VRRP_IPV4_DST_MAC_ADDRESS_STR = '01:00:5E:00:00:12' +VRRP_IPV4_DST_MAC_ADDRESS = netaddr.EUI(VRRP_IPV4_DST_MAC_ADDRESS_STR).packed +VRRP_IPV4_DST_ADDRESS_STR = '224.0.0.18' +VRRP_IPV4_DST_ADDRESS = netaddr.IPAddress(VRRP_IPV4_DST_ADDRESS_STR).value +VRRP_IPV4_TTL = 255 + + +def vrrp_ipv4_src_mac_address(vrid): + return VRRP_IPV4_SRC_MAC_ADDRESS[:-1] + chr(vrid) + + +# IPv6 +# the LSB 8 bits is used for VRID +VRRP_IPV6_SRC_MAC_ADDRESS_STR = '00:00:5E:00:02:00' +VRRP_IPV6_SRC_MAC_ADDRESS = netaddr.EUI(VRRP_IPV6_SRC_MAC_ADDRESS_STR).packed +VRRP_IPV6_DST_MAC_ADDRESS_STR = '33:33:00:00:00:12' +VRRP_IPV6_DST_MAC_ADDRESS = netaddr.EUI(VRRP_IPV6_DST_MAC_ADDRESS_STR).packed +VRRP_IPV6_DST_ADDRESS_STR = 'FF02:0:0:0:0:0:0:12' +VRRP_IPV6_DST_ADDRESS = netaddr.IPAddress(VRRP_IPV6_DST_ADDRESS_STR).packed +VRRP_IPV6_HOP_LIMIT = 255 + + +def vrrp_ipv6_src_mac_address(vrid): + return VRRP_IPV6_SRC_MAC_ADDRESS[:-1] + chr(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): + if type(ip_address) == int: + return False + + assert type(ip_address) == str + assert len(ip_address) == 16 + return True + + +class vrrp(packet_base.PacketBase): + _VERSION_PACK_STR = '!B' + _IPV4_ADDRESS_PACK_STR_RAW = 'I' + _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_): + protocols = packet_.protocols + + try: + may_ip, may_vrrp = protocols[-2], protocols[-1] + if isinstance(may_vrrp, bytearray): + may_ip, may_vrrp = protocols[-3], protocols[-2] + except IndexError: + return None, None + + if (not isinstance(may_ip, ipv4.ipv4) and + not isinstance(may_ip, ipv6.ipv6)): + return None, None + if not isinstance(may_vrrp, vrrp): + return None, None + return may_ip, may_vrrp + + @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(ip_addresses) == self.count_ip + + self.auth_type = auth_type + self.auth_data = auth_data + + self._is_ipv6 = is_ipv6(self.ip_addresses[0]) + self.length = len(self) + 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): + if self.is_ipv6: + traffic_class = 0xc0 # set tos to internetwork control + flow_label = 0 + payload_length = ipv6.ipv6._MIN_LEN + self.length # 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 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): + _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): + 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 = struct.unpack_from(ip_addresses_pack_str, buf, offset) + + offset += struct.calcsize(ip_addresses_pack_str) + auth_data = struct.unpack_from(cls._AUTH_DATA_PACK_STR, buf, offset) + + return cls(version, type_, vrid, priority, count_ip, adver_int, + checksum, ip_addresses, auth_type, auth_data), None + + @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, + *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.vrid and + self.vrid <= 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): + _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, self.length, vrrp_buf) == 0 + + @staticmethod + def create(type_, vrid, priority, max_adver_int, ip_addresses): + 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 + elif address_len == cls._IPV6_ADDRESS_LEN: + pack_str = '!' + cls._IPV6_ADDRESS_PACK_STR_RAW * count_ip + else: + raise ValueError( + 'unknown address version address_len %d count_ip %d' % ( + address_len, count_ip)) + + ip_addresses = struct.unpack_from(pack_str, buf, offset) + return cls(version, type_, vrid, priority, + count_ip, max_adver_int, checksum, ip_addresses), None + + @staticmethod + def serialize_static(vrrp_, prev): + if isinstance(prev, ipv4.ipv4): + assert type(vrrp_.ip_addresses[0]) == int + ip_address_pack_raw = vrrpv3._IPV4_ADDRESS_PACK_STR_RAW + elif isinstance(prev, ipv6.ipv6): + assert type(vrrp_.ip_addresses[0]) == str + assert len(vrrp_.ip_addresses[0]) == vrrp._IPV6_ADDRESS_LEN + 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, + *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.vrid and + self.vrid <= 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) |