#include #include /* Clive Nicolson clive@baby.bedroom.gen.nz 20040620 */ /* See http://dsl.linux.it/OamLoopback */ /* This assumes a little endian machine */ struct ATM_HEADER_S { unsigned int VPI_UPPER: 4; unsigned int GFC: 4; unsigned int VCI_UPPER: 4; unsigned int VPI_LOWER: 4; u_int8_t VCI_MIDDLE; unsigned int CLP: 1; #if 0 unsigned int PTI: 3; #else unsigned int PTI_USER: 1; unsigned int PTI_CI: 1; unsigned int PTI_MGM: 1; #endif unsigned int VCI_LOWER: 4; u_int8_t HEC; }; #define SET_VPI(HeaderPtr, VPI) do { \ HeaderPtr->VPI_UPPER = (VPI >> 4) & 0xf; \ HeaderPtr->VPI_LOWER = VPI & 0xf; \ } while (0) #define GET_VPI(HeaderPtr) ((HeaderPtr->VPI_UPPER << 4) | HeaderPtr->VPI_LOWER) #define SET_VCI(HeaderPtr, VCI) do { \ HeaderPtr->VCI_UPPER = (VCI >> 12) & 0xf; \ HeaderPtr->VCI_MIDDLE = (VCI >> 4) & 0xff; \ HeaderPtr->VCI_LOWER = VCI & 0xf; \ } while (0) #define GET_VCI(HeaderPtr) ((HeaderPtr->VCI_UPPER << 12) | \ (HeaderPtr->VCI_MIDDLE << 4) | \ (HeaderPtr->VCI_LOWER)) static struct ATM_HEADER_S ATM_HEADER_F5END_TO_END = { VPI_UPPER: 8 >> 4, GFC: 0, VCI_UPPER: 35 >> 12, VPI_LOWER: 8 & 0xf, VCI_MIDDLE: (35 >> 4) & 0xff, CLP: 1, PTI_USER: 1, PTI_CI: 0, PTI_MGM: 1, VCI_LOWER: 35 & 0xf, HEC: 0 }; static const u_int16_t Crc10Table[256] = { 0x0000,0x0233,0x0255,0x0066,0x0299,0x00aa,0x00cc,0x02ff, 0x0301,0x0132,0x0154,0x0367,0x0198,0x03ab,0x03cd,0x01fe, 0x0031,0x0202,0x0264,0x0057,0x02a8,0x009b,0x00fd,0x02ce, 0x0330,0x0103,0x0165,0x0356,0x01a9,0x039a,0x03fc,0x01cf, 0x0062,0x0251,0x0237,0x0004,0x02fb,0x00c8,0x00ae,0x029d, 0x0363,0x0150,0x0136,0x0305,0x01fa,0x03c9,0x03af,0x019c, 0x0053,0x0260,0x0206,0x0035,0x02ca,0x00f9,0x009f,0x02ac, 0x0352,0x0161,0x0107,0x0334,0x01cb,0x03f8,0x039e,0x01ad, 0x00c4,0x02f7,0x0291,0x00a2,0x025d,0x006e,0x0008,0x023b, 0x03c5,0x01f6,0x0190,0x03a3,0x015c,0x036f,0x0309,0x013a, 0x00f5,0x02c6,0x02a0,0x0093,0x026c,0x005f,0x0039,0x020a, 0x03f4,0x01c7,0x01a1,0x0392,0x016d,0x035e,0x0338,0x010b, 0x00a6,0x0295,0x02f3,0x00c0,0x023f,0x000c,0x006a,0x0259, 0x03a7,0x0194,0x01f2,0x03c1,0x013e,0x030d,0x036b,0x0158, 0x0097,0x02a4,0x02c2,0x00f1,0x020e,0x003d,0x005b,0x0268, 0x0396,0x01a5,0x01c3,0x03f0,0x010f,0x033c,0x035a,0x0169, 0x0188,0x03bb,0x03dd,0x01ee,0x0311,0x0122,0x0144,0x0377, 0x0289,0x00ba,0x00dc,0x02ef,0x0010,0x0223,0x0245,0x0076, 0x01b9,0x038a,0x03ec,0x01df,0x0320,0x0113,0x0175,0x0346, 0x02b8,0x008b,0x00ed,0x02de,0x0021,0x0212,0x0274,0x0047, 0x01ea,0x03d9,0x03bf,0x018c,0x0373,0x0140,0x0126,0x0315, 0x02eb,0x00d8,0x00be,0x028d,0x0072,0x0241,0x0227,0x0014, 0x01db,0x03e8,0x038e,0x01bd,0x0342,0x0171,0x0117,0x0324, 0x02da,0x00e9,0x008f,0x02bc,0x0043,0x0270,0x0216,0x0025, 0x014c,0x037f,0x0319,0x012a,0x03d5,0x01e6,0x0180,0x03b3, 0x024d,0x007e,0x0018,0x022b,0x00d4,0x02e7,0x0281,0x00b2, 0x017d,0x034e,0x0328,0x011b,0x03e4,0x01d7,0x01b1,0x0382, 0x027c,0x004f,0x0029,0x021a,0x00e5,0x02d6,0x02b0,0x0083, 0x012e,0x031d,0x037b,0x0148,0x03b7,0x0184,0x01e2,0x03d1, 0x022f,0x001c,0x007a,0x0249,0x00b6,0x0285,0x02e3,0x00d0, 0x011f,0x032c,0x034a,0x0179,0x0386,0x01b5,0x01d3,0x03e0, 0x021e,0x002d,0x004b,0x0278,0x0087,0x02b4,0x02d2,0x00e1, }; static u_int16_t OAMCrc10( u_int16_t crc10_accum, u_int8_t *data_blk_ptr, int data_blk_size ) { int Index; for (Index = 0; Index < data_blk_size; Index++) crc10_accum = ((crc10_accum & 0x3) << 8) ^ Crc10Table[(crc10_accum >> 2) & 0xff] ^ *data_blk_ptr++; return crc10_accum; } enum OAM_TYPE_E { OAM_TYPE_FAULT_MANAGEMENT=1, OAM_TYPE_PERFORM_MANAGEMENT=2, OAM_TYPE_ACT_DEACT_MANAGEMENT=8, OAM_TYPE_SYSTEM_MANAGEMENT=0xF }; enum OAM_FUNCTION_FAULT_E { OAM_FUNCTION_FAULT_AIS=0, OAM_FUNCTION_FAULT_RDI, OAM_FUNCTION_FAULT_CONTINUITY_CHECK, OAM_FUNCTION_FAULT_LOOPBACK=8 }; struct OAM_LOOPBACK_S { u_int8_t LoopBackInd; u_int8_t CorrelationTag[4]; u_int8_t LocationID[16]; u_int8_t SoureID[16]; u_int8_t Unused[8]; }; /* This assumes a little endian machine */ static struct OAM_MESSAGE_S { enum OAM_FUNCTION_FAULT_E FunctionType:4; enum OAM_TYPE_E OAMType:4; union { struct OAM_LOOPBACK_S OAMLoopBack; u_int8_t Payload[45]; } Message; u_int16_t OAMCRC10/*:10*/; // u_int8_t OAMReserved:6; } my_oam_message = { FunctionType: OAM_FUNCTION_FAULT_LOOPBACK, OAMType: OAM_TYPE_FAULT_MANAGEMENT, { {1, {0x00,0x03,0x67,0xA6}, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a}, {0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a,0x6a} } }, 0x6c00 }; static struct ATM_OAM_CELL_S { struct ATM_HEADER_S *pLoopBackHeader; struct OAM_MESSAGE_S *pData; } OAMCell = { &ATM_HEADER_F5END_TO_END, &my_oam_message }; static void DumpOAMCell( const struct ATM_OAM_CELL_S *p_OAMCell ) { const struct ATM_HEADER_S *p_hdr = p_OAMCell->pLoopBackHeader; const struct OAM_MESSAGE_S *p_oam = p_OAMCell->pData; int i; printf("OAM: GFC=%01x VPI=%3u VCI=%5u " "PTI_MGM=%d PTI_CI=%d PTI_USER=%d CLP=%d ", p_hdr->GFC, GET_VPI(p_hdr), GET_VCI(p_hdr), p_hdr->PTI_MGM, p_hdr->PTI_CI, p_hdr->PTI_USER, p_hdr->CLP); printf("%01x %01x ", p_oam->OAMType, p_oam->FunctionType); for (i = 0; i < sizeof(p_oam->Message.Payload); i++) printf("%02x", p_oam->Message.Payload[i]); printf(" %04x\n", ntohs(p_oam->OAMCRC10)); } int main(int argc, char **argv) { struct ATM_HEADER_S *p_hdr = OAMCell.pLoopBackHeader; struct OAM_MESSAGE_S *p_oam = OAMCell.pData; DumpOAMCell(&OAMCell); /* Assume Vpi/Vci is valid */ if (p_hdr->PTI_MGM) { if (OAMCrc10(0, (u_int8_t *)p_oam, sizeof(*p_oam)) == 0) printf("Rx OAM crc ok\n"); if (p_oam->OAMType == OAM_TYPE_FAULT_MANAGEMENT && p_oam->FunctionType == OAM_FUNCTION_FAULT_LOOPBACK) { printf("Rx OAM loop back\n"); if (p_oam->Message.OAMLoopBack.LoopBackInd & 1) { u_int16_t Crc10; p_oam->Message.OAMLoopBack.LoopBackInd = 0; Crc10 = OAMCrc10(0, (u_int8_t *)p_oam, sizeof(*p_oam)); p_oam->OAMCRC10 ^= htons(Crc10); printf("Xmit\n"); /* Now check what the other end should see */ DumpOAMCell(&OAMCell); if (OAMCrc10(0, (u_int8_t *)p_oam, sizeof(*p_oam)) == 0) printf("Tx OAM crc ok\n"); } } } return 0; }