1 # Copyright (C) 2012 Nippon Telegraph and Telephone Corporation.
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
21 from . import packet_base
22 from . import packet_utils
23 from ryu.lib import stringify
31 ICMP_TIME_EXCEEDED = 11
33 ICMP_ECHO_REPLY_CODE = 0
34 ICMP_HOST_UNREACH_CODE = 1
35 ICMP_PORT_UNREACH_CODE = 3
36 ICMP_TTL_EXPIRED_CODE = 0
39 class icmp(packet_base.PacketBase):
40 """ICMP (RFC 792) header encoder/decoder class.
42 An instance has the following attributes at least.
43 Most of them are same to the on-wire counterparts but in host byte order.
44 __init__ takes the corresponding args in this order.
46 .. tabularcolumns:: |l|L|
48 ============== ====================
50 ============== ====================
54 (0 means automatically-calculate when encoding)
56 Either a bytearray, or \
57 ryu.lib.packet.icmp.echo or \
58 ryu.lib.packet.icmp.dest_unreach or \
59 ryu.lib.packet.icmp.TimeExceeded object \
61 This includes "unused" 16 bits and the following \
62 "Internet Header + 64 bits of Original Data Datagram" of \
64 NOTE for icmp.dest_unreach and icmp.TimeExceeded: \
65 This includes "unused" 8 or 24 bits and the following \
66 "Internet Header + leading octets of original datagram" \
67 of the original packet.
68 ============== ====================
72 _MIN_LEN = struct.calcsize(_PACK_STR)
76 def register_icmp_type(*args):
77 def _register_icmp_type(cls):
79 icmp._ICMP_TYPES[type_] = cls
81 return _register_icmp_type
83 def __init__(self, type_=ICMP_ECHO_REQUEST, code=0, csum=0, data=b''):
84 super(icmp, self).__init__()
92 (type_, code, csum) = struct.unpack_from(cls._PACK_STR, buf)
93 msg = cls(type_, code, csum)
97 cls_ = cls._ICMP_TYPES.get(type_, None)
99 msg.data = cls_.parser(buf, offset)
101 msg.data = buf[offset:]
103 return msg, None, None
105 def serialize(self, payload, prev):
106 hdr = bytearray(struct.pack(icmp._PACK_STR, self.type,
107 self.code, self.csum))
110 if self.type in icmp._ICMP_TYPES:
111 assert isinstance(self.data, _ICMPv4Payload)
112 hdr += self.data.serialize()
117 hdr += self.data.serialize()
120 self.csum = packet_utils.checksum(hdr)
121 struct.pack_into('!H', hdr, 2, self.csum)
126 return self._MIN_LEN + len(self.data)
129 @six.add_metaclass(abc.ABCMeta)
130 class _ICMPv4Payload(stringify.StringifyMixin):
132 Base class for the payload of ICMPv4 packet.
136 @icmp.register_icmp_type(ICMP_ECHO_REPLY, ICMP_ECHO_REQUEST)
137 class echo(_ICMPv4Payload):
138 """ICMP sub encoder/decoder class for Echo and Echo Reply messages.
140 This is used with ryu.lib.packet.icmp.icmp for
141 ICMP Echo and Echo Reply messages.
143 An instance has the following attributes at least.
144 Most of them are same to the on-wire counterparts but in host byte order.
145 __init__ takes the corresponding args in this order.
147 .. tabularcolumns:: |l|L|
149 ============== ====================
150 Attribute Description
151 ============== ====================
154 data Internet Header + 64 bits of Original Data Datagram
155 ============== ====================
159 _MIN_LEN = struct.calcsize(_PACK_STR)
161 def __init__(self, id_=0, seq=0, data=None):
162 super(echo, self).__init__()
168 def parser(cls, buf, offset):
169 (id_, seq) = struct.unpack_from(cls._PACK_STR, buf, offset)
171 offset += cls._MIN_LEN
173 if len(buf) > offset:
174 msg.data = buf[offset:]
179 hdr = bytearray(struct.pack(echo._PACK_STR, self.id,
182 if self.data is not None:
188 length = self._MIN_LEN
189 if self.data is not None:
190 length += len(self.data)
194 @icmp.register_icmp_type(ICMP_DEST_UNREACH)
195 class dest_unreach(_ICMPv4Payload):
196 """ICMP sub encoder/decoder class for Destination Unreachable Message.
198 This is used with ryu.lib.packet.icmp.icmp for
199 ICMP Destination Unreachable Message.
201 An instance has the following attributes at least.
202 Most of them are same to the on-wire counterparts but in host byte order.
203 __init__ takes the corresponding args in this order.
205 [RFC1191] reserves bits for the "Next-Hop MTU" field.
206 [RFC4884] introduced 8-bit data length attribute.
208 .. tabularcolumns:: |l|p{35em}|
210 ============== =====================================================
211 Attribute Description
212 ============== =====================================================
216 NOTE: This field is required when icmp code is 4
218 code 4 = fragmentation needed and DF set
219 data Internet Header + leading octets of original datagram
220 ============== =====================================================
224 _MIN_LEN = struct.calcsize(_PACK_STR)
226 def __init__(self, data_len=0, mtu=0, data=None):
227 super(dest_unreach, self).__init__()
229 if ((data_len >= 0) and (data_len <= 255)):
230 self.data_len = data_len
232 raise ValueError('Specified data length (%d) is invalid.' % data_len)
238 def parser(cls, buf, offset):
239 (data_len, mtu) = struct.unpack_from(cls._PACK_STR,
241 msg = cls(data_len, mtu)
242 offset += cls._MIN_LEN
244 if len(buf) > offset:
245 msg.data = buf[offset:]
250 hdr = bytearray(struct.pack(dest_unreach._PACK_STR,
251 self.data_len, self.mtu))
253 if self.data is not None:
259 length = self._MIN_LEN
260 if self.data is not None:
261 length += len(self.data)
265 @icmp.register_icmp_type(ICMP_TIME_EXCEEDED)
266 class TimeExceeded(_ICMPv4Payload):
267 """ICMP sub encoder/decoder class for Time Exceeded Message.
269 This is used with ryu.lib.packet.icmp.icmp for
270 ICMP Time Exceeded Message.
272 An instance has the following attributes at least.
273 Most of them are same to the on-wire counterparts but in host byte order.
274 __init__ takes the corresponding args in this order.
276 [RFC4884] introduced 8-bit data length attribute.
278 .. tabularcolumns:: |l|L|
280 ============== ====================
281 Attribute Description
282 ============== ====================
284 data Internet Header + leading octets of original datagram
285 ============== ====================
289 _MIN_LEN = struct.calcsize(_PACK_STR)
291 def __init__(self, data_len=0, data=None):
292 if (data_len >= 0) and (data_len <= 255):
293 self.data_len = data_len
295 raise ValueError('Specified data length (%d) is invalid.' % data_len)
300 def parser(cls, buf, offset):
301 (data_len, ) = struct.unpack_from(cls._PACK_STR, buf, offset)
303 offset += cls._MIN_LEN
305 if len(buf) > offset:
306 msg.data = buf[offset:]
311 hdr = bytearray(struct.pack(TimeExceeded._PACK_STR, self.data_len))
313 if self.data is not None:
319 length = self._MIN_LEN
320 if self.data is not None:
321 length += len(self.data)
325 icmp.set_classes(icmp._ICMP_TYPES)