| /* |
| * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org> |
| * (C) 2013 by Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com> |
| * |
| * 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 <http://www.sophos.com> |
| */ |
| |
| #include <string.h> |
| #include <stdio.h> |
| #include <stdbool.h> |
| #include <netdb.h> |
| |
| #include <linux/netfilter/nf_tables.h> |
| |
| #include <libmnl/libmnl.h> |
| #include <libnftables/rule.h> |
| #include <libnftables/expr.h> |
| |
| #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; |
| } |
| } |
| |
| 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; |
| } |
| |
| 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; |
| } |
| |