/****************************************************************************** * mpls.c * * User-space interface library for MPLS-on-Linux. * * - Command line access to MPLS * * Copyright (c) 2000, K A Fraser */ #include "mpls.h" #include #include /* * We declare the libc functions we need explicitly, as Linux and glibc * header files don't coexist happily. */ char *strncpy(char *dest, const char *src, size_t n); int strcmp(const char *s1, const char *s2); int printf(const char *format, ...); typedef void FILE; int fprintf(FILE *stream, const char *format, ...); extern FILE *stderr; extern int errno; char *strerror(int errnum); unsigned long strtoul(const char *, char **, int); #define ERR(__f, __a...) fprintf(stderr, "***ERROR: "); \ fprintf(stderr, __f , ## __a) enum { CMD_NONE, CMD_ASM, CMD_DSM, CMD_APM, CMD_DPM, CMD_AIM, CMD_DIM, CMD_AEM, CMD_DEM, CMD_FA, CMD_D, CMD_ALP, CMD_DLP } cmd_mode; static unsigned int read_num(char *s, int min, int max) { unsigned int result; errno = 0; result = (unsigned int)strtoul(s, NULL, 10); if ( (errno == ERANGE) || result > max || result < min ) { ERR("Argument out of range %d -> %d (read as %d)\n", min, max, result); errno = ERANGE; } return(result); } #define RN(s,min,max) \ ({ unsigned int r = read_num((s),(min),(max)); \ if ( errno == ERANGE ) goto out; r; }) #ifdef LABEL_STACKING static void read_stack(cid_t *cid, char *stack, int min, int max) { int label_cnt = 0; char *var_stack; char *end_ptr = stack; unsigned long int lval; /* The function expects a colon separated list of labels */ var_stack = stack; /* Point to start of string */ while ((label_cnt <= LABEL_STACK_DEPTH) && (*end_ptr != '\0')) { lval = strtoul(var_stack, &end_ptr, 10); if((errno == ERANGE) || (lval < min) || (lval > max)) { printf("Conversion failure or out of range\n"); exit(0); } label_cnt++; if(label_cnt == 1) { /* Store 'master' label - ie as original code */ cid->label = lval; } else { /* Paranoia don't write off end of array */ if(label_cnt <= LABEL_STACK_DEPTH) cid->label_stack[label_cnt - 2] = lval; } /* end_ptr points to non whitespace terminator so step over it... */ var_stack = end_ptr + 1; } /* Notice if we have had more labels than stack depth */ if(label_cnt > LABEL_STACK_DEPTH) { printf("Error: Entered more labels than stack depth of %d\n", LABEL_STACK_DEPTH); exit(1); } else { /* * Count does not include master label for compatibility (ie we have * only a single label if the count is zero) */ cid->num_labels = --label_cnt; } } #endif /* LABEL_STACKING */ static void print_usage(void) { switch ( cmd_mode ) { case CMD_ASM: printf(" ADD SWITCH MAPPING\n"); printf("Usage: mpls as " " []\n"); break; case CMD_DSM: printf(" DELETE SWITCH MAPPING\n"); printf("Usage: mpls ds \n"); break; case CMD_APM: printf(" ADD PORT MAPPING\n"); printf("Usage: mpls ap 'E'|'A'|'L' ( " " | )\n"); printf("MAC address must be specified as (hex) xx:xx:xx:xx:xx:xx\n"); break; case CMD_DPM: printf(" DELETE PORT MAPPING\n"); printf("Usage: mpls dp \n"); break; case CMD_AIM: printf(" ADD INGRESS MAPPING\n"); printf("Usage: mpls ai \n"); printf("NB. IPv4 specific for now...\n"); break; case CMD_DIM: printf(" DELETE INGRESS MAPPING\n"); printf("Usage: mpls di \n"); printf("NB. IPv4 specific for now...\n"); break; case CMD_AEM: printf(" ADD EGRESS MAPPING\n"); printf("Usage: mpls ae \n"); printf("NB. IPv4 specific for now...\n"); break; case CMD_DEM: printf(" DELETE EGRESS MAPPING\n"); printf("Usage: mpls de \n"); break; case CMD_FA: printf(" FLUSH ALL MPLS STATE\n"); printf("Usage: mpls fa\n"); break; case CMD_D: printf(" ENABLE/DISABLE DEBUGGING\n"); printf("Usage: mpls d 'on'|'off'\n"); break; #ifdef LABEL_STACKING case CMD_ALP: printf(" ADD LABEL POP\n"); printf("Usage: mpls al \n"); break; case CMD_DLP: printf(" DEL LABEL POP\n"); printf("Usage: mpls dl \n"); break; #endif /* LABEL_STACKING */ default: printf("Usage: mpls [-h] { as | ds | ap | dp | ai" " | di | ae | de | fa | d } ...\n"); printf(" as: add switch mapping ds: delete switch mapping\n"); printf(" ap: add port mapping dp: delete port mapping\n"); printf(" ai: add ingress mapping di: delete ingress mapping\n"); printf(" ae: add ingress mapping de: delete ingress mapping\n"); #ifdef LABEL_STACKING printf(" al: add label pop dl: delete label pop\n"); #endif printf(" fa: flush all d: debug enable/disable\n"); break; } } int main(int argc, char **argv) { int result = -1, n = 1, help = 0; cmd_mode = CMD_NONE; if ( mpls_init() ) { ERR("Couldn't create netlink socket!!!: %d (%s)\n\n", errno, strerror(errno)); goto out; } if ( argc == 1 ) goto out; if ( strcmp(argv[n], "-h") == 0 ) { n++; if ( argc == 2 ) goto out; help = 1; } if ( strcmp(argv[n], "as") == 0 ) { switch_mapping_t sm; /* For the paranoid */ memset(&sm, 0, sizeof(switch_mapping_t)); cmd_mode = CMD_ASM; n++; if ( help || (n < (argc-5)) || (n > (argc-4)) ) goto out; sm.in_cid.port = RN(argv[n++], 0, 255); sm.in_cid.label = RN(argv[n++], 0, (1<<20)-1); sm.out_cid.port = RN(argv[n++], 0, 255); #ifdef LABEL_STACKING /* Label stack can be applied to output label */ read_stack(&sm.out_cid, argv[n++], 0, (1<<20)-1); if(sm.out_cid.num_labels) { int index; printf("AddSwMapping: in_port=%d, in_label=%d, out_port=%d, " "out_label_stack=%d", sm.in_cid.port, sm.in_cid.label, sm.out_cid.port, sm.out_cid.label); for(index = 0;index (argc-1)) ) goto out; pm.id = RN(argv[n++], 0, 255); if ( *argv[n] == 'a' || *argv[n] == 'A' ) { pm.type = ATM_PORT; } else if ( *argv[n] == 'e' || *argv[n] == 'E' ) { pm.type = ETH_PORT; } else if ( *argv[n] == 'l' || *argv[n] == 'L' ) { pm.type = LOCAL_PORT; } else { ERR("Invalid port type '%c'\n", *argv[n]); } n++; if ( pm.type == ETH_PORT ) { if ( n != (argc-2) ) goto out; strncpy(pm.u.eth.l_ifname, argv[n++], IFNAMSIZ); pm.u.eth.l_ifname[IFNAMSIZ-1] = '\0'; s = argv[n++]; for ( i = 0; i < ETH_ALEN; i++ ) { unsigned int r; if ( (*s != ':') && i ) { ERR("Colon expected in MAC address (suff %s, " "%dth section)\n", s, i); goto out; } if ( i ) s++; errno = 0; r = (unsigned int)strtoul(s, &s, 16); if ( (errno == ERANGE) || (r > 255) ) { ERR("Invalid MAC address\n"); goto out; } pm.u.eth.r_addr[i] = (char)r; } printf("AddPortMapping: Ethernet, l_ifname=%s, " "r_addr=%02x:%02x:%02x:%02x:%02x:%02x, port=%d\n", pm.u.eth.l_ifname, (__u8)pm.u.eth.r_addr[0], (__u8)pm.u.eth.r_addr[1], (__u8)pm.u.eth.r_addr[2], (__u8)pm.u.eth.r_addr[3], (__u8)pm.u.eth.r_addr[4], (__u8)pm.u.eth.r_addr[5], pm.id); } else if ( pm.type == ATM_PORT ) { if ( n != (argc-1) ) goto out; pm.u.atm.l_ifindex = RN(argv[n++], 0, ~0); printf("AddPortMapping: ATM, l_ifindex=%d\n", pm.u.atm.l_ifindex); } if ( mpls_add_port_mapping(&pm) ) { ERR("AddPortMapping failed: %d (%s)\n", errno, strerror(errno)); } } else if ( strcmp(argv[n], "dp") == 0 ) { int port; cmd_mode = CMD_DPM; if ( help || (++n != (argc-1)) ) goto out; port = RN(argv[n++], 0, 255); printf("DelPortMapping: port=%d\n", port); if ( mpls_del_port_mapping(port) ) { ERR("DelPortMapping failed: %d (%s)\n", errno, strerror(errno)); } } else if ( strcmp(argv[n], "ai") == 0 ) { ingress_mapping_t im; memset(&im, 0, sizeof(ingress_mapping_t)); cmd_mode = CMD_AIM; if ( help || (++n != (argc-3)) ) goto out; im.fec.proto = MPLSPROTO_IPV4; im.fec.u.ipv4.tclassid = RN(argv[n++], 0, ~0); im.in_cid.port = RN(argv[n++], 0, 255); im.in_cid.label = RN(argv[n++], 0, (1<<20)-1); printf("AddIngMapping: IPv4, tclassid=0x%08x, port=%d, label=%d\n", im.fec.u.ipv4.tclassid, im.in_cid.port, im.in_cid.label); if ( mpls_add_ingress_mapping(&im) ) { ERR("AddIngMapping failed: %d (%s)\n", errno, strerror(errno)); } } else if ( strcmp(argv[n], "di") == 0 ) { u32 realm; fec_t fec; cmd_mode = CMD_DIM; if ( help || (++n != (argc-1)) ) goto out; realm = RN(argv[n++], 0, ~0); printf("DelIngMapping: tclassid=%08x\n", realm); fec.proto = MPLSPROTO_IPV4; fec.u.ipv4.tclassid = realm; if ( mpls_del_ingress_mapping(&fec) ) { ERR("DelIngMapping failed: %d (%s)\n", errno, strerror(errno)); } } else if ( strcmp(argv[n], "ae") == 0 ) { egress_mapping_t em; memset(&em, 0 , sizeof(egress_mapping_t)); cmd_mode = CMD_AEM; if ( help || (++n != (argc-3)) ) goto out; em.in_cid.port = RN(argv[n++], 0, 255); em.in_cid.label = RN(argv[n++], 0, (1<<20)-1); em.egress.proto = MPLSPROTO_IPV4; strncpy(em.egress.u.ipv4.ifname, argv[n++], IFNAMSIZ); em.egress.u.ipv4.ifname[IFNAMSIZ-1] = '\0'; printf("AddEgMapping: port=%d, label=%d, egress_dev=%s\n", em.in_cid.port, em.in_cid.label, em.egress.u.ipv4.ifname); if ( mpls_add_egress_mapping(&em) ) { ERR("AddEgMapping failed: %d (%s)\n", errno, strerror(errno)); } } else if ( strcmp(argv[n], "de") == 0 ) { cid_t cid; cmd_mode = CMD_DEM; if ( help || (++n != (argc-2)) ) goto out; cid.port = RN(argv[n++], 0, 255); cid.label = RN(argv[n++], 0, (1<<20)-1); printf("DelEgMapping: port=%d, label=%d\n", cid.port, cid.label); if ( mpls_del_egress_mapping(&cid) ) { ERR("DelEgMapping failed: %d (%s)\n", errno, strerror(errno)); } } else if ( strcmp(argv[n], "fa") == 0 ) { cmd_mode = CMD_FA; if ( help || (++n != (argc-0)) ) goto out; printf("Flush All\n"); if ( mpls_flush_all() ) { ERR("Flush failed: %d (%s)\n", errno, strerror(errno)); } } else if ( strcmp(argv[n], "d") == 0 ) { int on = 0; cmd_mode = CMD_D; if ( help || (++n != (argc-1)) ) goto out; if ( strcmp(argv[n], "on") == 0 ) on = 1; if(on) { printf("Debugging on\n"); if(mpls_debug_on()) { ERR("Debug {en|dis}able failed: %d (%s)\n", errno, strerror(errno)); } } else { printf("Debugging off\n"); if(mpls_debug_off()) { ERR("Debug {en|dis}able failed: %d (%s)\n", errno, strerror(errno)); } } } else goto out; result = 0; out: if ( result ) print_usage(); mpls_cleanup(); return(0); }