diff options
author | Olivier DESNOE <olivier.desnoe@gmail.com> | 2016-12-27 21:45:17 +0100 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2017-01-14 22:48:23 +0900 |
commit | 6d4b094dc35bb4f3092fc4e141b0b685d6353c4d (patch) | |
tree | 47b7e8c3388b53e97aaa0a11f661426d8808d8b5 | |
parent | 2197fd55d1caba0871e53e61e841776f78a006ed (diff) |
packet lib: adding DHCPv6 support
Signed-off-by: Olivier DESNOE <olivier.desnoe@gmail.com>
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
-rw-r--r-- | doc/source/library_packet_ref.rst | 7 | ||||
-rw-r--r-- | ryu/lib/packet/dhcp6.py | 291 | ||||
-rw-r--r-- | ryu/lib/packet/udp.py | 10 |
3 files changed, 304 insertions, 4 deletions
diff --git a/doc/source/library_packet_ref.rst b/doc/source/library_packet_ref.rst index 7246c00c..23c738d5 100644 --- a/doc/source/library_packet_ref.rst +++ b/doc/source/library_packet_ref.rst @@ -74,6 +74,13 @@ Protocol Header classes .. automodule:: ryu.lib.packet.dhcp :members: +.. autoclass:: ryu.lib.packet.dhcp6.dhcp6 + :members: +.. autoclass:: ryu.lib.packet.dhcp6.options + :members: +.. autoclass:: ryu.lib.packet.dhcp6.option + :members: + .. autoclass:: ryu.lib.packet.vrrp.vrrp :members: .. autoclass:: ryu.lib.packet.vrrp.vrrpv2 diff --git a/ryu/lib/packet/dhcp6.py b/ryu/lib/packet/dhcp6.py new file mode 100644 index 00000000..1e0505fd --- /dev/null +++ b/ryu/lib/packet/dhcp6.py @@ -0,0 +1,291 @@ +# Copyright (C) 2016 Bouygues Telecom. +# +# 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. + +""" +DHCPv6 packet parser/serializer + +RFC 3315 +DHCP packet format + +The following diagram illustrates the format of DHCP messages sent +between clients and servers: + +\ 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 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| msg_type | transaction_id | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | +. options . +. (variable) . +| | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +There are two relay agent messages, which share the following 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 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| msg_type | hop_count | | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | +| | +| link_address | +| | +| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-| +| | | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | +| | +| peer_address | +| | +| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-| +| | | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | +. . +. options (variable number and length) .... . +| | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +""" +import random +import struct + +from . import packet_base +from ryu.lib import addrconv +from ryu.lib import stringify + +# DHCPv6 message types +DHCPV6_SOLICIT = 1 +DHCPV6_ADVERTISE = 2 +DHCPV6_REQUEST = 3 +DHCPV6_CONFIRM = 4 +DHCPV6_RENEW = 5 +DHCPV6_REBIND = 6 +DHCPV6_REPLY = 7 +DHCPV6_RELEASE = 8 +DHCPV6_DECLINE = 9 +DHCPV6_RECONFIGURE = 10 +DHCPV6_INFORMATION_REQUEST = 11 +DHCPV6_RELAY_FORW = 12 +DHCPV6_RELAY_REPL = 13 + +# DHCPv6 option-codes +DHCPV6_OPTION_CLIENTID = 1 +DHCPV6_OPTION_SERVERID = 2 +DHCPV6_OPTION_IA_NA = 3 +DHCPV6_OPTION_IA_TA = 4 +DHCPV6_OPTION_IAADDR = 5 +DHCPV6_OPTION_ORO = 6 +DHCPV6_OPTION_PREFERENCE = 7 +DHCPV6_OPTION_ELAPSED_TIME = 8 +DHCPV6_OPTION_RELAY_MSG = 9 +DHCPV6_OPTION_AUTH = 11 +DHCPV6_OPTION_UNICAST = 12 +DHCPV6_OPTION_STATUS_CODE = 13 +DHCPV6_OPTION_RAPID_COMMIT = 14 +DHCPV6_OPTION_USER_CLASS = 15 +DHCPV6_OPTION_VENDOR_CLASS = 16 +DHCPV6_OPTION_VENDOR_OPTS = 17 +DHCPV6_OPTION_INTERFACE_ID = 18 +DHCPV6_OPTION_RECONF_MSG = 19 +DHCPV6_OPTION_RECONF_ACCEPT = 20 + + +class dhcp6(packet_base.PacketBase): + """DHCPv6 (RFC 3315) header encoder/decoder class. + + The serialized packet would looks like the ones described + in the following sections. + + * RFC 3315 DHCP packet format + + An instance has the following attributes at least. + Most of them are same to the on-wire counterparts but in host byte order. + __init__ takes the corresponding args in this order. + + + ============== ==================== + Attribute Description + ============== ==================== + msg_type Identifies the DHCP message type + transaction_id For unrelayed messages only: the transaction ID for\ + this message exchange. + hop_count For relayed messages only: number of relay agents that\ + have relayed this message. + link_address For relayed messages only: a global or site-local address\ + that will be used by the server to identify the link on\ + which the client is located. + peer_address For relayed messages only: the address of the client or\ + relay agent from which the message to be relayed was\ + received. + options Options carried in this message + ============== ==================== + """ + _MIN_LEN = 8 + _DHCPV6_UNPACK_STR = '!I' + _DHCPV6_RELAY_UNPACK_STR = '!H16s16s' + _DHCPV6_UNPACK_STR_LEN = struct.calcsize(_DHCPV6_UNPACK_STR) + _DHCPV6_RELAY_UNPACK_STR_LEN = struct.calcsize(_DHCPV6_RELAY_UNPACK_STR) + _DHCPV6_PACK_STR = '!I' + _DHCPV6_RELAY_PACK_STR = '!H16s16s' + + def __init__(self, msg_type, options, transaction_id=None, hop_count=0, + link_address='::', peer_address='::'): + super(dhcp6, self).__init__() + self.msg_type = msg_type + self.options = options + if transaction_id is None: + self.transaction_id = random.randint(0, 0xffffff) + else: + self.transaction_id = transaction_id + self.hop_count = hop_count + self.link_address = link_address + self.peer_address = peer_address + + @classmethod + def parser(cls, buf): + (msg_type, ) = struct.unpack_from('!B', buf) + + buf = b'\x00' + buf[1:] # unpack xid as a 4-byte integer + if msg_type == DHCPV6_RELAY_FORW or msg_type == DHCPV6_RELAY_REPL: + (hop_count, link_address, peer_address) \ + = struct.unpack_from(cls._DHCPV6_RELAY_UNPACK_STR, buf) + length = struct.calcsize(cls._DHCPV6_RELAY_UNPACK_STR) + else: + (transaction_id, ) \ + = struct.unpack_from(cls._DHCPV6_UNPACK_STR, buf) + length = struct.calcsize(cls._DHCPV6_UNPACK_STR) + + if len(buf) > length: + parse_opt = options.parser(buf[length:]) + length += parse_opt.options_len + if msg_type == DHCPV6_RELAY_FORW or msg_type == DHCPV6_RELAY_REPL: + return (cls(msg_type, parse_opt, 0, hop_count, + addrconv.ipv6.bin_to_text(link_address), + addrconv.ipv6.bin_to_text(peer_address)), + None, buf[length:]) + else: + return (cls(msg_type, parse_opt, transaction_id), + None, buf[length:]) + else: + return None, None, buf + + def serialize(self, payload=None, prev=None): + seri_opt = self.options.serialize() + if (self.msg_type == DHCPV6_RELAY_FORW or + self.msg_type == DHCPV6_RELAY_REPL): + pack_str = '%s%ds' % (self._DHCPV6_RELAY_PACK_STR, + self.options.options_len) + buf = struct.pack(pack_str, self.hop_count, + addrconv.ipv6.text_to_bin(self.link_address), + addrconv.ipv6.text_to_bin(self.peer_address), + seri_opt) + else: + pack_str = '%s%ds' % (self._DHCPV6_PACK_STR, + self.options.options_len) + buf = struct.pack(pack_str, self.transaction_id, seri_opt) + return struct.pack('!B', self.msg_type) + buf[1:] + + +class options(stringify.StringifyMixin): + """DHCP (RFC 3315) options encoder/decoder class. + + This is used with ryu.lib.packet.dhcp6.dhcp6. + + """ + + def __init__(self, option_list=None, options_len=0): + super(options, self).__init__() + if option_list is None: + self.option_list = [] + else: + self.option_list = option_list + self.options_len = options_len + + @classmethod + def parser(cls, buf): + opt_parse_list = [] + offset = 0 + while len(buf) > offset: + opt_buf = buf[offset:] + opt = option.parser(opt_buf) + opt_parse_list.append(opt) + offset += opt.length + 4 + return cls(opt_parse_list, len(buf)) + + def serialize(self): + seri_opt = "" + for opt in self.option_list: + seri_opt += opt.serialize() + if self.options_len == 0: + self.options_len = len(seri_opt) + return seri_opt + + +class option(stringify.StringifyMixin): + """DHCP (RFC 3315) options encoder/decoder class. + + This is used with ryu.lib.packet.dhcp6.dhcp6.options. + + An instance has the following attributes at least. + Most of them are same to the on-wire counterparts but in host byte order. + __init__ takes the corresponding args in this order. + + The format of DHCP options is: + + \ 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 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | option-code | option-len | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | option-data | + | (option-len octets) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + ============== ==================== + Attribute Description + ============== ==================== + option-code An unsigned integer identifying the specific option\ + type carried in this option. + option-len An unsigned integer giving the length of the\ + option-data field in this option in octets. + option-data The data for the option; the format of this data\ + depends on the definition of the option. + ============== ==================== + """ + _UNPACK_STR = '!H' + _UNPACK_STR_LEN = struct.calcsize(_UNPACK_STR) + _PACK_STR = '!HH%ds' + + def __init__(self, code, data, length=0): + super(option, self).__init__() + self.code = code + self.data = data + self.length = length + + @classmethod + def parser(cls, buf): + code = struct.unpack_from(cls._UNPACK_STR, buf)[0] + buf = buf[cls._UNPACK_STR_LEN:] + length = struct.unpack_from(cls._UNPACK_STR, buf)[0] + buf = buf[cls._UNPACK_STR_LEN:] + value_unpack_str = '%ds' % length + data = struct.unpack_from(value_unpack_str, buf)[0] + return cls(code, data, length) + + def serialize(self): + if self.length == 0: + self.length = len(self.data) + options_pack_str = self._PACK_STR % self.length + return struct.pack(options_pack_str, self.code, self.length, self.data) diff --git a/ryu/lib/packet/udp.py b/ryu/lib/packet/udp.py index 9d3ed3c5..0e25f418 100644 --- a/ryu/lib/packet/udp.py +++ b/ryu/lib/packet/udp.py @@ -18,6 +18,7 @@ import struct from . import packet_base from . import packet_utils from . import dhcp +from . import dhcp6 from . import vxlan from . import geneve @@ -53,11 +54,12 @@ class udp(packet_base.PacketBase): @staticmethod def get_packet_type(src_port, dst_port): - if ((src_port == 68 and dst_port == 67) or - (src_port == 67 and dst_port == 68) or - (src_port == 67 and - dst_port == 67)): + if ((src_port in [67, 68] and dst_port == 67) or + (dst_port in [67, 68] and src_port == 67)): return dhcp.dhcp + if ((src_port in [546, 547] and dst_port == 547) or + (dst_port in [546, 547] and src_port == 547)): + return dhcp6.dhcp6 if (dst_port == vxlan.UDP_DST_PORT or dst_port == vxlan.UDP_DST_PORT_OLD): return vxlan.vxlan |