/* * (C) 2012-2013 by Pablo Neira Ayuso * (C) 2013 by Tomasz Bursztyka * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This code has been sponsored by Sophos Astaro */ #include #include #include #include #include #include #include #include #include #include #include #include "nft-shared.h" #include "xshared.h" extern struct nft_family_ops nft_family_ops_ipv4; extern struct nft_family_ops nft_family_ops_ipv6; void add_meta(struct nft_rule *r, uint32_t key) { struct nft_rule_expr *expr; expr = nft_rule_expr_alloc("meta"); if (expr == NULL) return; nft_rule_expr_set_u32(expr, NFT_EXPR_META_KEY, key); nft_rule_expr_set_u32(expr, NFT_EXPR_META_DREG, NFT_REG_1); nft_rule_add_expr(r, expr); } void add_payload(struct nft_rule *r, int offset, int len) { struct nft_rule_expr *expr; expr = nft_rule_expr_alloc("payload"); if (expr == NULL) return; nft_rule_expr_set_u32(expr, NFT_EXPR_PAYLOAD_BASE, NFT_PAYLOAD_NETWORK_HEADER); nft_rule_expr_set_u32(expr, NFT_EXPR_PAYLOAD_DREG, NFT_REG_1); nft_rule_expr_set_u32(expr, NFT_EXPR_PAYLOAD_OFFSET, offset); nft_rule_expr_set_u32(expr, NFT_EXPR_PAYLOAD_LEN, len); nft_rule_add_expr(r, expr); } /* bitwise operation is = sreg & mask ^ xor */ void add_bitwise_u16(struct nft_rule *r, int mask, int xor) { struct nft_rule_expr *expr; expr = nft_rule_expr_alloc("bitwise"); if (expr == NULL) return; nft_rule_expr_set_u32(expr, NFT_EXPR_BITWISE_SREG, NFT_REG_1); nft_rule_expr_set_u32(expr, NFT_EXPR_BITWISE_DREG, NFT_REG_1); nft_rule_expr_set_u32(expr, NFT_EXPR_BITWISE_LEN, sizeof(uint16_t)); nft_rule_expr_set(expr, NFT_EXPR_BITWISE_MASK, &mask, sizeof(uint16_t)); nft_rule_expr_set(expr, NFT_EXPR_BITWISE_XOR, &xor, sizeof(uint16_t)); nft_rule_add_expr(r, expr); } void add_cmp_ptr(struct nft_rule *r, uint32_t op, void *data, size_t len) { struct nft_rule_expr *expr; expr = nft_rule_expr_alloc("cmp"); if (expr == NULL) return; nft_rule_expr_set_u8(expr, NFT_EXPR_CMP_SREG, NFT_REG_1); nft_rule_expr_set_u8(expr, NFT_EXPR_CMP_OP, op); nft_rule_expr_set(expr, NFT_EXPR_CMP_DATA, data, len); nft_rule_add_expr(r, expr); } void add_cmp_u8(struct nft_rule *r, uint8_t val, uint32_t op) { add_cmp_ptr(r, op, &val, sizeof(val)); } void add_cmp_u16(struct nft_rule *r, uint16_t val, uint32_t op) { add_cmp_ptr(r, op, &val, sizeof(val)); } void add_cmp_u32(struct nft_rule *r, uint32_t val, uint32_t op) { add_cmp_ptr(r, op, &val, sizeof(val)); } void add_iniface(struct nft_rule *r, char *iface, int invflags) { int iface_len; uint32_t op; iface_len = strlen(iface); if (invflags & IPT_INV_VIA_IN) op = NFT_CMP_NEQ; else op = NFT_CMP_EQ; if (iface[iface_len - 1] == '+') { add_meta(r, NFT_META_IIFNAME); add_cmp_ptr(r, op, iface, iface_len - 1); } else { add_meta(r, NFT_META_IIF); add_cmp_u32(r, if_nametoindex(iface), op); } } void add_outiface(struct nft_rule *r, char *iface, int invflags) { int iface_len; uint32_t op; iface_len = strlen(iface); if (invflags & IPT_INV_VIA_OUT) op = NFT_CMP_NEQ; else op = NFT_CMP_EQ; if (iface[iface_len - 1] == '+') { add_meta(r, NFT_META_OIFNAME); add_cmp_ptr(r, op, iface, iface_len - 1); } else { add_meta(r, NFT_META_OIF); add_cmp_u32(r, if_nametoindex(iface), op); } } void add_addr(struct nft_rule *r, int offset, void *data, size_t len, int invflags) { uint32_t op; add_payload(r, offset, len); if (invflags & IPT_INV_SRCIP || invflags & IPT_INV_DSTIP) op = NFT_CMP_NEQ; else op = NFT_CMP_EQ; add_cmp_ptr(r, op, data, len); } void add_proto(struct nft_rule *r, int offset, size_t len, uint8_t proto, int invflags) { uint32_t op; add_payload(r, offset, len); if (invflags & XT_INV_PROTO) op = NFT_CMP_NEQ; else op = NFT_CMP_EQ; add_cmp_u8(r, proto, op); } bool is_same_interfaces(const char *a_iniface, const char *a_outiface, unsigned const char *a_iniface_mask, unsigned const char *a_outiface_mask, const char *b_iniface, const char *b_outiface, unsigned const char *b_iniface_mask, unsigned const char *b_outiface_mask) { int i; for (i = 0; i < IFNAMSIZ; i++) { if (a_iniface_mask[i] != b_iniface_mask[i]) { DEBUGP("different iniface mask %x, %x (%d)\n", a_iniface_mask[i] & 0xff, b_iniface_mask[i] & 0xff, i); return false; } if ((a_iniface[i] & a_iniface_mask[i]) != (b_iniface[i] & b_iniface_mask[i])) { DEBUGP("different iniface\n"); return false; } if (a_outiface_mask[i] != b_outiface_mask[i]) { DEBUGP("different outiface mask\n"); return false; } if ((a_outiface[i] & a_outiface_mask[i]) != (b_outiface[i] & b_outiface_mask[i])) { DEBUGP("different outiface\n"); return false; } } return true; } void parse_meta(struct nft_rule_expr *e, uint8_t key, char *iniface, unsigned char *iniface_mask, char *outiface, unsigned char *outiface_mask, uint8_t *invflags) { uint32_t value; const void *ifname; size_t len; switch(key) { case NFT_META_IIF: value = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_DATA); if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) *invflags |= IPT_INV_VIA_IN; if_indextoname(value, iniface); memset(iniface_mask, 0xff, strlen(iniface)+1); break; case NFT_META_OIF: value = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_DATA); if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) *invflags |= IPT_INV_VIA_OUT; if_indextoname(value, outiface); memset(outiface_mask, 0xff, strlen(outiface)+1); break; case NFT_META_IIFNAME: ifname = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len); if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) *invflags |= IPT_INV_VIA_IN; memcpy(iniface, ifname, len); iniface[len] = '\0'; /* If zero, then this is an interface mask */ if (if_nametoindex(iniface) == 0) { iniface[len] = '+'; iniface[len+1] = '\0'; } memset(iniface_mask, 0xff, len); break; case NFT_META_OIFNAME: ifname = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len); if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) *invflags |= IPT_INV_VIA_OUT; memcpy(outiface, ifname, len); outiface[len] = '\0'; /* If zero, then this is an interface mask */ if (if_nametoindex(outiface) == 0) { outiface[len] = '+'; outiface[len+1] = '\0'; } memset(outiface_mask, 0xff, len); break; default: DEBUGP("unknown meta key %d\n", key); break; } } const char *nft_parse_target(struct nft_rule *r, const void **targinfo, size_t *target_len) { struct nft_rule_expr_iter *iter; struct nft_rule_expr *expr; const char *targname = NULL; iter = nft_rule_expr_iter_create(r); if (iter == NULL) return NULL; expr = nft_rule_expr_iter_next(iter); while (expr != NULL) { const char *name = nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME); if (strcmp(name, "target") == 0) { targname = nft_rule_expr_get_str(expr, NFT_EXPR_TG_NAME); *targinfo = nft_rule_expr_get(expr, NFT_EXPR_TG_INFO, target_len); break; } else if (strcmp(name, "immediate") == 0) { uint32_t verdict = nft_rule_expr_get_u32(expr, NFT_EXPR_IMM_VERDICT); switch(verdict) { case NF_ACCEPT: targname = "ACCEPT"; break; case NF_DROP: targname = "DROP"; break; case NFT_RETURN: targname = "RETURN"; break; case NFT_GOTO: targname = nft_rule_expr_get_str(expr, NFT_EXPR_IMM_CHAIN); break; case NFT_JUMP: targname = nft_rule_expr_get_str(expr, NFT_EXPR_IMM_CHAIN); break; } } expr = nft_rule_expr_iter_next(iter); } nft_rule_expr_iter_destroy(iter); return targname; } void print_proto(uint16_t proto, int invert) { const struct protoent *pent = getprotobynumber(proto); if (invert) printf("! "); if (pent) { printf("-p %s ", pent->p_name); return; } printf("-p %u ", proto); } void get_cmp_data(struct nft_rule_expr_iter *iter, void *data, size_t dlen, bool *inv) { struct nft_rule_expr *e; const char *name; size_t len; uint8_t op; e = nft_rule_expr_iter_next(iter); if (e == NULL) return; name = nft_rule_expr_get_str(e, NFT_RULE_EXPR_ATTR_NAME); if (strcmp(name, "cmp") != 0) { DEBUGP("skipping no cmp after meta\n"); return; } memcpy(data, nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len), dlen); op = nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP); if (op == NFT_CMP_NEQ) *inv = true; else *inv = false; } static void nft_parse_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, int family, struct iptables_command_state *cs) { uint8_t key = nft_rule_expr_get_u8(e, NFT_EXPR_META_KEY); struct nft_family_ops *ops = nft_family_ops_lookup(family); const char *name; e = nft_rule_expr_iter_next(iter); if (e == NULL) return; name = nft_rule_expr_get_str(e, NFT_RULE_EXPR_ATTR_NAME); if (strcmp(name, "cmp") != 0) { DEBUGP("skipping no cmp after meta\n"); return; } ops->parse_meta(e, key, cs); } static void nft_parse_payload(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, int family, struct iptables_command_state *cs) { struct nft_family_ops *ops = nft_family_ops_lookup(family); uint32_t offset; offset = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_OFFSET); ops->parse_payload(iter, cs, offset); } static void nft_parse_counter(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, struct xt_counters *counters) { counters->pcnt = nft_rule_expr_get_u64(e, NFT_EXPR_CTR_PACKETS); counters->bcnt = nft_rule_expr_get_u64(e, NFT_EXPR_CTR_BYTES); } static void nft_parse_immediate(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, int family, struct iptables_command_state *cs) { int verdict = nft_rule_expr_get_u32(e, NFT_EXPR_IMM_VERDICT); const char *chain = nft_rule_expr_get_str(e, NFT_EXPR_IMM_CHAIN); struct nft_family_ops *ops; /* Standard target? */ switch(verdict) { case NF_ACCEPT: cs->jumpto = "ACCEPT"; return; case NF_DROP: cs->jumpto = "DROP"; return; case NFT_RETURN: cs->jumpto = "RETURN"; return; case NFT_GOTO: ops = nft_family_ops_lookup(family); ops->parse_immediate(cs); case NFT_JUMP: cs->jumpto = chain; return; } } void nft_rule_to_iptables_command_state(struct nft_rule *r, struct iptables_command_state *cs) { struct nft_rule_expr_iter *iter; struct nft_rule_expr *expr; int family = nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY); iter = nft_rule_expr_iter_create(r); if (iter == NULL) return; expr = nft_rule_expr_iter_next(iter); while (expr != NULL) { const char *name = nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME); if (strcmp(name, "counter") == 0) { nft_parse_counter(expr, iter, &cs->counters); } else if (strcmp(name, "payload") == 0) { nft_parse_payload(expr, iter, family, cs); } else if (strcmp(name, "meta") == 0) { nft_parse_meta(expr, iter, family, cs); } else if (strcmp(name, "immediate") == 0) { nft_parse_immediate(expr, iter, family, cs); } expr = nft_rule_expr_iter_next(iter); } nft_rule_expr_iter_destroy(iter); } static void print_match(struct nft_rule_expr *expr, int numeric) { size_t len; const char *match_name = nft_rule_expr_get_str(expr, NFT_EXPR_MT_NAME); const void *match_info = nft_rule_expr_get(expr, NFT_EXPR_MT_INFO, &len); const struct xtables_match *match = xtables_find_match(match_name, XTF_TRY_LOAD, NULL); struct xt_entry_match *m = calloc(1, sizeof(struct xt_entry_match) + len); /* emulate struct xt_entry_match since ->print needs it */ memcpy((void *)&m->data, match_info, len); if (match) { if (match->print) /* FIXME missing first parameter */ match->print(NULL, m, numeric); else printf("%s ", match_name); } else { if (match_name[0]) printf("UNKNOWN match `%s' ", match_name); } free(m); } int print_matches(struct nft_rule *r, int format) { struct nft_rule_expr_iter *iter; struct nft_rule_expr *expr; iter = nft_rule_expr_iter_create(r); if (iter == NULL) return -ENOMEM; expr = nft_rule_expr_iter_next(iter); while (expr != NULL) { const char *name = nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME); if (strcmp(name, "match") == 0) print_match(expr, format & FMT_NUMERIC); expr = nft_rule_expr_iter_next(iter); } nft_rule_expr_iter_destroy(iter); return 0; } int print_target(const char *targname, const void *targinfo, size_t target_len, int format) { struct xtables_target *target; struct xt_entry_target *t; if (targname == NULL) return 0; t = calloc(1, sizeof(struct xt_entry_target) + target_len); if (t == NULL) return -ENOMEM; /* emulate struct xt_entry_target since ->print needs it */ memcpy((void *)&t->data, targinfo, target_len); target = xtables_find_target(targname, XTF_TRY_LOAD); if (target) { if (target->print) /* FIXME missing first parameter */ target->print(NULL, t, format & FMT_NUMERIC); } else if (target_len > 0) printf("[%ld bytes of unknown target data] ", target_len); free(t); return 0; } void print_num(uint64_t number, unsigned int format) { if (format & FMT_KILOMEGAGIGA) { if (number > 99999) { number = (number + 500) / 1000; if (number > 9999) { number = (number + 500) / 1000; if (number > 9999) { number = (number + 500) / 1000; if (number > 9999) { number = (number + 500) / 1000; printf(FMT("%4lluT ","%lluT "), (unsigned long long)number); } else printf(FMT("%4lluG ","%lluG "), (unsigned long long)number); } else printf(FMT("%4lluM ","%lluM "), (unsigned long long)number); } else printf(FMT("%4lluK ","%lluK "), (unsigned long long)number); } else printf(FMT("%5llu ","%llu "), (unsigned long long)number); } else printf(FMT("%8llu ","%llu "), (unsigned long long)number); } void print_firewall_details(const struct iptables_command_state *cs, const char *targname, uint8_t flags, uint8_t invflags, uint8_t proto, const char *iniface, const char *outiface, unsigned int num, unsigned int format) { if (format & FMT_LINENUMBERS) printf(FMT("%-4u ", "%u "), num); if (!(format & FMT_NOCOUNTS)) { print_num(cs->counters.pcnt, format); print_num(cs->counters.bcnt, format); } if (!(format & FMT_NOTARGET)) printf(FMT("%-9s ", "%s "), targname ? targname : ""); fputc(invflags & XT_INV_PROTO ? '!' : ' ', stdout); { const char *pname = proto_to_name(proto, format&FMT_NUMERIC); if (pname) printf(FMT("%-5s", "%s "), pname); else printf(FMT("%-5hu", "%hu "), proto); } if (format & FMT_OPTIONS) { if (format & FMT_NOTABLE) fputs("opt ", stdout); fputc(invflags & IPT_INV_FRAG ? '!' : '-', stdout); fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout); fputc(' ', stdout); } if (format & FMT_VIA) { char iface[IFNAMSIZ+2]; if (invflags & IPT_INV_VIA_IN) { iface[0] = '!'; iface[1] = '\0'; } else iface[0] = '\0'; if (iniface[0] != '\0') { strcat(iface, iniface); } else if (format & FMT_NUMERIC) strcat(iface, "*"); else strcat(iface, "any"); printf(FMT(" %-6s ","in %s "), iface); if (invflags & IPT_INV_VIA_OUT) { iface[0] = '!'; iface[1] = '\0'; } else iface[0] = '\0'; if (outiface[0] != '\0') { strcat(iface, outiface); } else if (format & FMT_NUMERIC) strcat(iface, "*"); else strcat(iface, "any"); printf(FMT("%-6s ","out %s "), iface); } } struct nft_family_ops *nft_family_ops_lookup(int family) { switch (family) { case AF_INET: return &nft_family_ops_ipv4; case AF_INET6: return &nft_family_ops_ipv6; default: break; } return NULL; }