1# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
2#
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
6#
7#    http://www.apache.org/licenses/LICENSE-2.0
8#
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
12# implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16import abc
17import six
18import struct
19from ryu.lib import addrconv
20from ryu.lib import stringify
21from ryu.lib.packet import packet_base
22
23# IEEE 802.1ag OpCode
24CFM_CC_MESSAGE = 0x01
25CFM_LOOPBACK_REPLY = 0x02
26CFM_LOOPBACK_MESSAGE = 0x03
27CFM_LINK_TRACE_REPLY = 0x04
28CFM_LINK_TRACE_MESSAGE = 0x05
29
30# IEEE 802.1ag TLV type
31CFM_END_TLV = 0x00
32CFM_SENDER_ID_TLV = 0x01
33CFM_PORT_STATUS_TLV = 0x02
34CFM_DATA_TLV = 0x03
35CFM_INTERFACE_STATUS_TLV = 0x04
36CFM_REPLY_INGRESS_TLV = 0x05
37CFM_REPLY_EGRESS_TLV = 0x06
38CFM_LTM_EGRESS_IDENTIFIER_TLV = 0x07
39CFM_LTR_EGRESS_IDENTIFIER_TLV = 0x08
40CFM_ORGANIZATION_SPECIFIC_TLV = 0x1f
41
42# IEEE 802.1ag CFM version
43CFM_VERSION = 0
44
45
46class cfm(packet_base.PacketBase):
47    """CFM (Connectivity Fault Management) Protocol header class.
48
49    http://standards.ieee.org/getieee802/download/802.1ag-2007.pdf
50
51    OpCode Field range assignments
52
53    +---------------+--------------------------------------------------+
54    | OpCode range  | CFM PDU or organization                          |
55    +===============+==================================================+
56    | 0             | Reserved for IEEE 802.1                          |
57    +---------------+--------------------------------------------------+
58    | 1             | Continuity Check Message (CCM)                   |
59    +---------------+--------------------------------------------------+
60    | 2             | Loopback Reply (LBR)                             |
61    +---------------+--------------------------------------------------+
62    | 3             | Loopback Message (LBM)                           |
63    +---------------+--------------------------------------------------+
64    | 4             | Linktrace Reply (LTR)                            |
65    +---------------+--------------------------------------------------+
66    | 5             | Linktrace Message (LTM)                          |
67    +---------------+--------------------------------------------------+
68    | 06 - 31       | Reserved for IEEE 802.1                          |
69    +---------------+--------------------------------------------------+
70    | 32 - 63       | Defined by ITU-T Y.1731                          |
71    +---------------+--------------------------------------------------+
72    | 64 - 255      | Reserved for IEEE 802.1.                         |
73    +---------------+--------------------------------------------------+
74
75    An instance has the following attributes at least.
76    Most of them are same to the on-wire counterparts but in host byte order.
77    __init__ takes the corresponding args in this order.
78
79    .. tabularcolumns:: |l|L|
80
81    ============== ========================================
82    Attribute      Description
83    ============== ========================================
84    op             CFM PDU
85    ============== ========================================
86
87    """
88    _PACK_STR = '!B'
89    _MIN_LEN = struct.calcsize(_PACK_STR)
90    _CFM_OPCODE = {}
91    _TYPE = {
92        'ascii': [
93            'ltm_orig_addr', 'ltm_targ_addr'
94        ]
95    }
96
97    @staticmethod
98    def register_cfm_opcode(type_):
99        def _register_cfm_opcode(cls):
100            cfm._CFM_OPCODE[type_] = cls
101            return cls
102        return _register_cfm_opcode
103
104    def __init__(self, op=None):
105        super(cfm, self).__init__()
106        assert isinstance(op, operation)
107        self.op = op
108
109    @classmethod
110    def parser(cls, buf):
111        (opcode, ) = struct.unpack_from(cls._PACK_STR, buf, cls._MIN_LEN)
112        cls_ = cls._CFM_OPCODE.get(opcode)
113        op = cls_.parser(buf)
114        instance = cls(op)
115        rest = buf[len(instance):]
116        return instance, None, rest
117
118    def serialize(self, payload, prev):
119        buf = self.op.serialize()
120        return buf
121
122    def __len__(self):
123        return len(self.op)
124
125
126@six.add_metaclass(abc.ABCMeta)
127class operation(stringify.StringifyMixin):
128
129    _TLV_TYPES = {}
130    _END_TLV_LEN = 1
131
132    @staticmethod
133    def register_tlv_types(type_):
134        def _register_tlv_types(cls):
135            operation._TLV_TYPES[type_] = cls
136            return cls
137        return _register_tlv_types
138
139    def __init__(self, md_lv, version, tlvs):
140        self.md_lv = md_lv
141        self.version = version
142        tlvs = tlvs or []
143        assert isinstance(tlvs, list)
144        for tlv_ in tlvs:
145            assert isinstance(tlv_, tlv)
146        self.tlvs = tlvs
147
148    @classmethod
149    @abc.abstractmethod
150    def parser(cls, buf):
151        pass
152
153    @abc.abstractmethod
154    def serialize(self):
155        pass
156
157    @abc.abstractmethod
158    def __len__(self):
159        pass
160
161    @classmethod
162    def _parser_tlvs(cls, buf):
163        offset = 0
164        tlvs = []
165        while True:
166            (type_, ) = struct.unpack_from('!B', buf, offset)
167            cls_ = cls._TLV_TYPES.get(type_)
168            if not cls_:
169                assert type_ is CFM_END_TLV
170                break
171            tlv_ = cls_.parser(buf[offset:])
172            tlvs.append(tlv_)
173            offset += len(tlv_)
174        return tlvs
175
176    @staticmethod
177    def _serialize_tlvs(tlvs):
178        buf = bytearray()
179        for tlv_ in tlvs:
180            buf.extend(tlv_.serialize())
181        return buf
182
183    def _calc_len(self, len_):
184        for tlv_ in self.tlvs:
185            len_ += len(tlv_)
186        len_ += self._END_TLV_LEN
187        return len_
188
189
190@cfm.register_cfm_opcode(CFM_CC_MESSAGE)
191class cc_message(operation):
192
193    """CFM (IEEE Std 802.1ag-2007) Continuity Check Message (CCM)
194    encoder/decoder class.
195
196    This is used with ryu.lib.packet.cfm.cfm.
197
198    An instance has the following attributes at least.
199    Most of them are same to the on-wire counterparts but in host byte order.
200    __init__ takes the corresponding args in this order.
201
202    .. tabularcolumns:: |l|L|
203
204    ==================== =======================================
205    Attribute            Description
206    ==================== =======================================
207    md_lv                Maintenance Domain Level.
208    version              The protocol version number.
209    rdi                  RDI bit.
210    interval             CCM Interval.The default is 4 (1 frame/s)
211    seq_num              Sequence Number.
212    mep_id               Maintenance association End Point Identifier.
213    md_name_format       Maintenance Domain Name Format.
214                         The default is 4 (Character string)
215    md_name_length       Maintenance Domain Name Length.
216                         (0 means automatically-calculate
217                         when encoding.)
218    md_name              Maintenance Domain Name.
219    short_ma_name_format Short MA Name Format.
220                         The default is 2 (Character string)
221    short_ma_name_length Short MA Name Format Length.
222                         (0 means automatically-calculate
223                         when encoding.)
224    short_ma_name        Short MA Name.
225    tlvs                 TLVs.
226    ==================== =======================================
227    """
228
229    _PACK_STR = '!4BIHB'
230
231    _MIN_LEN = struct.calcsize(_PACK_STR)
232    _TLV_OFFSET = 70
233    _MD_NAME_FORMAT_LEN = 1
234    _MD_NAME_LENGTH_LEN = 1
235    _SHORT_MA_NAME_FORMAT_LEN = 1
236    _SHORT_MA_NAME_LENGTH_LEN = 1
237    _MA_ID_LEN = 64
238
239    # Maintenance Domain Name Format
240    _MD_FMT_NO_MD_NAME_PRESENT = 1
241    _MD_FMT_DOMAIN_NAME_BASED_STRING = 2
242    _MD_FMT_MAC_ADDRESS_TWO_OCTET_INTEGER = 3
243    _MD_FMT_CHARACTER_STRING = 4
244
245    # Short MA Name Format
246    _SHORT_MA_FMT_PRIMARY_VID = 1
247    _SHORT_MA_FMT_CHARACTER_STRING = 2
248    _SHORT_MA_FMT_TWO_OCTET_INTEGER = 3
249    _SHORT_MA_FMT_RFC_2685_VPN_ID = 4
250
251    # CCM Transmission Interval
252    _INTERVAL_300_HZ = 1
253    _INTERVAL_10_MSEC = 2
254    _INTERVAL_100_MSEC = 3
255    _INTERVAL_1_SEC = 4
256    _INTERVAL_10_SEC = 5
257    _INTERVAL_1_MIN = 6
258    _INTERVAL_10_MIN = 6
259
260    def __init__(self, md_lv=0, version=CFM_VERSION,
261                 rdi=0, interval=_INTERVAL_1_SEC, seq_num=0, mep_id=1,
262                 md_name_format=_MD_FMT_CHARACTER_STRING,
263                 md_name_length=0, md_name=b"0",
264                 short_ma_name_format=_SHORT_MA_FMT_CHARACTER_STRING,
265                 short_ma_name_length=0, short_ma_name=b"1",
266                 tlvs=None):
267        super(cc_message, self).__init__(md_lv, version, tlvs)
268        self._opcode = CFM_CC_MESSAGE
269        assert rdi in [0, 1]
270        self.rdi = rdi
271        assert interval is not 0
272        self.interval = interval
273        self.seq_num = seq_num
274        assert 1 <= mep_id <= 8191
275        self.mep_id = mep_id
276        self.md_name_format = md_name_format
277        self.md_name_length = md_name_length
278        self.md_name = md_name
279        self.short_ma_name_format = short_ma_name_format
280        self.short_ma_name_length = short_ma_name_length
281        self.short_ma_name = short_ma_name
282
283    @classmethod
284    def parser(cls, buf):
285        (md_lv_version, opcode, flags, tlv_offset, seq_num, mep_id,
286         md_name_format) = struct.unpack_from(cls._PACK_STR, buf)
287        md_name_length = 0
288        md_name = b""
289        md_lv = int(md_lv_version >> 5)
290        version = int(md_lv_version & 0x1f)
291        rdi = int(flags >> 7)
292        interval = int(flags & 0x07)
293        offset = cls._MIN_LEN
294        # parse md_name
295        if md_name_format != cls._MD_FMT_NO_MD_NAME_PRESENT:
296            (md_name_length, ) = struct.unpack_from("!B", buf, offset)
297            offset += cls._MD_NAME_LENGTH_LEN
298            form = "%dB" % md_name_length
299            md_name = struct.unpack_from(form, buf, offset)
300            offset += md_name_length
301        # parse short_ma_name
302        (short_ma_name_format,
303         short_ma_name_length) = struct.unpack_from("!2B", buf, offset)
304        offset += (cls._SHORT_MA_NAME_FORMAT_LEN +
305                   cls._SHORT_MA_NAME_LENGTH_LEN)
306        form = "%dB" % short_ma_name_length
307        short_ma_name = struct.unpack_from(form, buf, offset)
308        offset = cls._MIN_LEN + (cls._MA_ID_LEN - cls._MD_NAME_FORMAT_LEN)
309        tlvs = cls._parser_tlvs(buf[offset:])
310        # ascii to text
311        if md_name_format == cls._MD_FMT_DOMAIN_NAME_BASED_STRING or \
312           md_name_format == cls._MD_FMT_CHARACTER_STRING:
313            md_name = b"".join(map(six.int2byte, md_name))
314        if short_ma_name_format == cls._SHORT_MA_FMT_CHARACTER_STRING:
315            short_ma_name = b"".join(map(six.int2byte, short_ma_name))
316        return cls(md_lv, version, rdi, interval, seq_num, mep_id,
317                   md_name_format, md_name_length,
318                   md_name,
319                   short_ma_name_format, short_ma_name_length,
320                   short_ma_name,
321                   tlvs)
322
323    def serialize(self):
324        buf = struct.pack(self._PACK_STR,
325                          (self.md_lv << 5) | self.version,
326                          self._opcode,
327                          (self.rdi << 7) | self.interval,
328                          self._TLV_OFFSET,
329                          self.seq_num, self.mep_id, self.md_name_format)
330        buf = bytearray(buf)
331        # Maintenance Domain Name
332        if self.md_name_format != self._MD_FMT_NO_MD_NAME_PRESENT:
333            if self.md_name_length == 0:
334                self.md_name_length = len(self.md_name)
335            buf.extend(struct.pack('!B%ds' % self.md_name_length,
336                                   self.md_name_length, self.md_name))
337        # Short MA Name
338        if self.short_ma_name_length == 0:
339            self.short_ma_name_length = len(self.short_ma_name)
340        buf.extend(struct.pack('!2B%ds' % self.short_ma_name_length,
341                               self.short_ma_name_format,
342                               self.short_ma_name_length,
343                               self.short_ma_name
344                               ))
345        # 0 pad
346        maid_length = (self._MD_NAME_FORMAT_LEN +
347                       self._SHORT_MA_NAME_FORMAT_LEN +
348                       self._SHORT_MA_NAME_LENGTH_LEN +
349                       self.short_ma_name_length)
350        if self.md_name_format != self._MD_FMT_NO_MD_NAME_PRESENT:
351            maid_length += self._MD_NAME_LENGTH_LEN + self.md_name_length
352        buf.extend(bytearray(self._MA_ID_LEN - maid_length))
353        # tlvs
354        if self.tlvs:
355            buf.extend(self._serialize_tlvs(self.tlvs))
356        buf.extend(struct.pack("!B", CFM_END_TLV))
357        return buf
358
359    def __len__(self):
360        return self._calc_len(
361            (self._MIN_LEN - self._MD_NAME_FORMAT_LEN) + self._MA_ID_LEN)
362
363
364class loopback(operation):
365
366    _PACK_STR = '!4BI'
367    _MIN_LEN = struct.calcsize(_PACK_STR)
368    _TLV_OFFSET = 4
369
370    @abc.abstractmethod
371    def __init__(self, md_lv, version, transaction_id, tlvs):
372        super(loopback, self).__init__(md_lv, version, tlvs)
373        self._flags = 0
374        self.transaction_id = transaction_id
375
376    @classmethod
377    def parser(cls, buf):
378        (md_lv_version, opcode, flags, tlv_offset,
379         transaction_id) = struct.unpack_from(cls._PACK_STR, buf)
380        md_lv = int(md_lv_version >> 5)
381        version = int(md_lv_version & 0x1f)
382        tlvs = cls._parser_tlvs(buf[cls._MIN_LEN:])
383        return cls(md_lv, version, transaction_id, tlvs)
384
385    def serialize(self):
386        buf = struct.pack(self._PACK_STR,
387                          (self.md_lv << 5) | self.version,
388                          self._opcode,
389                          self._flags,
390                          self._TLV_OFFSET,
391                          self.transaction_id,
392                          )
393        buf = bytearray(buf)
394        # tlvs
395        if self.tlvs:
396            buf.extend(self._serialize_tlvs(self.tlvs))
397        buf.extend(struct.pack("!B", CFM_END_TLV))
398
399        return buf
400
401    def __len__(self):
402        return self._calc_len(self._MIN_LEN)
403
404
405@cfm.register_cfm_opcode(CFM_LOOPBACK_MESSAGE)
406class loopback_message(loopback):
407
408    """CFM (IEEE Std 802.1ag-2007) Loopback Message (LBM) encoder/decoder class.
409
410    This is used with ryu.lib.packet.cfm.cfm.
411
412    An instance has the following attributes at least.
413    Most of them are same to the on-wire counterparts but in host byte order.
414    __init__ takes the corresponding args in this order.
415
416    .. tabularcolumns:: |l|L|
417
418    ================= =======================================
419    Attribute         Description
420    ================= =======================================
421    md_lv             Maintenance Domain Level.
422    version           The protocol version number.
423    transaction_id    Loopback Transaction Identifier.
424    tlvs              TLVs.
425    ================= =======================================
426    """
427
428    def __init__(self, md_lv=0, version=CFM_VERSION,
429                 transaction_id=0,
430                 tlvs=None,
431                 ):
432        super(loopback_message, self).__init__(md_lv, version,
433                                               transaction_id,
434                                               tlvs)
435        self._opcode = CFM_LOOPBACK_MESSAGE
436
437
438@cfm.register_cfm_opcode(CFM_LOOPBACK_REPLY)
439class loopback_reply(loopback):
440
441    """CFM (IEEE Std 802.1ag-2007) Loopback Reply (LBR) encoder/decoder class.
442
443    This is used with ryu.lib.packet.cfm.cfm.
444
445    An instance has the following attributes at least.
446    Most of them are same to the on-wire counterparts but in host byte order.
447    __init__ takes the corresponding args in this order.
448
449    .. tabularcolumns:: |l|L|
450
451    ==================== =======================================
452    Attribute            Description
453    ==================== =======================================
454    md_lv                Maintenance Domain Level.
455    version              The protocol version number.
456    transaction_id       Loopback Transaction Identifier.
457    tlvs                 TLVs.
458    ==================== =======================================
459    """
460
461    def __init__(self, md_lv=0, version=CFM_VERSION,
462                 transaction_id=0,
463                 tlvs=None,
464                 ):
465        super(loopback_reply, self).__init__(md_lv, version,
466                                             transaction_id,
467                                             tlvs)
468        self._opcode = CFM_LOOPBACK_REPLY
469
470
471class link_trace(operation):
472
473    @abc.abstractmethod
474    def __init__(self, md_lv, version, use_fdb_only,
475                 transaction_id, ttl, tlvs):
476        super(link_trace, self).__init__(md_lv, version, tlvs)
477        assert use_fdb_only in [0, 1]
478        self.use_fdb_only = use_fdb_only
479        self.transaction_id = transaction_id
480        self.ttl = ttl
481
482    @classmethod
483    @abc.abstractmethod
484    def parser(cls, buf):
485        pass
486
487    @abc.abstractmethod
488    def serialize(self):
489        pass
490
491    def __len__(self):
492        return self._calc_len(self._MIN_LEN)
493
494
495@cfm.register_cfm_opcode(CFM_LINK_TRACE_MESSAGE)
496class link_trace_message(link_trace):
497
498    """CFM (IEEE Std 802.1ag-2007) Linktrace Message (LTM)
499    encoder/decoder class.
500
501    This is used with ryu.lib.packet.cfm.cfm.
502
503    An instance has the following attributes at least.
504    Most of them are same to the on-wire counterparts but in host byte order.
505    __init__ takes the corresponding args in this order.
506
507    .. tabularcolumns:: |l|L|
508
509    ==================== =======================================
510    Attribute            Description
511    ==================== =======================================
512    md_lv                Maintenance Domain Level.
513    version              The protocol version number.
514    use_fdb_only         UseFDBonly bit.
515    transaction_id       LTM Transaction Identifier.
516    ttl                  LTM TTL.
517    ltm_orig_addr        Original MAC Address.
518    ltm_targ_addr        Target MAC Address.
519    tlvs                 TLVs.
520    ==================== =======================================
521    """
522
523    _PACK_STR = '!4BIB6s6s'
524    _ALL_PACK_LEN = struct.calcsize(_PACK_STR)
525    _MIN_LEN = _ALL_PACK_LEN
526    _TLV_OFFSET = 17
527    _TYPE = {
528        'ascii': [
529            'ltm_orig_addr', 'ltm_targ_addr'
530        ]
531    }
532
533    def __init__(self, md_lv=0, version=CFM_VERSION,
534                 use_fdb_only=1,
535                 transaction_id=0,
536                 ttl=64,
537                 ltm_orig_addr='00:00:00:00:00:00',
538                 ltm_targ_addr='00:00:00:00:00:00',
539                 tlvs=None
540                 ):
541        super(link_trace_message, self).__init__(md_lv, version,
542                                                 use_fdb_only,
543                                                 transaction_id,
544                                                 ttl,
545                                                 tlvs)
546        self._opcode = CFM_LINK_TRACE_MESSAGE
547        self.ltm_orig_addr = ltm_orig_addr
548        self.ltm_targ_addr = ltm_targ_addr
549
550    @classmethod
551    def parser(cls, buf):
552        (md_lv_version, opcode, flags, tlv_offset, transaction_id, ttl,
553         ltm_orig_addr, ltm_targ_addr) = struct.unpack_from(cls._PACK_STR, buf)
554        md_lv = int(md_lv_version >> 5)
555        version = int(md_lv_version & 0x1f)
556        use_fdb_only = int(flags >> 7)
557        tlvs = cls._parser_tlvs(buf[cls._MIN_LEN:])
558        return cls(md_lv, version, use_fdb_only,
559                   transaction_id, ttl,
560                   addrconv.mac.bin_to_text(ltm_orig_addr),
561                   addrconv.mac.bin_to_text(ltm_targ_addr),
562                   tlvs)
563
564    def serialize(self):
565        buf = struct.pack(self._PACK_STR,
566                          (self.md_lv << 5) | self.version,
567                          self._opcode,
568                          self.use_fdb_only << 7,
569                          self._TLV_OFFSET,
570                          self.transaction_id,
571                          self.ttl,
572                          addrconv.mac.text_to_bin(self.ltm_orig_addr),
573                          addrconv.mac.text_to_bin(self.ltm_targ_addr),
574                          )
575        buf = bytearray(buf)
576        if self.tlvs:
577            buf.extend(self._serialize_tlvs(self.tlvs))
578        buf.extend(struct.pack("!B", CFM_END_TLV))
579        return buf
580
581
582@cfm.register_cfm_opcode(CFM_LINK_TRACE_REPLY)
583class link_trace_reply(link_trace):
584
585    """CFM (IEEE Std 802.1ag-2007) Linktrace Reply (LTR) encoder/decoder class.
586
587    This is used with ryu.lib.packet.cfm.cfm.
588
589    An instance has the following attributes at least.
590    Most of them are same to the on-wire counterparts but in host byte order.
591    __init__ takes the corresponding args in this order.
592
593    .. tabularcolumns:: |l|L|
594
595    ==================== =======================================
596    Attribute            Description
597    ==================== =======================================
598    version              The protocol version number.
599    use_fdb_only         UseFDBonly bit.
600    fwd_yes              FwdYes bit.
601    terminal_mep         TerminalMep bit.
602    transaction_id       LTR Transaction Identifier.
603    ttl                  Reply TTL.
604    relay_action         Relay Action.The default is 1 (RlyHit)
605    tlvs                 TLVs.
606    ==================== =======================================
607    """
608    _PACK_STR = '!4BIBB'
609    _ALL_PACK_LEN = struct.calcsize(_PACK_STR)
610    _MIN_LEN = _ALL_PACK_LEN
611    _TLV_OFFSET = 6
612
613    # Relay Action field values
614    _RLY_HIT = 1
615    _RLY_FDB = 2
616    _RLY_MPDB = 3
617
618    def __init__(self, md_lv=0, version=CFM_VERSION, use_fdb_only=1,
619                 fwd_yes=0, terminal_mep=1, transaction_id=0,
620                 ttl=64, relay_action=_RLY_HIT, tlvs=None
621                 ):
622        super(link_trace_reply, self).__init__(md_lv, version,
623                                               use_fdb_only,
624                                               transaction_id,
625                                               ttl,
626                                               tlvs)
627        self._opcode = CFM_LINK_TRACE_REPLY
628        assert fwd_yes in [0, 1]
629        self.fwd_yes = fwd_yes
630        assert terminal_mep in [0, 1]
631        self.terminal_mep = terminal_mep
632        assert relay_action in [self._RLY_HIT, self._RLY_FDB, self._RLY_MPDB]
633        self.relay_action = relay_action
634
635    @classmethod
636    def parser(cls, buf):
637        (md_lv_version, opcode, flags, tlv_offset, transaction_id, ttl,
638         relay_action) = struct.unpack_from(cls._PACK_STR, buf)
639        md_lv = int(md_lv_version >> 5)
640        version = int(md_lv_version & 0x1f)
641        use_fdb_only = int(flags >> 7)
642        fwd_yes = int(flags >> 6 & 0x01)
643        terminal_mep = int(flags >> 5 & 0x01)
644        tlvs = cls._parser_tlvs(buf[cls._MIN_LEN:])
645        return cls(md_lv, version, use_fdb_only, fwd_yes, terminal_mep,
646                   transaction_id, ttl, relay_action, tlvs)
647
648    def serialize(self):
649        buf = struct.pack(self._PACK_STR,
650                          (self.md_lv << 5) | self.version,
651                          self._opcode,
652                          (self.use_fdb_only << 7) |
653                          (self.fwd_yes << 6) |
654                          (self.terminal_mep << 5),
655                          self._TLV_OFFSET,
656                          self.transaction_id,
657                          self.ttl,
658                          self.relay_action,
659                          )
660        buf = bytearray(buf)
661        if self.tlvs:
662            buf.extend(self._serialize_tlvs(self.tlvs))
663        buf.extend(struct.pack("!B", CFM_END_TLV))
664        return buf
665
666
667cfm.set_classes(cfm._CFM_OPCODE)
668
669
670@six.add_metaclass(abc.ABCMeta)
671class tlv(stringify.StringifyMixin):
672
673    _TYPE_LEN = 1
674    _LENGTH_LEN = 2
675    _TYPE = {
676        'ascii': [
677            'egress_id_mac', 'last_egress_id_mac',
678            'next_egress_id_mac', 'mac_address'
679        ]
680    }
681
682    def __init__(self, length):
683        self.length = length
684
685    @classmethod
686    @abc.abstractmethod
687    def parser(cls, buf):
688        pass
689
690    @abc.abstractmethod
691    def serialize(self):
692        pass
693
694    def __len__(self):
695        return self.length + self._TYPE_LEN + self._LENGTH_LEN
696
697
698@operation.register_tlv_types(CFM_SENDER_ID_TLV)
699class sender_id_tlv(tlv):
700
701    """CFM (IEEE Std 802.1ag-2007) Sender ID TLV encoder/decoder class.
702
703    This is used with ryu.lib.packet.cfm.cfm.
704
705    An instance has the following attributes at least.
706    Most of them are same to the on-wire counterparts but in host byte order.
707    __init__ takes the corresponding args in this order.
708
709    .. tabularcolumns:: |l|L|
710
711    ==================== =======================================
712    Attribute            Description
713    ==================== =======================================
714    length               Length of Value field.
715                         (0 means automatically-calculate when encoding.)
716    chassis_id_length    Chassis ID Length.
717                         (0 means automatically-calculate when encoding.)
718    chassis_id_subtype   Chassis ID Subtype.
719                         The default is 4 (Mac Address)
720    chassis_id           Chassis ID.
721    ma_domain_length     Management Address Domain Length.
722                         (0 means automatically-calculate when encoding.)
723    ma_domain            Management Address Domain.
724    ma_length            Management Address Length.
725                         (0 means automatically-calculate when encoding.)
726    ma                   Management Address.
727    ==================== =======================================
728    """
729
730    _PACK_STR = '!BHB'
731    _MIN_LEN = struct.calcsize(_PACK_STR)
732    _CHASSIS_ID_LENGTH_LEN = 1
733    _CHASSIS_ID_SUBTYPE_LEN = 1
734    _MA_DOMAIN_LENGTH_LEN = 1
735    _MA_LENGTH_LEN = 1
736
737    # Chassis ID subtype enumeration
738    _CHASSIS_ID_CHASSIS_COMPONENT = 1
739    _CHASSIS_ID_INTERFACE_ALIAS = 2
740    _CHASSIS_ID_PORT_COMPONENT = 3
741    _CHASSIS_ID_MAC_ADDRESS = 4
742    _CHASSIS_ID_NETWORK_ADDRESS = 5
743    _CHASSIS_ID_INTERFACE_NAME = 6
744    _CHASSIS_ID_LOCALLY_ASSIGNED = 7
745
746    def __init__(self,
747                 length=0,
748                 chassis_id_length=0,
749                 chassis_id_subtype=_CHASSIS_ID_MAC_ADDRESS,
750                 chassis_id=b'',
751                 ma_domain_length=0,
752                 ma_domain=b'',
753                 ma_length=0,
754                 ma=b''
755                 ):
756        super(sender_id_tlv, self).__init__(length)
757        self._type = CFM_SENDER_ID_TLV
758        self.chassis_id_length = chassis_id_length
759        assert chassis_id_subtype in [
760            self._CHASSIS_ID_CHASSIS_COMPONENT,
761            self._CHASSIS_ID_INTERFACE_ALIAS,
762            self._CHASSIS_ID_PORT_COMPONENT,
763            self._CHASSIS_ID_MAC_ADDRESS,
764            self._CHASSIS_ID_NETWORK_ADDRESS,
765            self._CHASSIS_ID_INTERFACE_NAME,
766            self._CHASSIS_ID_LOCALLY_ASSIGNED]
767        self.chassis_id_subtype = chassis_id_subtype
768        self.chassis_id = chassis_id
769        self.ma_domain_length = ma_domain_length
770        self.ma_domain = ma_domain
771        self.ma_length = ma_length
772        self.ma = ma
773
774    @classmethod
775    def parser(cls, buf):
776        (type_, length, chassis_id_length) = struct.unpack_from(cls._PACK_STR,
777                                                                buf)
778        chassis_id_subtype = 4
779        chassis_id = b''
780        ma_domain_length = 0
781        ma_domain = b''
782        ma_length = 0
783        ma = b''
784        offset = cls._MIN_LEN
785        if chassis_id_length != 0:
786            (chassis_id_subtype, ) = struct.unpack_from("!B", buf, offset)
787            offset += cls._CHASSIS_ID_SUBTYPE_LEN
788            form = "%ds" % chassis_id_length
789            (chassis_id,) = struct.unpack_from(form, buf, offset)
790            offset += chassis_id_length
791        if length + (cls._TYPE_LEN + cls._LENGTH_LEN) > offset:
792            (ma_domain_length, ) = struct.unpack_from("!B", buf, offset)
793            offset += cls._MA_DOMAIN_LENGTH_LEN
794            form = "%ds" % ma_domain_length
795            (ma_domain, ) = struct.unpack_from(form, buf, offset)
796            offset += ma_domain_length
797            if length + (cls._TYPE_LEN + cls._LENGTH_LEN) > offset:
798                (ma_length, ) = struct.unpack_from("!B", buf, offset)
799                offset += cls._MA_LENGTH_LEN
800                form = "%ds" % ma_length
801                (ma, ) = struct.unpack_from(form, buf, offset)
802        return cls(length, chassis_id_length, chassis_id_subtype,
803                   chassis_id, ma_domain_length, ma_domain, ma_length, ma)
804
805    def serialize(self):
806        # calculate length when it contains 0
807        if self.chassis_id_length == 0:
808            self.chassis_id_length = len(self.chassis_id)
809        if self.ma_domain_length == 0:
810            self.ma_domain_length = len(self.ma_domain)
811        if self.ma_length == 0:
812            self.ma_length = len(self.ma)
813        if self.length == 0:
814            self.length += self._CHASSIS_ID_LENGTH_LEN
815            if self.chassis_id_length != 0:
816                self.length += (self._CHASSIS_ID_SUBTYPE_LEN +
817                                self.chassis_id_length)
818            if self.chassis_id_length != 0 or self.ma_domain_length != 0:
819                self.length += self._MA_DOMAIN_LENGTH_LEN
820            if self.ma_domain_length != 0:
821                self.length += (self.ma_domain_length +
822                                self._MA_LENGTH_LEN + self.ma_length)
823        # start serialize
824        buf = struct.pack(self._PACK_STR,
825                          self._type,
826                          self.length,
827                          self.chassis_id_length
828                          )
829        buf = bytearray(buf)
830        # Chassis ID Subtype and Chassis ID present
831        # if the Chassis ID Length field contains not 0.
832        if self.chassis_id_length != 0:
833            buf.extend(struct.pack("!B", self.chassis_id_subtype))
834            form = "%ds" % self.chassis_id_length
835            buf.extend(struct.pack(form, self.chassis_id))
836        # Management Address Domain Length present
837        # if the Chassis ID Length field or Management Address Length field
838        # contains not 0.
839        if self.chassis_id_length != 0 or self.ma_domain_length != 0:
840            buf.extend(struct.pack("!B", self.ma_domain_length))
841        # Management Address Domain present
842        # Management Address Domain Length field contains not 0.
843        if self.ma_domain_length != 0:
844            form = "%ds" % self.ma_domain_length
845            buf.extend(struct.pack(form, self.ma_domain))
846            buf.extend(struct.pack("!B", self.ma_length))
847            # Management Address present
848            # Management Address Length field contains not 0.
849            if self.ma_length != 0:
850                form = "%ds" % self.ma_length
851                buf.extend(struct.pack(form, self.ma))
852        return buf
853
854
855@operation.register_tlv_types(CFM_PORT_STATUS_TLV)
856class port_status_tlv(tlv):
857
858    """CFM (IEEE Std 802.1ag-2007) Port Status TLV encoder/decoder class.
859
860    This is used with ryu.lib.packet.cfm.cfm.
861
862    An instance has the following attributes at least.
863    Most of them are same to the on-wire counterparts but in host byte order.
864    __init__ takes the corresponding args in this order.
865
866    .. tabularcolumns:: |l|L|
867
868    ==================== =======================================
869    Attribute            Description
870    ==================== =======================================
871    length               Length of Value field.
872                         (0 means automatically-calculate when encoding.)
873    port_status          Port Status.The default is 1 (psUp)
874    ==================== =======================================
875    """
876
877    _PACK_STR = '!BHB'
878    _MIN_LEN = struct.calcsize(_PACK_STR)
879
880    # Port Status TLV values
881    _PS_BLOCKED = 1
882    _PS_UP = 2
883
884    def __init__(self, length=0, port_status=_PS_UP):
885        super(port_status_tlv, self).__init__(length)
886        self._type = CFM_PORT_STATUS_TLV
887        assert port_status in [self._PS_BLOCKED, self._PS_UP]
888        self.port_status = port_status
889
890    @classmethod
891    def parser(cls, buf):
892        (type_, length,
893         port_status) = struct.unpack_from(cls._PACK_STR, buf)
894        return cls(length, port_status)
895
896    def serialize(self):
897        # calculate length when it contains 0
898        if self.length == 0:
899            self.length = 1
900        # start serialize
901        buf = struct.pack(self._PACK_STR,
902                          self._type,
903                          self.length,
904                          self.port_status)
905        return bytearray(buf)
906
907
908@operation.register_tlv_types(CFM_DATA_TLV)
909class data_tlv(tlv):
910
911    """CFM (IEEE Std 802.1ag-2007) Data TLV encoder/decoder class.
912
913    This is used with ryu.lib.packet.cfm.cfm.
914
915    An instance has the following attributes at least.
916    Most of them are same to the on-wire counterparts but in host byte order.
917    __init__ takes the corresponding args in this order.
918
919    .. tabularcolumns:: |l|L|
920
921    ============== =======================================
922    Attribute      Description
923    ============== =======================================
924    length         Length of Value field.
925                   (0 means automatically-calculate when encoding)
926    data_value     Bit pattern of any of n octets.(n = length)
927    ============== =======================================
928    """
929
930    _PACK_STR = '!BH'
931    _MIN_LEN = struct.calcsize(_PACK_STR)
932
933    def __init__(self, length=0, data_value=b""
934                 ):
935        super(data_tlv, self).__init__(length)
936        self._type = CFM_DATA_TLV
937        self.data_value = data_value
938
939    @classmethod
940    def parser(cls, buf):
941        (type_, length) = struct.unpack_from(cls._PACK_STR, buf)
942        form = "%ds" % length
943        (data_value, ) = struct.unpack_from(form, buf, cls._MIN_LEN)
944        return cls(length, data_value)
945
946    def serialize(self):
947        # calculate length when it contains 0
948        if self.length == 0:
949            self.length = len(self.data_value)
950        # start serialize
951        buf = struct.pack(self._PACK_STR,
952                          self._type,
953                          self.length)
954        buf = bytearray(buf)
955        form = "%ds" % self.length
956        buf.extend(struct.pack(form, self.data_value))
957        return buf
958
959
960@operation.register_tlv_types(CFM_INTERFACE_STATUS_TLV)
961class interface_status_tlv(tlv):
962
963    """CFM (IEEE Std 802.1ag-2007) Interface Status TLV encoder/decoder class.
964
965    This is used with ryu.lib.packet.cfm.cfm.
966
967    An instance has the following attributes at least.
968    Most of them are same to the on-wire counterparts but in host byte order.
969    __init__ takes the corresponding args in this order.
970
971    .. tabularcolumns:: |l|L|
972
973    ==================== =======================================
974    Attribute            Description
975    ==================== =======================================
976    length               Length of Value field.
977                         (0 means automatically-calculate when encoding.)
978    interface_status     Interface Status.The default is 1 (isUp)
979    ==================== =======================================
980    """
981
982    _PACK_STR = '!BHB'
983    _MIN_LEN = struct.calcsize(_PACK_STR)
984
985    # Interface Status TLV values
986    _IS_UP = 1
987    _IS_DOWN = 2
988    _IS_TESTING = 3
989    _IS_UNKNOWN = 4
990    _IS_DORMANT = 5
991    _IS_NOT_PRESENT = 6
992    _IS_LOWER_LAYER_DOWN = 7
993
994    def __init__(self, length=0, interface_status=_IS_UP):
995        super(interface_status_tlv, self).__init__(length)
996        self._type = CFM_INTERFACE_STATUS_TLV
997        assert interface_status in [
998            self._IS_UP, self._IS_DOWN, self._IS_TESTING,
999            self._IS_UNKNOWN, self._IS_DORMANT, self._IS_NOT_PRESENT,
1000            self._IS_LOWER_LAYER_DOWN]
1001        self.interface_status = interface_status
1002
1003    @classmethod
1004    def parser(cls, buf):
1005        (type_, length,
1006         interface_status) = struct.unpack_from(cls._PACK_STR, buf)
1007        return cls(length, interface_status)
1008
1009    def serialize(self):
1010        # calculate length when it contains 0
1011        if self.length == 0:
1012            self.length = 1
1013        # start serialize
1014        buf = struct.pack(self._PACK_STR,
1015                          self._type,
1016                          self.length,
1017                          self.interface_status)
1018        return bytearray(buf)
1019
1020
1021@operation.register_tlv_types(CFM_LTM_EGRESS_IDENTIFIER_TLV)
1022class ltm_egress_identifier_tlv(tlv):
1023
1024    """CFM (IEEE Std 802.1ag-2007) LTM EGRESS TLV encoder/decoder class.
1025
1026    This is used with ryu.lib.packet.cfm.cfm.
1027
1028    An instance has the following attributes at least.
1029    Most of them are same to the on-wire counterparts but in host byte order.
1030    __init__ takes the corresponding args in this order.
1031
1032    .. tabularcolumns:: |l|L|
1033
1034    ============== =======================================
1035    Attribute      Description
1036    ============== =======================================
1037    length         Length of Value field.
1038                   (0 means automatically-calculate when encoding.)
1039    egress_id_ui   Egress Identifier of Unique ID.
1040    egress_id_mac  Egress Identifier of MAC address.
1041    ============== =======================================
1042    """
1043
1044    _PACK_STR = '!BHH6s'
1045    _MIN_LEN = struct.calcsize(_PACK_STR)
1046
1047    def __init__(self,
1048                 length=0,
1049                 egress_id_ui=0,
1050                 egress_id_mac='00:00:00:00:00:00'
1051                 ):
1052        super(ltm_egress_identifier_tlv, self).__init__(length)
1053        self._type = CFM_LTM_EGRESS_IDENTIFIER_TLV
1054        self.egress_id_ui = egress_id_ui
1055        self.egress_id_mac = egress_id_mac
1056
1057    @classmethod
1058    def parser(cls, buf):
1059        (type_, length, egress_id_ui,
1060         egress_id_mac) = struct.unpack_from(cls._PACK_STR, buf)
1061        return cls(length, egress_id_ui,
1062                   addrconv.mac.bin_to_text(egress_id_mac))
1063
1064    def serialize(self):
1065        # calculate length when it contains 0
1066        if self.length == 0:
1067            self.length = 8
1068        # start serialize
1069        buf = struct.pack(self._PACK_STR,
1070                          self._type,
1071                          self.length,
1072                          self.egress_id_ui,
1073                          addrconv.mac.text_to_bin(self.egress_id_mac)
1074                          )
1075        return bytearray(buf)
1076
1077
1078@operation.register_tlv_types(CFM_LTR_EGRESS_IDENTIFIER_TLV)
1079class ltr_egress_identifier_tlv(tlv):
1080
1081    """CFM (IEEE Std 802.1ag-2007) LTR EGRESS TLV encoder/decoder class.
1082
1083    This is used with ryu.lib.packet.cfm.cfm.
1084
1085    An instance has the following attributes at least.
1086    Most of them are same to the on-wire counterparts but in host byte order.
1087    __init__ takes the corresponding args in this order.
1088
1089    .. tabularcolumns:: |l|L|
1090
1091    ==================== =======================================
1092    Attribute            Description
1093    ==================== =======================================
1094    length               Length of Value field.
1095                         (0 means automatically-calculate when encoding.)
1096    last_egress_id_ui    Last Egress Identifier of Unique ID.
1097    last_egress_id_mac   Last Egress Identifier of MAC address.
1098    next_egress_id_ui    Next Egress Identifier of Unique ID.
1099    next_egress_id_mac   Next Egress Identifier of MAC address.
1100    ==================== =======================================
1101    """
1102
1103    _PACK_STR = '!BHH6sH6s'
1104    _MIN_LEN = struct.calcsize(_PACK_STR)
1105
1106    def __init__(self,
1107                 length=0,
1108                 last_egress_id_ui=0,
1109                 last_egress_id_mac='00:00:00:00:00:00',
1110                 next_egress_id_ui=0,
1111                 next_egress_id_mac='00:00:00:00:00:00'
1112                 ):
1113        super(ltr_egress_identifier_tlv, self).__init__(length)
1114        self._type = CFM_LTR_EGRESS_IDENTIFIER_TLV
1115        self.last_egress_id_ui = last_egress_id_ui
1116        self.last_egress_id_mac = last_egress_id_mac
1117        self.next_egress_id_ui = next_egress_id_ui
1118        self.next_egress_id_mac = next_egress_id_mac
1119
1120    @classmethod
1121    def parser(cls, buf):
1122        (type_, length,
1123         last_egress_id_ui, last_egress_id_mac,
1124         next_egress_id_ui, next_egress_id_mac
1125         ) = struct.unpack_from(cls._PACK_STR, buf)
1126        return cls(length,
1127                   last_egress_id_ui,
1128                   addrconv.mac.bin_to_text(last_egress_id_mac),
1129                   next_egress_id_ui,
1130                   addrconv.mac.bin_to_text(next_egress_id_mac))
1131
1132    def serialize(self):
1133        # calculate length when it contains 0
1134        if self.length == 0:
1135            self.length = 16
1136        # start serialize
1137        buf = struct.pack(self._PACK_STR,
1138                          self._type,
1139                          self.length,
1140                          self.last_egress_id_ui,
1141                          addrconv.mac.text_to_bin(self.last_egress_id_mac),
1142                          self.next_egress_id_ui,
1143                          addrconv.mac.text_to_bin(self.next_egress_id_mac)
1144                          )
1145        return bytearray(buf)
1146
1147
1148@operation.register_tlv_types(CFM_ORGANIZATION_SPECIFIC_TLV)
1149class organization_specific_tlv(tlv):
1150
1151    """CFM (IEEE Std 802.1ag-2007) Organization Specific TLV
1152       encoder/decoder class.
1153
1154    This is used with ryu.lib.packet.cfm.cfm.
1155
1156    An instance has the following attributes at least.
1157    Most of them are same to the on-wire counterparts but in host byte order.
1158    __init__ takes the corresponding args in this order.
1159
1160    .. tabularcolumns:: |l|L|
1161
1162    ============== =======================================
1163    Attribute      Description
1164    ============== =======================================
1165    length         Length of Value field.
1166                   (0 means automatically-calculate when encoding.)
1167    oui            Organizationally Unique Identifier.
1168    subtype        Subtype.
1169    value          Value.(optional)
1170    ============== =======================================
1171    """
1172
1173    _PACK_STR = '!BH3sB'
1174    _MIN_LEN = struct.calcsize(_PACK_STR)
1175    _OUI_AND_SUBTYPE_LEN = 4
1176
1177    def __init__(self,
1178                 length=0,
1179                 oui=b"\x00\x00\x00",
1180                 subtype=0,
1181                 value=b""
1182                 ):
1183        super(organization_specific_tlv, self).__init__(length)
1184        self._type = CFM_ORGANIZATION_SPECIFIC_TLV
1185        self.oui = oui
1186        self.subtype = subtype
1187        self.value = value
1188
1189    @classmethod
1190    def parser(cls, buf):
1191        (type_, length, oui, subtype) = struct.unpack_from(cls._PACK_STR, buf)
1192        value = b""
1193        if length > cls._OUI_AND_SUBTYPE_LEN:
1194            form = "%ds" % (length - cls._OUI_AND_SUBTYPE_LEN)
1195            (value,) = struct.unpack_from(form, buf, cls._MIN_LEN)
1196        return cls(length, oui, subtype, value)
1197
1198    def serialize(self):
1199        # calculate length when it contains 0
1200        if self.length == 0:
1201            self.length = len(self.value) + self._OUI_AND_SUBTYPE_LEN
1202        # start serialize
1203        buf = struct.pack(self._PACK_STR,
1204                          self._type,
1205                          self.length,
1206                          self.oui,
1207                          self.subtype,
1208                          )
1209        buf = bytearray(buf)
1210        form = "%ds" % (self.length - self._OUI_AND_SUBTYPE_LEN)
1211        buf.extend(struct.pack(form, self.value))
1212        return buf
1213
1214
1215class reply_tlv(tlv):
1216
1217    _PACK_STR = '!BHB6s'
1218    _MIN_LEN = struct.calcsize(_PACK_STR)
1219    _MIN_VALUE_LEN = _MIN_LEN - struct.calcsize('!BH')
1220    _PORT_ID_LENGTH_LEN = 1
1221    _PORT_ID_SUBTYPE_LEN = 1
1222
1223    def __init__(self, length, action, mac_address, port_id_length,
1224                 port_id_subtype, port_id):
1225        super(reply_tlv, self).__init__(length)
1226        self.action = action
1227        self.mac_address = mac_address
1228        self.port_id_length = port_id_length
1229        self.port_id_subtype = port_id_subtype
1230        self.port_id = port_id
1231
1232    @classmethod
1233    def parser(cls, buf):
1234        (type_, length, action,
1235         mac_address) = struct.unpack_from(cls._PACK_STR, buf)
1236        port_id_length = 0
1237        port_id_subtype = 0
1238        port_id = b''
1239        if length > cls._MIN_VALUE_LEN:
1240            (port_id_length,
1241             port_id_subtype) = struct.unpack_from('!2B', buf, cls._MIN_LEN)
1242            form = "%ds" % port_id_length
1243            (port_id,) = struct.unpack_from(form, buf,
1244                                            cls._MIN_LEN +
1245                                            cls._PORT_ID_LENGTH_LEN +
1246                                            cls._PORT_ID_SUBTYPE_LEN)
1247        return cls(length, action,
1248                   addrconv.mac.bin_to_text(mac_address),
1249                   port_id_length, port_id_subtype, port_id)
1250
1251    def serialize(self):
1252        # calculate length when it contains 0
1253        if self.port_id_length == 0:
1254            self.port_id_length = len(self.port_id)
1255        if self.length == 0:
1256            self.length = self._MIN_VALUE_LEN
1257            if self.port_id_length != 0:
1258                self.length += (self.port_id_length +
1259                                self._PORT_ID_LENGTH_LEN +
1260                                self._PORT_ID_SUBTYPE_LEN)
1261        # start serialize
1262        buf = struct.pack(self._PACK_STR,
1263                          self._type,
1264                          self.length,
1265                          self.action,
1266                          addrconv.mac.text_to_bin(self.mac_address),
1267                          )
1268        buf = bytearray(buf)
1269        if self.port_id_length != 0:
1270            buf.extend(struct.pack("!BB",
1271                                   self.port_id_length,
1272                                   self.port_id_subtype))
1273            form = "%ds" % self.port_id_length
1274            buf.extend(struct.pack(form, self.port_id))
1275        return buf
1276
1277
1278@operation.register_tlv_types(CFM_REPLY_INGRESS_TLV)
1279class reply_ingress_tlv(reply_tlv):
1280
1281    """CFM (IEEE Std 802.1ag-2007) Reply Ingress TLV encoder/decoder class.
1282
1283    This is used with ryu.lib.packet.cfm.cfm.
1284
1285    An instance has the following attributes at least.
1286    Most of them are same to the on-wire counterparts but in host byte order.
1287    __init__ takes the corresponding args in this order.
1288
1289    .. tabularcolumns:: |l|L|
1290
1291    ================= =======================================
1292    Attribute         Description
1293    ================= =======================================
1294    length            Length of Value field.
1295                      (0 means automatically-calculate when encoding.)
1296    action            Ingress Action.The default is 1 (IngOK)
1297    mac_address       Ingress MAC Address.
1298    port_id_length    Ingress PortID Length.
1299                      (0 means automatically-calculate when encoding.)
1300    port_id_subtype   Ingress PortID Subtype.
1301    port_id           Ingress PortID.
1302    ================= =======================================
1303    """
1304
1305    # Ingress Action field values
1306    _ING_OK = 1
1307    _ING_DOWN = 2
1308    _ING_BLOCKED = 3
1309    _ING_VID = 4
1310
1311    def __init__(self,
1312                 length=0,
1313                 action=_ING_OK,
1314                 mac_address='00:00:00:00:00:00',
1315                 port_id_length=0,
1316                 port_id_subtype=0,
1317                 port_id=b''
1318                 ):
1319        super(reply_ingress_tlv, self).__init__(length, action,
1320                                                mac_address, port_id_length,
1321                                                port_id_subtype, port_id)
1322        assert action in [self._ING_OK, self._ING_DOWN,
1323                          self._ING_BLOCKED, self._ING_VID]
1324        self._type = CFM_REPLY_INGRESS_TLV
1325
1326
1327@operation.register_tlv_types(CFM_REPLY_EGRESS_TLV)
1328class reply_egress_tlv(reply_tlv):
1329
1330    """CFM (IEEE Std 802.1ag-2007) Reply Egress TLV encoder/decoder class.
1331
1332    This is used with ryu.lib.packet.cfm.cfm.
1333
1334    An instance has the following attributes at least.
1335    Most of them are same to the on-wire counterparts but in host byte order.
1336    __init__ takes the corresponding args in this order.
1337
1338    .. tabularcolumns:: |l|L|
1339
1340    ================= =======================================
1341    Attribute         Description
1342    ================= =======================================
1343    length            Length of Value field.
1344                      (0 means automatically-calculate when encoding.)
1345    action            Egress Action.The default is 1 (EgrOK)
1346    mac_address       Egress MAC Address.
1347    port_id_length    Egress PortID Length.
1348                      (0 means automatically-calculate when encoding.)
1349    port_id_subtype   Egress PortID Subtype.
1350    port_id           Egress PortID.
1351    ================= =======================================
1352    """
1353
1354    # Egress Action field values
1355    _EGR_OK = 1
1356    _EGR_DOWN = 2
1357    _EGR_BLOCKED = 3
1358    _EGR_VID = 4
1359
1360    def __init__(self,
1361                 length=0,
1362                 action=_EGR_OK,
1363                 mac_address='00:00:00:00:00:00',
1364                 port_id_length=0,
1365                 port_id_subtype=0,
1366                 port_id=b''
1367                 ):
1368        super(reply_egress_tlv, self).__init__(length, action,
1369                                               mac_address, port_id_length,
1370                                               port_id_subtype, port_id)
1371        assert action in [self._EGR_OK, self._EGR_DOWN,
1372                          self._EGR_BLOCKED, self._EGR_VID]
1373        self._type = CFM_REPLY_EGRESS_TLV
1374
1375
1376operation.set_classes(operation._TLV_TYPES)
1377