| 
 | 1 | +# scapy.contrib.description = Inband Network Telemetry Protocol (INT)  | 
 | 2 | +# scapy.contrib.status = loads  | 
 | 3 | + | 
 | 4 | +'''  | 
 | 5 | +Inband Network Telemetry Protocol (INT)  | 
 | 6 | +
  | 
 | 7 | +References:  | 
 | 8 | +https://staging.p4.org/p4-spec/docs/INT_v2_1.pdf  | 
 | 9 | +https://staging.p4.org/p4-spec/docs/telemetry_report_v2_0.pdf  | 
 | 10 | +https://github.com/p4lang/p4-applications  | 
 | 11 | +
  | 
 | 12 | +Example Packet Formate:  | 
 | 13 | +INT-MX mode:  | 
 | 14 | +INToGre = Ether/IP/GRE/INTShimGre/INTMetaMx/Raw  | 
 | 15 | +INToTCP = Ether/IP/TCP/INTShimTcpUdp/INTMetaMx/Raw  | 
 | 16 | +INToUDP = Ether/IP/UDP/INTShimTcpUdp/INTMetaMx/Raw  | 
 | 17 | +INToVXLAN = Ether/IP/UDP/VXLAN/INTShimVxlan/INTMetaMx/Raw  | 
 | 18 | +INToGENEVE = Ether/IP/UDP/GENEVE/GeneveOptINT/INTMetaMx/Raw  | 
 | 19 | +
  | 
 | 20 | +INT-MD mode:  | 
 | 21 | +INToGre = Ether/IP/GRE/INTShimGre/INTMetaMd/INTMetaHop/Raw  | 
 | 22 | +INToTCP = Ether/IP/TCP/INTShimTcpUdp/INTMetaMd/INTMetaHop/Raw  | 
 | 23 | +INToUDP = Ether/IP/UDP/INTShimTcpUdp/INTMetaMd/INTMetaHop/Raw  | 
 | 24 | +INToVXLAN = Ether/IP/UDP/VXLAN/INTShimVxlan/INTMetaMd/INTMetaHop/Raw  | 
 | 25 | +INToGENEVE = Ether/IP/UDP/GENEVE/GeneveOptINT/INTMetaMd/INTMetaHop/Raw  | 
 | 26 | +'''  | 
 | 27 | + | 
 | 28 | +import struct  | 
 | 29 | +from scapy.packet import Packet, bind_layers  | 
 | 30 | +from scapy.fields import BitField, BitEnumField, FlagsField, ByteField, \  | 
 | 31 | +    ShortField, IntField, LongField, FieldLenField, ConditionalField, \  | 
 | 32 | +    MultipleTypeField, PacketField, PacketListField  | 
 | 33 | +from scapy.layers.l2 import GRE  | 
 | 34 | +from scapy.layers.inet import TCP, UDP  | 
 | 35 | +from scapy.layers.vxlan import VXLAN  | 
 | 36 | + | 
 | 37 | +INT_PRI_MASK = 0x80  | 
 | 38 | +INT_L4_DPORT = 0x4568  | 
 | 39 | +INT_GRE_PROTOCOL = 0x4569  | 
 | 40 | +INT_VXLAN_PROTOCOL = 0x82  | 
 | 41 | +INT_GENEVE_CLASSID = 0x0103  | 
 | 42 | + | 
 | 43 | +_INT_TYPE = {  | 
 | 44 | +    1: 'INT-MD',  | 
 | 45 | +    2: 'INT-DST',  | 
 | 46 | +    3: 'INT-MX',  | 
 | 47 | +}  | 
 | 48 | + | 
 | 49 | +_INT_GRE = {  | 
 | 50 | +    0: 'Original packet with GRE',  | 
 | 51 | +    1: 'Original packet without GRE',  | 
 | 52 | +}  | 
 | 53 | + | 
 | 54 | +_INT_GPE = {  | 
 | 55 | +    0: 'Original packet used VXLAN GPE encapsulation',  | 
 | 56 | +    1: 'Original packet used VXLAN encapsulation',  | 
 | 57 | +}  | 
 | 58 | + | 
 | 59 | +_INT_INSTR_BITMAP = [  | 
 | 60 | +    'checksum',  | 
 | 61 | +    'reserved14',  | 
 | 62 | +    'reserved13',  | 
 | 63 | +    'reserved12',  | 
 | 64 | +    'reserved11',  | 
 | 65 | +    'reserved10',  | 
 | 66 | +    'reserved9',  | 
 | 67 | +    'buf_info',  | 
 | 68 | +    'tx_info',  | 
 | 69 | +    'l2_intf',  | 
 | 70 | +    'egr_ts',  | 
 | 71 | +    'igr_ts',  | 
 | 72 | +    'que_info',  | 
 | 73 | +    'latency',  | 
 | 74 | +    'l1_intf',  | 
 | 75 | +    'node_id',  | 
 | 76 | +]  | 
 | 77 | + | 
 | 78 | + | 
 | 79 | +class INTMetaHop(Packet):  | 
 | 80 | +    name = 'INTMetaHop'  | 
 | 81 | +    fields_desc = [  | 
 | 82 | +        ConditionalField(  | 
 | 83 | +            IntField('node_id', 0),  | 
 | 84 | +            lambda pkt:pkt.parent.instr_bitmap & (0x1 << (15 - 0))  | 
 | 85 | +        ),  | 
 | 86 | +        ConditionalField(  | 
 | 87 | +            ShortField('igr_l1_intf', 0),  | 
 | 88 | +            lambda pkt:pkt.parent.instr_bitmap & (0x1 << (15 - 1))  | 
 | 89 | +        ),  | 
 | 90 | +        ConditionalField(  | 
 | 91 | +            ShortField('egr_l1_intf', 0),  | 
 | 92 | +            lambda pkt:pkt.parent.instr_bitmap & (0x1 << (15 - 1))  | 
 | 93 | +        ),  | 
 | 94 | +        ConditionalField(  | 
 | 95 | +            IntField('latency', 0),  | 
 | 96 | +            lambda pkt:pkt.parent.instr_bitmap & (0x1 << (15 - 2))  | 
 | 97 | +        ),  | 
 | 98 | +        ConditionalField(  | 
 | 99 | +            BitField('que_id', 0, 8),  | 
 | 100 | +            lambda pkt:pkt.parent.instr_bitmap & (0x1 << (15 - 3))  | 
 | 101 | +        ),  | 
 | 102 | +        ConditionalField(  | 
 | 103 | +            BitField('que_occupy', 0, 24),  | 
 | 104 | +            lambda pkt:pkt.parent.instr_bitmap & (0x1 << (15 - 3))  | 
 | 105 | +        ),  | 
 | 106 | +        ConditionalField(  | 
 | 107 | +            LongField('igr_ts', 0),  | 
 | 108 | +            lambda pkt:pkt.parent.instr_bitmap & (0x1 << (15 - 4))  | 
 | 109 | +        ),  | 
 | 110 | +        ConditionalField(  | 
 | 111 | +            LongField('egr_ts', 0),  | 
 | 112 | +            lambda pkt:pkt.parent.instr_bitmap & (0x1 << (15 - 5))  | 
 | 113 | +        ),  | 
 | 114 | +        ConditionalField(  | 
 | 115 | +            IntField('igr_l2_intf', 0),  | 
 | 116 | +            lambda pkt:pkt.parent.instr_bitmap & (0x1 << (15 - 6))  | 
 | 117 | +        ),  | 
 | 118 | +        ConditionalField(  | 
 | 119 | +            IntField('egr_l2_intf', 0),  | 
 | 120 | +            lambda pkt:pkt.parent.instr_bitmap & (0x1 << (15 - 6))  | 
 | 121 | +        ),  | 
 | 122 | +        ConditionalField(  | 
 | 123 | +            IntField('egr_tx_info', 0),  | 
 | 124 | +            lambda pkt:pkt.parent.instr_bitmap & (0x1 << (15 - 7))  | 
 | 125 | +        ),  | 
 | 126 | +        ConditionalField(  | 
 | 127 | +            BitField('buf_id', 0, 8),  | 
 | 128 | +            lambda pkt:pkt.parent.instr_bitmap & (0x1 << (15 - 8))  | 
 | 129 | +        ),  | 
 | 130 | +        ConditionalField(  | 
 | 131 | +            BitField('buf_occupy', 0, 24),  | 
 | 132 | +            lambda pkt:pkt.parent.instr_bitmap & (0x1 << (15 - 8))  | 
 | 133 | +        ),  | 
 | 134 | +        ConditionalField(  | 
 | 135 | +            IntField('reserved9', 0xffffffff),  | 
 | 136 | +            lambda pkt:pkt.parent.instr_bitmap & (0x1 << (15 - 9))  | 
 | 137 | +        ),  | 
 | 138 | +        ConditionalField(  | 
 | 139 | +            IntField('reserved10', 0xffffffff),  | 
 | 140 | +            lambda pkt:pkt.parent.instr_bitmap & (0x1 << (15 - 10))  | 
 | 141 | +        ),  | 
 | 142 | +        ConditionalField(  | 
 | 143 | +            IntField('reserved11', 0xffffffff),  | 
 | 144 | +            lambda pkt:pkt.parent.instr_bitmap & (0x1 << (15 - 11))  | 
 | 145 | +        ),  | 
 | 146 | +        ConditionalField(  | 
 | 147 | +            IntField('reserved12', 0xffffffff),  | 
 | 148 | +            lambda pkt:pkt.parent.instr_bitmap & (0x1 << (15 - 12))  | 
 | 149 | +        ),  | 
 | 150 | +        ConditionalField(  | 
 | 151 | +            IntField('reserved13', 0xffffffff),  | 
 | 152 | +            lambda pkt:pkt.parent.instr_bitmap & (0x1 << (15 - 13))  | 
 | 153 | +        ),  | 
 | 154 | +        ConditionalField(  | 
 | 155 | +            IntField('reserved14', 0xffffffff),  | 
 | 156 | +            lambda pkt:pkt.parent.instr_bitmap & (0x1 << (15 - 14))  | 
 | 157 | +        ),  | 
 | 158 | +        ConditionalField(  | 
 | 159 | +            IntField('checksum', 0xffffffff),  | 
 | 160 | +            lambda pkt:pkt.parent.instr_bitmap & (0x1 << (15 - 15))  | 
 | 161 | +        ),  | 
 | 162 | +    ]  | 
 | 163 | + | 
 | 164 | +    def extract_padding(self, s):  | 
 | 165 | +        return "", s  | 
 | 166 | + | 
 | 167 | + | 
 | 168 | +class INTMetaMx(Packet):  | 
 | 169 | +    name = 'INTMetaMx'  | 
 | 170 | +    fields_desc = [  | 
 | 171 | +        BitField('version', 0, 4),  | 
 | 172 | +        BitField('discard', 0, 1),  | 
 | 173 | +        BitField('reserved1', 0, 27),  | 
 | 174 | +        FlagsField('instr_bitmap', 0xfe00, 16, _INT_INSTR_BITMAP),  | 
 | 175 | +        ShortField('ds_id', 0),  | 
 | 176 | +        ShortField('ds_instr', 0),  | 
 | 177 | +        ShortField('ds_flags', 0),  | 
 | 178 | +    ]  | 
 | 179 | + | 
 | 180 | +    def extract_padding(self, s):  | 
 | 181 | +        return "", s  | 
 | 182 | + | 
 | 183 | + | 
 | 184 | +class INTMetaMd(Packet):  | 
 | 185 | +    name = 'INTMetaMd'  | 
 | 186 | +    fields_desc = [  | 
 | 187 | +        BitField('version', 0, 4),  | 
 | 188 | +        BitField('discard', 0, 1),  | 
 | 189 | +        BitField('exceed_mht', 0, 1),  | 
 | 190 | +        BitField('exceed_mtu', 0, 1),  | 
 | 191 | +        BitField('reserved0', 0, 12),  | 
 | 192 | +        BitField('hop_len', 0, 5),  | 
 | 193 | +        BitField('hop_left', 0, 8),  | 
 | 194 | +        FlagsField('instr_bitmap', 0xfe00, 16, _INT_INSTR_BITMAP),  | 
 | 195 | +        ShortField('ds_id', 0),  | 
 | 196 | +        ShortField('ds_instr', 0),  | 
 | 197 | +        ShortField('ds_flags', 0),  | 
 | 198 | +        PacketListField('meta_hops', [], INTMetaHop,  | 
 | 199 | +                        length_from=lambda pkt: pkt.parent.length * 4 - 12),  | 
 | 200 | +    ]  | 
 | 201 | + | 
 | 202 | +    def post_build(self, pkt, pay):  | 
 | 203 | +        if self.meta_hops is not None:  | 
 | 204 | +            tmp_len = len(self.meta_hops[0]) // 4  | 
 | 205 | +            old_value = struct.unpack("B", pkt[2:3])[0]  | 
 | 206 | +            new_value = (old_value & 0b11100000) | (tmp_len & 0b00011111)  | 
 | 207 | +            pkt = pkt[:2] + struct.pack("B", new_value) + pkt[3:]  | 
 | 208 | +        return pkt + pay  | 
 | 209 | + | 
 | 210 | +    def extract_padding(self, s):  | 
 | 211 | +        return "", s  | 
 | 212 | + | 
 | 213 | + | 
 | 214 | +class INTShimTcpUdp(Packet):  | 
 | 215 | +    name = 'INTShimTcpUdp'  | 
 | 216 | +    fields_desc = [  | 
 | 217 | +        BitEnumField('type', 1, 4, _INT_TYPE),  | 
 | 218 | +        BitField('npt', 0, 2),  | 
 | 219 | +        BitField('reserved1', 0, 2),  | 
 | 220 | +        FieldLenField('length', None,  | 
 | 221 | +                      length_of="metadata",  | 
 | 222 | +                      adjust=lambda pkt, x: x // 4, fmt="B"),  | 
 | 223 | +        ConditionalField(ByteField('reserved3', 0), lambda pkt: pkt.npt == 0),  | 
 | 224 | +        ConditionalField(BitField('dscp', 0, 6), lambda pkt: pkt.npt == 0),  | 
 | 225 | +        ConditionalField(BitField('reserved4', 0, 2), lambda pkt: pkt.npt == 0),  | 
 | 226 | +        ConditionalField(ShortField('l4_dport', 0), lambda pkt: pkt.npt == 1),  | 
 | 227 | +        ConditionalField(ByteField('ip_proto', 0), lambda pkt: pkt.npt == 2),  | 
 | 228 | +        ConditionalField(ByteField('reserved5', 0), lambda pkt: pkt.npt == 2),  | 
 | 229 | +        MultipleTypeField([  | 
 | 230 | +            (PacketField('metadata', None, INTMetaMd), lambda pkt: pkt.type == 1),  | 
 | 231 | +            (PacketField('metadata', None, INTMetaMx), lambda pkt: pkt.type == 3), ],  | 
 | 232 | +            PacketField('metadata', None, INTMetaMd)  | 
 | 233 | +        ),  | 
 | 234 | +    ]  | 
 | 235 | + | 
 | 236 | + | 
 | 237 | +class INTShimGre(Packet):  | 
 | 238 | +    name = 'INTShimGre'  | 
 | 239 | +    fields_desc = [  | 
 | 240 | +        BitEnumField('type', 1, 4, _INT_TYPE),  | 
 | 241 | +        BitEnumField('gre', 0, 1, _INT_GRE),  | 
 | 242 | +        BitField('reserved0', 0, 3),  | 
 | 243 | +        FieldLenField('length', None,  | 
 | 244 | +                      length_of="metadata",  | 
 | 245 | +                      adjust=lambda pkt, x: x // 4, fmt="B"),  | 
 | 246 | +        ShortField('gre_proto', 0),  | 
 | 247 | +        MultipleTypeField([  | 
 | 248 | +            (PacketField('metadata', None, INTMetaMd), lambda pkt: pkt.type == 1),  | 
 | 249 | +            (PacketField('metadata', None, INTMetaMx), lambda pkt: pkt.type == 3), ],  | 
 | 250 | +            PacketField('metadata', None, INTMetaMd)  | 
 | 251 | +        ),  | 
 | 252 | +    ]  | 
 | 253 | + | 
 | 254 | + | 
 | 255 | +class INTShimVxlan(Packet):  | 
 | 256 | +    name = 'INTShimVxlan'  | 
 | 257 | +    fields_desc = [  | 
 | 258 | +        BitEnumField('type', 1, 4, _INT_TYPE),  | 
 | 259 | +        BitField('reserved2', 0, 4),  | 
 | 260 | +        FieldLenField('length', None,  | 
 | 261 | +                      length_of="metadata",  | 
 | 262 | +                      adjust=lambda pkt, x: x // 4, fmt="B"),  | 
 | 263 | +        BitEnumField('gpe', 0, 1, _INT_GPE),  | 
 | 264 | +        BitField('reserved6', 0, 7),  | 
 | 265 | +        ByteField('vxlan_proto', 0),  | 
 | 266 | +        MultipleTypeField([  | 
 | 267 | +            (PacketField('metadata', None, INTMetaMd), lambda pkt: pkt.type == 1),  | 
 | 268 | +            (PacketField('metadata', None, INTMetaMx), lambda pkt: pkt.type == 3), ],  | 
 | 269 | +            PacketField('metadata', None, INTMetaMd)  | 
 | 270 | +        ),  | 
 | 271 | +    ]  | 
 | 272 | + | 
 | 273 | + | 
 | 274 | +bind_layers(UDP, INTShimTcpUdp, dport=INT_L4_DPORT)  | 
 | 275 | +bind_layers(TCP, INTShimTcpUdp, dport=INT_L4_DPORT)  | 
 | 276 | +bind_layers(GRE, INTShimGre, proto=INT_GRE_PROTOCOL)  | 
 | 277 | +bind_layers(VXLAN, INTShimVxlan, NextProtocol=INT_VXLAN_PROTOCOL)  | 
0 commit comments