Nathan Knuth | ab966e5 | 2017-01-30 07:48:13 -0800 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | #--------------------------------------------------------------------------# |
| 3 | # Copyright (C) 2015 - 2016 by Tibit Communications, Inc. # |
| 4 | # All rights reserved. # |
| 5 | # # |
| 6 | # _______ ____ _ ______ # |
| 7 | # /_ __(_) __ )(_)_ __/ # |
| 8 | # / / / / __ / / / / # |
| 9 | # / / / / /_/ / / / / # |
| 10 | # /_/ /_/_____/_/ /_/ # |
| 11 | # # |
| 12 | #--------------------------------------------------------------------------# |
| 13 | """ EOAM protocol implementation in scapy """ |
| 14 | |
| 15 | TIBIT_VERSION_NUMBER = '1.1.2' |
| 16 | |
| 17 | import time |
| 18 | import logging |
| 19 | import argparse |
| 20 | import sys |
| 21 | import inspect |
| 22 | |
| 23 | logging.getLogger("scapy.runtime").setLevel(logging.ERROR) |
| 24 | from scapy.all import Dot1Q |
| 25 | from scapy.all import sniff |
| 26 | from scapy.all import IP, UDP |
| 27 | from scapy.packet import Packet, bind_layers |
| 28 | from scapy.fields import StrField |
| 29 | from scapy.layers.l2 import Ether |
| 30 | from scapy.main import interact |
| 31 | from scapy.sendrecv import sendp |
| 32 | from scapy.sendrecv import srp1 |
| 33 | from scapy.config import conf |
| 34 | conf.verb = None |
| 35 | |
| 36 | import fcntl, socket, struct # for get hw address |
| 37 | |
| 38 | # TODO should remove import * |
| 39 | from EOAM_TLV import * |
| 40 | |
| 41 | EOAM_MULTICAST_ADDRESS = '01:80:c2:00:00:02' |
| 42 | IGMP_MULTICAST_ADDRESS = '01:00:5e:00:00:01' # for test |
| 43 | |
| 44 | class EOAM(): |
| 45 | """ EOAM frame layer """ |
| 46 | def __init__(self, ctag=None, dryrun=False, stag=None, |
| 47 | verbose=False, etype='8809', |
| 48 | dst=EOAM_MULTICAST_ADDRESS, |
| 49 | hexdump=False, interface='eth0', |
| 50 | sleep=1.0): |
| 51 | self.verbose = verbose |
| 52 | self.dst = dst |
| 53 | self.dryrun = dryrun |
| 54 | self.hexdump = hexdump |
| 55 | self.interface = interface |
| 56 | self.etype = int(etype, 16) |
| 57 | self.stag = stag |
| 58 | self.ctag = ctag |
| 59 | self.sleep = sleep |
| 60 | if (self.verbose == True): |
| 61 | print("=== Settings ================") |
| 62 | print("ctag = %s" % self.ctag) |
| 63 | print("stag = %s" % self.stag) |
| 64 | print("dst = %s" % self.dst) |
| 65 | print("dryrun = %s" % self.dryrun) |
| 66 | print("hexdump = %s" % self.hexdump) |
| 67 | print("interface = %s" % self.interface) |
| 68 | print("etype = 0x%04x" % self.etype) |
| 69 | print("verbose = %s" % self.verbose) |
| 70 | print("=== END Settings ============") |
| 71 | |
| 72 | def send_frame(self, frame_body): |
| 73 | PACKET = Ether() |
| 74 | PACKET.length = 64 |
| 75 | PACKET.dst = self.dst |
| 76 | PACKET.src = self.getHwAddr(self.interface) |
| 77 | if self.stag: |
| 78 | # WARNING: September/2016: This should be 0x88a8, but the Intel 10G |
| 79 | # hardware I am currently using does not support receiving a TPID of |
| 80 | # 0x88a8. So, I send double CTAGs, and I usually set this to 0x8100. |
| 81 | # (NOTE: The Intel hardware can send a TPID of 0x88a8) |
| 82 | PACKET.type = 0x8100 |
| 83 | if self.ctag: |
| 84 | PACKET/=Dot1Q(type=0x8100,vlan=int(self.stag)) |
| 85 | PACKET/=Dot1Q(type=self.etype,vlan=int(self.ctag)) |
| 86 | else: |
| 87 | PACKET/=Dot1Q(type=self.etype,vlan=int(self.stag)) |
| 88 | else: |
| 89 | if self.ctag: |
| 90 | PACKET.type = 0x8100 |
| 91 | PACKET/=Dot1Q(type=self.etype,vlan=int(self.ctag)) |
| 92 | else: |
| 93 | PACKET.type = self.etype |
| 94 | # PACKET/=Dot1Q(type=self.etype, vlan=int(self.ctag)) |
| 95 | PACKET/=SlowProtocolsSubtype()/FlagsBytes()/OAMPDU() |
| 96 | PACKET/=frame_body |
| 97 | PACKET/=EndOfPDU() |
| 98 | if (self.verbose == True): |
| 99 | PACKET.show() |
| 100 | print '###[ Frame Length %d (before padding) ]###' % len(PACKET) |
| 101 | if (self.hexdump == True): |
| 102 | print hexdump(PACKET) |
| 103 | if (self.dryrun != True): |
| 104 | sendp(PACKET, iface=self.interface, verbose=self.verbose) |
| 105 | time.sleep(self.sleep) |
| 106 | return PACKET |
| 107 | |
| 108 | def get_request(self, TLV): |
| 109 | return self.send_frame(CablelabsOUI()/DPoEOpcode_GetRequest()/TLV) |
| 110 | |
| 111 | def set_request(self, TLV): |
| 112 | return self.send_frame(CablelabsOUI()/DPoEOpcode_SetRequest()/TLV) |
| 113 | |
| 114 | def send_multicast_register(self, TLV): |
| 115 | ''' |
| 116 | Note, for mulicast, the standard specifies a register message |
| 117 | with ActionFlags of either Register or Deregister |
| 118 | ''' |
| 119 | return self.send_frame(CablelabsOUI()/DPoEOpcode_MulticastRegister()/TLV) |
| 120 | |
| 121 | def set_request_broadcom(self, TLV): |
| 122 | return self.send_frame(BroadcomOUI()/DPoEOpcode_SetRequest()/TLV) |
| 123 | |
| 124 | def getHwAddr(self, ifname): |
| 125 | if (not self.dryrun): |
| 126 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) |
| 127 | info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', ifname[:15])) |
| 128 | else: |
| 129 | info = range(1, 24) |
| 130 | info[18:24] = ['0','1','2','3','4','5'] |
| 131 | |
| 132 | return ':'.join(['%02x' % ord(char) for char in info[18:24]]) |
| 133 | |
| 134 | |
| 135 | if __name__ == "__main__": |
| 136 | parser = argparse.ArgumentParser() |
| 137 | parser.add_argument('-d', '--dst', dest='dst', action='store', default=EOAM_MULTICAST_ADDRESS, |
| 138 | help='MAC destination (default: %s)' % EOAM_MULTICAST_ADDRESS) |
| 139 | parser.add_argument('-e', '--etype', dest='etype', action='store', default='8809', |
| 140 | help='EtherType value (default: 0x8809)') |
| 141 | parser.add_argument('-i', '--interface', dest='interface', action='store', default='eth0', |
| 142 | help='ETH interface to send (default: eth0)') |
| 143 | parser.add_argument('-s', '--stag', dest='stag', action='store', default=None, |
| 144 | help='STAG value (default: None)') |
| 145 | parser.add_argument('-c', '--ctag', dest='ctag', action='store', default=None, |
| 146 | help='CTAG value (default: None)') |
| 147 | parser.add_argument('-p', '--sleep', dest='sleep', action='store', default='1.0', type=float, |
| 148 | help='SLEEP time after frame (default: 0.5 secs)') |
| 149 | parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', default=False, |
| 150 | help='verbose frame print out') |
| 151 | parser.add_argument('-x', '--hexdump', dest='hexdump', action='store_true', default=False, |
| 152 | help='Hexdump the frame') |
| 153 | parser.add_argument('-y', '--dryrun', dest='dryrun', action='store_true', default=False, |
| 154 | help='Dry run test, dont send - just print') |
| 155 | |
| 156 | parser.add_argument('-t', '--test', dest='test', action='store_true', default=False, |
| 157 | help='Run commands under test') |
| 158 | parser.add_argument('-r', '--critical', dest='critical', action='store_true', default=False, |
| 159 | help='Send the critical OAM set of set_request()') |
| 160 | parser.add_argument('-ta', '--test_add', dest='test_add', action='store_true', default=False, |
| 161 | help='Run commands under test') |
| 162 | parser.add_argument('-td', '--test_del', dest='test_del', action='store_true', default=False, |
| 163 | help='Run commands under test') |
| 164 | parser.add_argument('-tc', '--test_clr', dest='test_clr', action='store_true', default=False, |
| 165 | help='Run commands under test') |
| 166 | |
| 167 | args = parser.parse_args() |
| 168 | |
| 169 | if (args.dryrun == True): |
| 170 | args.sleep = 0.0 |
| 171 | |
| 172 | eoam = EOAM( |
| 173 | dryrun=args.dryrun, |
| 174 | dst=args.dst, |
| 175 | etype=args.etype, |
| 176 | hexdump=args.hexdump, |
| 177 | interface=args.interface, |
| 178 | stag=args.stag, |
| 179 | ctag=args.ctag, |
| 180 | verbose=args.verbose, |
| 181 | ) |
| 182 | |
| 183 | if (not args.critical |
| 184 | and not args.test |
| 185 | and not args.test_add |
| 186 | and not args.test_del |
| 187 | and not args.test_clr): |
| 188 | print 'WARNING: *** No frames sent, please specify \'test\' or \'critical\', etc. See --help' |
| 189 | |
| 190 | # Critical OAM Messages |
| 191 | if (args.critical == True): |
| 192 | |
| 193 | # OAM GET Requests |
| 194 | print 'GET DeviceId and MAX Logical Links Message' |
| 195 | eoam.get_request(DeviceId()/MaxLogicalLinks()) |
| 196 | |
| 197 | print 'SET Report Thresholds Message' |
| 198 | eoam.set_request(ReportThresholdsSet()) |
| 199 | |
| 200 | print 'SET OAM Frame Rate Message' |
| 201 | eoam.set_request(OamFrameRateSet()) |
| 202 | |
| 203 | print 'GET multiple - Device and Manufacturing Info' |
| 204 | eoam.get_request(DONUObject()/DeviceId()/MaxLogicalLinks()/ |
| 205 | FirmwareInfo()/ChipsetInfo()/NumberOfNetworkPorts()/NumberOfS1Interfaces()) |
| 206 | |
| 207 | print 'GET - LLID Queue Configuration' |
| 208 | eoam.get_request(DONUObject()/LLIDQueueConfiguration()) |
| 209 | |
| 210 | print 'GET - ONU Manufacturer Organization Name' |
| 211 | eoam.get_request(DONUObject()/OnuManufacturerOrganizationName()) |
| 212 | |
| 213 | print 'GET - ONU Firmware Mfg Time Varying Controls' |
| 214 | eoam.get_request(DONUObject()/FirmwareMfgTimeVaryingControls()) |
| 215 | |
| 216 | print 'GET - ONU Vendor Name' |
| 217 | eoam.get_request(DONUObject()/VendorName()) |
| 218 | |
| 219 | print 'GET - ONU Model Number' |
| 220 | eoam.get_request(DONUObject()/ModelNumber()) |
| 221 | |
| 222 | print 'GET - ONU Hardware Version' |
| 223 | eoam.get_request(DONUObject()/HardwareVersion()) |
| 224 | |
| 225 | print 'SET - Clear Port Ingress Rules -- Network Port Object' |
| 226 | eoam.set_request(NetworkPortObject()/ClearPortIngressRules()) |
| 227 | |
| 228 | print 'SET - Clear Port Ingress Rules -- User Port Object' |
| 229 | eoam.set_request(UserPortObject()/ClearPortIngressRules()) |
| 230 | |
| 231 | print 'SET - Broadcom Specific TLVs' |
| 232 | eoam.set_request_broadcom(Broadcom07_7F_F1_Set01()/Broadcom07_7F_F1_Set02()/ |
| 233 | Broadcom07_7F_F1_Set03()/Broadcom07_7F_F1_Set04()) |
| 234 | |
| 235 | print 'SET - Multicast Register Message 01' |
| 236 | eoam.send_multicast_register(MulticastRegisterSetSumitomo01()) |
| 237 | |
| 238 | print 'SET - Multicast Register Message 02' |
| 239 | eoam.send_multicast_register(MulticastRegisterSetSumitomo02()) |
| 240 | |
| 241 | print 'SET - Custom Field EtherType' |
| 242 | eoam.set_request(UserPortObject()/CustomFieldEtherType()) |
| 243 | |
| 244 | print 'SET - Custom Field Generic L3' |
| 245 | eoam.set_request(UserPortObject()/CustomFieldGenericL3()) |
| 246 | |
| 247 | print 'SET - MAC Learning MIN/MAX/Age Limit -- User Port Object' |
| 248 | eoam.set_request(UserPortObject()/MacLearningMaxAllowedSet()/DynamicAddressAgeLimitSet()/ |
| 249 | SourceAddressAdmissionControlSet()/MacLearningMinGuaranteeSet()) |
| 250 | |
| 251 | print 'SET - MAC Learning/Flooding/Local Switching -- D-ONU Port Object' |
| 252 | eoam.set_request(DONUObject()/MacLearningAggregateLimitSet()/FloodUnknownSet()/LocalSwitchingSet()) |
| 253 | |
| 254 | print 'SET - Report Thresholds -- Unicast Logical Link' |
| 255 | eoam.set_request(UnicastLogicalLink()/UnicastLogicalLinkReportThresholdsSet()) |
| 256 | |
| 257 | print 'SET - Port Ingress Rule -- Network Port Object -- Precedence 12' |
| 258 | eoam.set_request(NetworkPortObject()/ |
| 259 | PortIngressRuleHeader(precedence=12)/ |
| 260 | PortIngressRuleClauseMatchLength01(operator=1)/ |
| 261 | PortIngressRuleResultForward()/ |
| 262 | PortIngressRuleResultQueue(objecttype=0x0003)/ |
| 263 | PortIngressRuleTerminator()/ |
| 264 | AddPortIngressRule()) |
| 265 | |
| 266 | print 'SET - Port Ingress Rule -- User Port Object -- Precedence 13' |
| 267 | eoam.set_request(UserPortObject()/ |
| 268 | PortIngressRuleHeader(precedence=13)/ |
| 269 | PortIngressRuleClauseMatchLength00(fieldcode=1, operator=7)/ |
| 270 | PortIngressRuleResultQueue(objecttype=0x0002)/ |
| 271 | PortIngressRuleTerminator()/ |
| 272 | AddPortIngressRule()) |
| 273 | |
| 274 | print 'SET - Port Ingress Rule -- User Port Object -- Precedence 7 Discard 01' |
| 275 | eoam.set_request(UserPortObject()/ |
| 276 | PortIngressRuleHeader(precedence=7)/ |
| 277 | PortIngressRuleClauseMatchLength06(fieldcode=1, operator=1)/ |
| 278 | PortIngressRuleResultDiscard()/ |
| 279 | PortIngressRuleTerminator()/ |
| 280 | AddPortIngressRule()) |
| 281 | |
| 282 | |
| 283 | print 'SET - Port Ingress Rule -- User Port Object -- Precedence 7 Discard 02' |
| 284 | eoam.set_request(UserPortObject()/ |
| 285 | PortIngressRuleHeader(precedence=7)/ |
| 286 | PortIngressRuleClauseMatchLength06(fieldcode=1, operator=1, match5=0x02)/ |
| 287 | PortIngressRuleClauseMatchLength02(fieldcode=0x19, operator=1, match=0x8889)/ |
| 288 | PortIngressRuleClauseMatchLength01(fieldcode=0x1a, operator=1, match=0x03)/ |
| 289 | PortIngressRuleResultDiscard()/ |
| 290 | PortIngressRuleTerminator()/ |
| 291 | AddPortIngressRule()) |
| 292 | |
| 293 | print 'GET - D-ONU Object -- Firmware Filename' |
| 294 | eoam.set_request(DONUObject()/FirmwareFilename()) |
| 295 | |
| 296 | print 'SET - User Port Object 0 -- Broadcom Specific TLVs' |
| 297 | eoam.set_request_broadcom(UserPortObject(number=0)/Broadcom07_7F_F6_Set()) |
| 298 | |
| 299 | print 'SET - User Port Object 1 -- Broadcom Specific TLVs' |
| 300 | eoam.set_request_broadcom(UserPortObject(number=1)/Broadcom07_7F_F6_Set()) |
| 301 | |
| 302 | print 'SET - User Port Object 0 -- Clause 30 Attributes -- MAC Enable' |
| 303 | eoam.set_request(UserPortObject()/Clause30AttributesMacEnable()) |
| 304 | |
| 305 | print 'SET - IPMC Forwarding Rule Configuration' |
| 306 | eoam.set_request(IpmcForwardingRuleConfiguration()) |
| 307 | |
| 308 | print 'SET - Enable User Traffic -- Unicast Logical Link' |
| 309 | eoam.set_request(UnicastLogicalLink()/EnableUserTraffic()) |
| 310 | |
| 311 | if (args.test == True): |
| 312 | print 'SET - Multicast Register Message 01' |
| 313 | eoam.send_multicast_register(MulticastRegisterSet(MulticastLink=0x3ff0, UnicastLink=0x120f)) |
| 314 | |
| 315 | #print 'SET - Multicast Deregister Message 02' |
| 316 | eoam.send_multicast_register(MulticastRegisterSet(ActionFlags="Deregister",MulticastLink=0x3ff0, UnicastLink=0x120f)) |
| 317 | |
| 318 | if (args.test_clr == True): |
| 319 | print 'SET Clear Static MAC Table -- User Port Object' |
| 320 | eoam.set_request(ClearStaticMacTable()) |
| 321 | |
| 322 | elif (args.test_add == True): |
| 323 | print 'SET Add Static MAC Address -- User Port Object' |
| 324 | eoam.set_request(AddStaticMacAddress(mac=IGMP_MULTICAST_ADDRESS)) |
| 325 | |
| 326 | elif (args.test_del == True): |
| 327 | print 'SET Delete Static MAC Address -- User Port Object' |
| 328 | eoam.set_request(DeleteStaticMacAddress(mac=IGMP_MULTICAST_ADDRESS)) |
| 329 | |
| 330 | |
| 331 | |
| 332 | # EXTERNAL OAM LIB TESTING |
| 333 | #import tboam |
| 334 | #tboam.get_request(f.branch, f.leaf) |
| 335 | #print 'SET - User Port Object 1 -- Broadcom Specific TLVs' |
| 336 | #f = eoam.set_request_broadcom(UserPortObject(number=1)/Broadcom07_7F_F6_Set()) |
| 337 | #print("=== receive frame ===========") |
| 338 | #Now, pretend I just received this frame. |
| 339 | #f.show() |
| 340 | #tboam.set_request(f.branch, f.leaf) |
| 341 | |
| 342 | # Examples |
| 343 | #oam_frame = eoam.get_request(UserPortObject()/LoopbackEnable()) |
| 344 | #oam_frame = eoam.get_reqeust(UserPortObject()/LoopbackDisable()) |
| 345 | #oam_frame = eoam.set_request(UserPortObject()/LoopbackEnable()) |
| 346 | #oam_frame = eoam.set_request(UnicastLogicalLink()/AlarmReportingSet()) |
| 347 | #oam_frame = eoam.get_request(UserPortObject()/BytesDropped()) |
| 348 | #oam_frame = eoam.get_request(UserPortObject()/TxBytesUnused()) |
| 349 | #print 'GET -- User Port Object -- Rx Frame 512-1023' |
| 350 | #oam_frame = eoam.get_request(UserPortObject()/RxFrame_512_1023()) |
| 351 | #print 'GET -- User Port Object -- Tx Frame 512-1023' |
| 352 | #oam_frame = eoam.get_request(UserPortObject()/TxFrame_512_1023()) |
| 353 | #oam_frame = eoam.get_request(NetworkPortObject()/BytesDropped()) |
| 354 | #oam_frame = eoam.get_request(NetworkPortObject()/TxBytesUnused()) |
| 355 | #print 'GET -- Network Port Object -- Rx Frame 512-1023' |
| 356 | #oam_frame = eoam.get_request(NetworkPortObject()/RxFrame_512_1023()) |
| 357 | #print 'GET -- Network Port Object -- Tx Frame 512-1023' |
| 358 | #oam_frame = eoam.get_request(NetworkPortObject()/TxFrame_512_1023()) |