static int meta_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr, struct bstr *args) { int opnd; struct bstr *a; struct tcf_meta_hdr meta_hdr; unsigned long lvalue = 0, rvalue = 0; memset(&meta_hdr, 0, sizeof(meta_hdr)); if (args == NULL) return PARSE_ERR(args, "meta: missing arguments"); if (!bstrcmp(args, "list")) { list_meta_ids(stderr); return -1; } a = parse_object(args, args, &meta_hdr.left, &lvalue, NULL); if (a == PARSE_FAILURE) return -1; else if (a == NULL) return PARSE_ERR(args, "meta: missing operand"); if (!bstrcmp(a, "eq")) opnd = TCF_EM_OPND_EQ; else if (!bstrcmp(a, "gt")) opnd = TCF_EM_OPND_GT; else if (!bstrcmp(a, "lt")) opnd = TCF_EM_OPND_LT; else return PARSE_ERR(a, "meta: invalid operand"); meta_hdr.left.op = (__u8) opnd; if (a->next == NULL) return PARSE_ERR(args, "meta: missing rvalue"); a = bstr_next(a); a = parse_object(args, a, &meta_hdr.right, &rvalue, &meta_hdr.left); if (a == PARSE_FAILURE) return -1; else if (a != NULL) return PARSE_ERR(a, "meta: unexpected trailer"); addraw_l(n, MAX_MSG, hdr, sizeof(*hdr)); addattr_l(n, MAX_MSG, TCA_EM_META_HDR, &meta_hdr, sizeof(meta_hdr)); dump_value(n, TCA_EM_META_LVALUE, lvalue, &meta_hdr.left); dump_value(n, TCA_EM_META_RVALUE, rvalue, &meta_hdr.right); return 0; }
static int ipset_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr, struct bstr *args) { struct xt_set_info set_info = {}; int ret; #define PARSE_ERR(CARG, FMT, ARGS...) \ em_parse_error(EINVAL, args, CARG, &ipset_ematch_util, FMT, ##ARGS) if (args == NULL) return PARSE_ERR(args, "ipset: missing set name"); if (args->len >= IPSET_MAXNAMELEN) return PARSE_ERR(args, "ipset: set name too long (max %u)", IPSET_MAXNAMELEN - 1); ret = get_set_byname(args->data, &set_info); if (ret < 0) return PARSE_ERR(args, "ipset: unknown set name '%s'", args->data); if (args->next == NULL) return PARSE_ERR(args, "ipset: missing set flags"); args = bstr_next(args); if (parse_dirs(args->data, &set_info)) return PARSE_ERR(args, "ipset: error parsing set flags"); if (args->next) { args = bstr_next(args); return PARSE_ERR(args, "ipset: unknown parameter"); } addraw_l(n, MAX_MSG, hdr, sizeof(*hdr)); addraw_l(n, MAX_MSG, &set_info, sizeof(set_info)); #undef PARSE_ERR return 0; }
static int nbyte_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr, struct bstr *args) { struct bstr *a; struct bstr *needle = args; unsigned long offset = 0, layer = TCF_LAYER_NETWORK; int offset_present = 0; struct tcf_em_nbyte nb; memset(&nb, 0, sizeof(nb)); #define PARSE_ERR(CARG, FMT, ARGS...) \ em_parse_error(EINVAL, args, CARG, &nbyte_ematch_util, FMT ,##ARGS) if (args == NULL) return PARSE_ERR(args, "nbyte: missing arguments"); if (needle->len <= 0) return PARSE_ERR(args, "nbyte: needle length is 0"); for (a = bstr_next(args); a; a = bstr_next(a)) { if (!bstrcmp(a, "at")) { if (a->next == NULL) return PARSE_ERR(a, "nbyte: missing argument"); a = bstr_next(a); offset = bstrtoul(a); if (offset == ULONG_MAX) return PARSE_ERR(a, "nbyte: invalid offset, " \ "must be numeric"); offset_present = 1; } else if (!bstrcmp(a, "layer")) { if (a->next == NULL) return PARSE_ERR(a, "nbyte: missing argument"); a = bstr_next(a); layer = parse_layer(a); if (layer == INT_MAX) { layer = bstrtoul(a); if (layer == ULONG_MAX) return PARSE_ERR(a, "nbyte: invalid " \ "layer"); } if (layer > TCF_LAYER_MAX) return PARSE_ERR(a, "nbyte: illegal layer, " \ "must be in 0..%d", TCF_LAYER_MAX); } else return PARSE_ERR(a, "nbyte: unknown parameter"); } if (offset_present == 0) return PARSE_ERR(a, "nbyte: offset required"); nb.len = needle->len; nb.layer = (__u8) layer; nb.off = (__u16) offset; addraw_l(n, MAX_MSG, hdr, sizeof(*hdr)); addraw_l(n, MAX_MSG, &nb, sizeof(nb)); addraw_l(n, MAX_MSG, needle->data, needle->len); #undef PARSE_ERR return 0; }
static inline struct bstr * parse_object(struct bstr *args, struct bstr *arg, struct tcf_meta_val *obj, unsigned long *dst, struct tcf_meta_val *left) { struct meta_entry *entry; unsigned long num; struct bstr *a; if (arg->quoted) { obj->kind = TCF_META_TYPE_VAR << 12; obj->kind |= TCF_META_ID_VALUE; *dst = (unsigned long) arg; return bstr_next(arg); } num = bstrtoul(arg); if (num != ULONG_MAX) { obj->kind = TCF_META_TYPE_INT << 12; obj->kind |= TCF_META_ID_VALUE; *dst = (unsigned long) num; return bstr_next(arg); } entry = lookup_meta_entry(arg); if (entry == NULL) { PARSE_ERR(arg, "meta: unknown meta id\n"); return PARSE_FAILURE; } obj->kind = entry->id | (map_type(entry->mask[0]) << 12); if (left) { struct tcf_meta_val *right = obj; if (TCF_META_TYPE(right->kind) == TCF_META_TYPE(left->kind)) goto compatible; if (can_adopt(left) && !can_adopt(right)) { if (is_compatible(left, right)) left->kind = overwrite_type(left, right); else goto not_compatible; } else if (can_adopt(right) && !can_adopt(left)) { if (is_compatible(right, left)) right->kind = overwrite_type(right, left); else goto not_compatible; } else if (can_adopt(left) && can_adopt(right)) { if (is_compatible(left, right)) left->kind = overwrite_type(left, right); else if (is_compatible(right, left)) right->kind = overwrite_type(right, left); else goto not_compatible; } else goto not_compatible; } compatible: a = bstr_next(arg); while(a) { if (!bstrcmp(a, "shift")) { unsigned long shift; if (a->next == NULL) { PARSE_ERR(a, "meta: missing argument"); return PARSE_FAILURE; } a = bstr_next(a); shift = bstrtoul(a); if (shift == ULONG_MAX) { PARSE_ERR(a, "meta: invalid shift, must " \ "be numeric"); return PARSE_FAILURE; } obj->shift = (__u8) shift; a = bstr_next(a); } else if (!bstrcmp(a, "mask")) { unsigned long mask; if (a->next == NULL) { PARSE_ERR(a, "meta: missing argument"); return PARSE_FAILURE; } a = bstr_next(a); mask = bstrtoul(a); if (mask == ULONG_MAX) { PARSE_ERR(a, "meta: invalid mask, must be " \ "numeric"); return PARSE_FAILURE; } *dst = (unsigned long) mask; a = bstr_next(a); } else break; } return a; not_compatible: PARSE_ERR(arg, "lvalue and rvalue are not compatible."); return PARSE_FAILURE; }
static int cmp_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr, struct bstr *args) { struct bstr *a; int align, opnd = 0; unsigned long offset = 0, layer = TCF_LAYER_NETWORK, mask = 0, value = 0; int offset_present = 0, value_present = 0; struct tcf_em_cmp cmp; memset(&cmp, 0, sizeof(cmp)); #define PARSE_ERR(CARG, FMT, ARGS...) \ em_parse_error(EINVAL, args, CARG, &cmp_ematch_util, FMT ,##ARGS) if (args == NULL) return PARSE_ERR(args, "cmp: missing arguments"); if (!bstrcmp(args, "u8")) align = TCF_EM_ALIGN_U8; else if (!bstrcmp(args, "u16")) align = TCF_EM_ALIGN_U16; else if (!bstrcmp(args, "u32")) align = TCF_EM_ALIGN_U32; else return PARSE_ERR(args, "cmp: invalid alignment"); for (a = bstr_next(args); a; a = bstr_next(a)) { if (!bstrcmp(a, "at")) { if (a->next == NULL) return PARSE_ERR(a, "cmp: missing argument"); a = bstr_next(a); offset = bstrtoul(a); if (offset == ULONG_MAX) return PARSE_ERR(a, "cmp: invalid offset, " \ "must be numeric"); offset_present = 1; } else if (!bstrcmp(a, "layer")) { if (a->next == NULL) return PARSE_ERR(a, "cmp: missing argument"); a = bstr_next(a); layer = parse_layer(a); if (layer == INT_MAX) { layer = bstrtoul(a); if (layer == ULONG_MAX) return PARSE_ERR(a, "cmp: invalid " \ "layer"); } if (layer > TCF_LAYER_MAX) return PARSE_ERR(a, "cmp: illegal layer, " \ "must be in 0..%d", TCF_LAYER_MAX); } else if (!bstrcmp(a, "mask")) { if (a->next == NULL) return PARSE_ERR(a, "cmp: missing argument"); a = bstr_next(a); mask = bstrtoul(a); if (mask == ULONG_MAX) return PARSE_ERR(a, "cmp: invalid mask"); } else if (!bstrcmp(a, "trans")) { cmp.flags |= TCF_EM_CMP_TRANS; } else if (!bstrcmp(a, "eq") || !bstrcmp(a, "gt") || !bstrcmp(a, "lt")) { if (!bstrcmp(a, "eq")) opnd = TCF_EM_OPND_EQ; else if (!bstrcmp(a, "gt")) opnd = TCF_EM_OPND_GT; else if (!bstrcmp(a, "lt")) opnd = TCF_EM_OPND_LT; if (a->next == NULL) return PARSE_ERR(a, "cmp: missing argument"); a = bstr_next(a); value = bstrtoul(a); if (value == ULONG_MAX) return PARSE_ERR(a, "cmp: invalid value"); value_present = 1; } else return PARSE_ERR(a, "nbyte: unknown parameter"); } if (offset_present == 0 || value_present == 0) return PARSE_ERR(a, "cmp: offset and value required"); cmp.val = (__u32) value; cmp.mask = (__u32) mask; cmp.off = (__u16) offset; cmp.align = (__u8) align; cmp.layer = (__u8) layer; cmp.opnd = (__u8) opnd; addraw_l(n, MAX_MSG, hdr, sizeof(*hdr)); addraw_l(n, MAX_MSG, &cmp, sizeof(cmp)); #undef PARSE_ERR return 0; }
static int u32_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr, struct bstr *args) { struct bstr *a; int align, nh_len; unsigned long key, mask, offmask = 0, offset; struct tc_u32_key u_key; memset(&u_key, 0, sizeof(u_key)); #define PARSE_ERR(CARG, FMT, ARGS...) \ em_parse_error(EINVAL, args, CARG, &u32_ematch_util, FMT ,##ARGS) if (args == NULL) return PARSE_ERR(args, "u32: missing arguments"); if (!bstrcmp(args, "u8")) align = 1; else if (!bstrcmp(args, "u16")) align = 2; else if (!bstrcmp(args, "u32")) align = 4; else return PARSE_ERR(args, "u32: invalid alignment"); a = bstr_next(args); if (a == NULL) return PARSE_ERR(a, "u32: missing key"); key = bstrtoul(a); if (key == ULONG_MAX) return PARSE_ERR(a, "u32: invalid key, must be numeric"); a = bstr_next(a); if (a == NULL) return PARSE_ERR(a, "u32: missing mask"); mask = bstrtoul(a); if (mask == ULONG_MAX) return PARSE_ERR(a, "u32: invalid mask, must be numeric"); a = bstr_next(a); if (a == NULL || bstrcmp(a, "at") != 0) return PARSE_ERR(a, "u32: missing \"at\""); a = bstr_next(a); if (a == NULL) return PARSE_ERR(a, "u32: missing offset"); nh_len = strlen("nexthdr+"); if (a->len > nh_len && !memcmp(a->data, "nexthdr+", nh_len)) { char buf[a->len - nh_len + 1]; offmask = -1; memcpy(buf, a->data + nh_len, a->len - nh_len); offset = strtoul(buf, NULL, 0); } else if (!bstrcmp(a, "nexthdr+")) { a = bstr_next(a); if (a == NULL) return PARSE_ERR(a, "u32: missing offset"); offset = bstrtoul(a); } else offset = bstrtoul(a); if (offset == ULONG_MAX) return PARSE_ERR(a, "u32: invalid offset"); if (a->next) return PARSE_ERR(a->next, "u32: unexpected trailer"); switch (align) { case 1: if (key > 0xFF) return PARSE_ERR(a, "Illegal key (>0xFF)"); if (mask > 0xFF) return PARSE_ERR(a, "Illegal mask (>0xFF)"); key <<= 24 - ((offset & 3) * 8); mask <<= 24 - ((offset & 3) * 8); offset &= ~3; break; case 2: if (key > 0xFFFF) return PARSE_ERR(a, "Illegal key (>0xFFFF)"); if (mask > 0xFFFF) return PARSE_ERR(a, "Illegal mask (>0xFFFF)"); if ((offset & 3) == 0) { key <<= 16; mask <<= 16; } offset &= ~3; break; } key = htonl(key); mask = htonl(mask); if (offset % 4) return PARSE_ERR(a, "u32: invalid offset alignment, " \ "must be aligned to 4."); key &= mask; u_key.mask = mask; u_key.val = key; u_key.off = offset; u_key.offmask = offmask; addraw_l(n, MAX_MSG, hdr, sizeof(*hdr)); addraw_l(n, MAX_MSG, &u_key, sizeof(u_key)); #undef PARSE_ERR return 0; }
static int canid_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr, struct bstr *args) { int iseff = 0; int ret = 0; struct rules rules = { .rules_capacity = 25, /* Denominator of EM_CANID_RULES_MAX Will be multiplied by 2 to calculate the size for realloc() */ .rules_cnt = 0 }; #define PARSE_ERR(CARG, FMT, ARGS...) \ em_parse_error(EINVAL, args, CARG, &canid_ematch_util, FMT, ##ARGS) if (args == NULL) return PARSE_ERR(args, "canid: missing arguments"); rules.rules_raw = malloc(sizeof(struct can_filter) * rules.rules_capacity); memset(rules.rules_raw, 0, sizeof(struct can_filter) * rules.rules_capacity); do { if (!bstrcmp(args, "sff")) { iseff = 0; } else if (!bstrcmp(args, "eff")) { iseff = 1; } else { ret = PARSE_ERR(args, "canid: invalid key"); goto exit; } args = bstr_next(args); if (args == NULL) { ret = PARSE_ERR(args, "canid: missing argument"); goto exit; } ret = canid_parse_rule(&rules, args, iseff); if (ret == -1) { ret = PARSE_ERR(args, "canid: Improperly formed CAN ID & mask\n"); goto exit; } else if (ret == -2) { ret = PARSE_ERR(args, "canid: Too many arguments on input\n"); goto exit; } } while ((args = bstr_next(args)) != NULL); addraw_l(n, MAX_MSG, hdr, sizeof(*hdr)); addraw_l(n, MAX_MSG, rules.rules_raw, sizeof(struct can_filter) * rules.rules_cnt); #undef PARSE_ERR exit: free(rules.rules_raw); return ret; } static int canid_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data, int data_len) { struct can_filter *conf = data; /* Array with rules */ int rules_count; int i; rules_count = data_len / sizeof(struct can_filter); for (i = 0; i < rules_count; i++) { struct can_filter *pcfltr = &conf[i]; if (pcfltr->can_id & CAN_EFF_FLAG) { if (pcfltr->can_mask == (CAN_EFF_FLAG | CAN_EFF_MASK)) fprintf(fd, "eff 0x%"PRIX32, pcfltr->can_id & CAN_EFF_MASK); else fprintf(fd, "eff 0x%"PRIX32":0x%"PRIX32, pcfltr->can_id & CAN_EFF_MASK, pcfltr->can_mask & CAN_EFF_MASK); } else { if (pcfltr->can_mask == (CAN_EFF_FLAG | CAN_SFF_MASK)) fprintf(fd, "sff 0x%"PRIX32, pcfltr->can_id & CAN_SFF_MASK); else fprintf(fd, "sff 0x%"PRIX32":0x%"PRIX32, pcfltr->can_id & CAN_SFF_MASK, pcfltr->can_mask & CAN_SFF_MASK); } if ((i + 1) < rules_count) fprintf(fd, " "); } return 0; } struct ematch_util canid_ematch_util = { .kind = "canid", .kind_num = TCF_EM_CANID, .parse_eopt = canid_parse_eopt, .print_eopt = canid_print_eopt, .print_usage = canid_print_usage };