/* ebt_stp * * Authors: * Bart De Schuymer * * July, 2003 */ #include #include #include #include #include "../include/ebtables_u.h" #include #define STP_TYPE 'a' #define STP_FLAGS 'b' #define STP_ROOTPRIO 'c' #define STP_ROOTADDR 'd' #define STP_ROOTCOST 'e' #define STP_SENDERPRIO 'f' #define STP_SENDERADDR 'g' #define STP_PORT 'h' #define STP_MSGAGE 'i' #define STP_MAXAGE 'j' #define STP_HELLOTIME 'k' #define STP_FWDD 'l' #define STP_NUMOPS 12 static const struct option opts[] = { { "stp-type" , required_argument, 0, STP_TYPE}, { "stp-flags" , required_argument, 0, STP_FLAGS}, { "stp-root-prio" , required_argument, 0, STP_ROOTPRIO}, { "stp-root-addr" , required_argument, 0, STP_ROOTADDR}, { "stp-root-cost" , required_argument, 0, STP_ROOTCOST}, { "stp-sender-prio" , required_argument, 0, STP_SENDERPRIO}, { "stp-sender-addr" , required_argument, 0, STP_SENDERADDR}, { "stp-port" , required_argument, 0, STP_PORT}, { "stp-msg-age" , required_argument, 0, STP_MSGAGE}, { "stp-max-age" , required_argument, 0, STP_MAXAGE}, { "stp-hello-time" , required_argument, 0, STP_HELLOTIME}, { "stp-forward-delay", required_argument, 0, STP_FWDD}, { 0 } }; #define BPDU_TYPE_CONFIG 0 #define BPDU_TYPE_TCN 0x80 #define BPDU_TYPE_CONFIG_STRING "config" #define BPDU_TYPE_TCN_STRING "tcn" #define FLAG_TC 0x01 #define FLAG_TC_ACK 0x80 #define FLAG_TC_STRING "topology-change" #define FLAG_TC_ACK_STRING "topology-change-ack" static void print_help() { printf( "stp options:\n" "--stp-type type : BPDU type\n" "--stp-flags flag : control flag\n" "--stp-root-prio prio[:prio] : root priority (16-bit) range\n" "--stp-root-addr address[/mask] : MAC address of root\n" "--stp-root-cost cost[:cost] : root cost (32-bit) range\n" "--stp-sender-prio prio[:prio] : sender priority (16-bit) range\n" "--stp-sender-addr address[/mask] : MAC address of sender\n" "--stp-port port[:port] : port id (16-bit) range\n" "--stp-msg-age age[:age] : message age timer (16-bit) range\n" "--stp-max-age age[:age] : maximum age timer (16-bit) range\n" "--stp-hello-time time[:time] : hello time timer (16-bit) range\n" "--stp-forward-delay delay[:delay]: forward delay timer (16-bit) range\n" " Recognized BPDU type strings:\n" " \"config\": configuration BPDU (=0)\n" " \"tcn\" : topology change notification BPDU (=0x80)\n" " Recognized control flag strings:\n" " \"topology-change\" : topology change flag (0x01)\n" " \"topology-change-ack\": topology change acknowledgement flag (0x80)"); } static void init(struct ebt_entry_match *match) { struct ebt_stp_info *stpinfo = (struct ebt_stp_info *)match->data; stpinfo->invflags = 0; stpinfo->bitmask = 0; } static int parse_range(const char *portstring, void *lower, void *upper, int bits, uint32_t min, uint32_t max) { char *buffer; char *cp, *end; uint32_t low_nr, upp_nr; int ret = 0; buffer = strdup(portstring); if ((cp = strchr(buffer, ':')) == NULL) { low_nr = strtoul(buffer, &end, 10); if (*end || low_nr < min || low_nr > max) { ret = -1; goto out; } if (bits == 2) { *(uint16_t *)lower = low_nr; *(uint16_t *)upper = low_nr; } else { *(uint32_t *)lower = low_nr; *(uint32_t *)upper = low_nr; } } else { *cp = '\0'; cp++; if (!*buffer) low_nr = min; else { low_nr = strtoul(buffer, &end, 10); if (*end || low_nr < min) { ret = -1; goto out; } } if (!*cp) upp_nr = max; else { upp_nr = strtoul(cp, &end, 10); if (*end || upp_nr > max) { ret = -1; goto out; } } if (upp_nr < low_nr) { ret = -1; goto out; } if (bits == 2) { *(uint16_t *)lower = low_nr; *(uint16_t *)upper = upp_nr; } else { *(uint32_t *)lower = low_nr; *(uint32_t *)upper = upp_nr; } } out: free(buffer); return ret; } static void print_range(unsigned int l, unsigned int u) { if (l == u) printf("%u ", l); else printf("%u:%u ", l, u); } static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry, unsigned int *flags, struct ebt_entry_match **match) { struct ebt_stp_info *stpinfo = (struct ebt_stp_info *)(*match)->data; unsigned int flag; long int i; char *end = NULL; if (c < 'a' || c > ('a' + STP_NUMOPS - 1)) return 0; flag = 1 << (c - 'a'); ebt_check_option2(flags, flag); if (ebt_check_inverse2(optarg)) stpinfo->invflags |= flag; stpinfo->bitmask |= flag; switch (flag) { case EBT_STP_TYPE: i = strtol(optarg, &end, 0); if (i < 0 || i > 255 || *end != '\0') { if (!strcasecmp(optarg, BPDU_TYPE_CONFIG_STRING)) stpinfo->type = BPDU_TYPE_CONFIG; else if (!strcasecmp(optarg, BPDU_TYPE_TCN_STRING)) stpinfo->type = BPDU_TYPE_TCN; else ebt_print_error2("Bad --stp-type argument"); } else stpinfo->type = i; break; case EBT_STP_FLAGS: i = strtol(optarg, &end, 0); if (i < 0 || i > 255 || *end != '\0') { if (!strcasecmp(optarg, FLAG_TC_STRING)) stpinfo->config.flags = FLAG_TC; else if (!strcasecmp(optarg, FLAG_TC_ACK_STRING)) stpinfo->config.flags = FLAG_TC_ACK; else ebt_print_error2("Bad --stp-flags argument"); } else stpinfo->config.flags = i; break; case EBT_STP_ROOTPRIO: if (parse_range(argv[optind-1], &(stpinfo->config.root_priol), &(stpinfo->config.root_priou), 2, 0, 0xffff)) ebt_print_error("Bad --stp-root-prio range"); break; case EBT_STP_ROOTCOST: if (parse_range(argv[optind-1], &(stpinfo->config.root_costl), &(stpinfo->config.root_costu), 4, 0, 0xffffffff)) ebt_print_error("Bad --stp-root-cost range"); break; case EBT_STP_SENDERPRIO: if (parse_range(argv[optind-1], &(stpinfo->config.sender_priol), &(stpinfo->config.sender_priou), 2, 0, 0xffff)) ebt_print_error("Bad --stp-sender-prio range"); break; case EBT_STP_PORT: if (parse_range(argv[optind-1], &(stpinfo->config.portl), &(stpinfo->config.portu), 2, 0, 0xffff)) ebt_print_error("Bad --stp-port range"); break; case EBT_STP_MSGAGE: if (parse_range(argv[optind-1], &(stpinfo->config.msg_agel), &(stpinfo->config.msg_ageu), 2, 0, 0xffff)) ebt_print_error("Bad --stp-msg-age range"); break; case EBT_STP_MAXAGE: if (parse_range(argv[optind-1], &(stpinfo->config.max_agel), &(stpinfo->config.max_ageu), 2, 0, 0xffff)) ebt_print_error("Bad --stp-max-age range"); break; case EBT_STP_HELLOTIME: if (parse_range(argv[optind-1], &(stpinfo->config.hello_timel), &(stpinfo->config.hello_timeu), 2, 0, 0xffff)) ebt_print_error("Bad --stp-hello-time range"); break; case EBT_STP_FWDD: if (parse_range(argv[optind-1], &(stpinfo->config.forward_delayl), &(stpinfo->config.forward_delayu), 2, 0, 0xffff)) ebt_print_error("Bad --stp-forward-delay range"); break; case EBT_STP_ROOTADDR: if (ebt_get_mac_and_mask(argv[optind-1], (unsigned char *)stpinfo->config.root_addr, (unsigned char *)stpinfo->config.root_addrmsk)) ebt_print_error("Bad --stp-root-addr address"); break; case EBT_STP_SENDERADDR: if (ebt_get_mac_and_mask(argv[optind-1], (unsigned char *)stpinfo->config.sender_addr, (unsigned char *)stpinfo->config.sender_addrmsk)) ebt_print_error("Bad --stp-sender-addr address"); break; default: ebt_print_error("stp match: this shouldn't happen"); } return 1; } static void final_check(const struct ebt_u_entry *entry, const struct ebt_entry_match *match, const char *name, unsigned int hookmask, unsigned int time) { uint8_t bridge_ula[6] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x00}; uint8_t msk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; if (memcmp(entry->destmac, bridge_ula, 6) || memcmp(entry->destmsk, msk, 6)) ebt_print_error("STP matching is only valid when the " "destination MAC address is the bridge group " "address (BGA) 01:80:c2:00:00:00"); } static void print(const struct ebt_u_entry *entry, const struct ebt_entry_match *match) { struct ebt_stp_info *stpinfo = (struct ebt_stp_info *)match->data; struct ebt_stp_config_info *c = &(stpinfo->config); int i; for (i = 0; i < STP_NUMOPS; i++) { if (!(stpinfo->bitmask & (1 << i))) continue; printf("--%s %s", opts[i].name, (stpinfo->invflags & (1 << i)) ? "! " : ""); if (EBT_STP_TYPE == (1 << i)) { if (stpinfo->type == BPDU_TYPE_CONFIG) printf("%s", BPDU_TYPE_CONFIG_STRING); else if (stpinfo->type == BPDU_TYPE_TCN) printf("%s", BPDU_TYPE_TCN_STRING); else printf("%d", stpinfo->type); } else if (EBT_STP_FLAGS == (1 << i)) { if (c->flags == FLAG_TC) printf("%s", FLAG_TC_STRING); else if (c->flags == FLAG_TC_ACK) printf("%s", FLAG_TC_ACK_STRING); else printf("%d", c->flags); } else if (EBT_STP_ROOTPRIO == (1 << i)) print_range(c->root_priol, c->root_priou); else if (EBT_STP_ROOTADDR == (1 << i)) ebt_print_mac_and_mask((unsigned char *)c->root_addr, (unsigned char*)c->root_addrmsk); else if (EBT_STP_ROOTCOST == (1 << i)) print_range(c->root_costl, c->root_costu); else if (EBT_STP_SENDERPRIO == (1 << i)) print_range(c->sender_priol, c->sender_priou); else if (EBT_STP_SENDERADDR == (1 << i)) ebt_print_mac_and_mask((unsigned char *)c->sender_addr, (unsigned char *)c->sender_addrmsk); else if (EBT_STP_PORT == (1 << i)) print_range(c->portl, c->portu); else if (EBT_STP_MSGAGE == (1 << i)) print_range(c->msg_agel, c->msg_ageu); else if (EBT_STP_MAXAGE == (1 << i)) print_range(c->max_agel, c->max_ageu); else if (EBT_STP_HELLOTIME == (1 << i)) print_range(c->hello_timel, c->hello_timeu); else if (EBT_STP_FWDD == (1 << i)) print_range(c->forward_delayl, c->forward_delayu); printf(" "); } } static int compare(const struct ebt_entry_match *m1, const struct ebt_entry_match *m2) { return (!memcmp(m1->data, m2->data, sizeof(struct ebt_stp_info))); } static struct ebt_u_match stp_match = { .name = "stp", .size = sizeof(struct ebt_stp_info), .help = print_help, .init = init, .parse = parse, .final_check = final_check, .print = print, .compare = compare, .extra_ops = opts, }; __attribute__((constructor)) static void extension_init(void) { ebt_register_match(&stp_match); }