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>
This commit is contained in:
parent
2197fd55d1
commit
6d4b094dc3
@ -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
|
||||
|
291
ryu/lib/packet/dhcp6.py
Normal file
291
ryu/lib/packet/dhcp6.py
Normal file
@ -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)
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user