/* * (C) 2013 by Giuseppe Longo * * 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. */ #include #include #include #include #include #include #include #include #include #include #include "nft-shared.h" /* a few names */ char *opcodes[] = { "Request", "Reply", "Request_Reverse", "Reply_Reverse", "DRARP_Request", "DRARP_Reply", "DRARP_Error", "InARP_Request", "ARP_NAK", }; static char * addr_to_dotted(const struct in_addr *addrp) { static char buf[20]; const unsigned char *bytep; bytep = (const unsigned char *) &(addrp->s_addr); sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]); return buf; } static char * addr_to_host(const struct in_addr *addr) { struct hostent *host; if ((host = gethostbyaddr((char *) addr, sizeof(struct in_addr), AF_INET)) != NULL) return (char *) host->h_name; return (char *) NULL; } static char * addr_to_network(const struct in_addr *addr) { struct netent *net; if ((net = getnetbyaddr((long) ntohl(addr->s_addr), AF_INET)) != NULL) return (char *) net->n_name; return (char *) NULL; } static char * addr_to_anyname(const struct in_addr *addr) { char *name; if ((name = addr_to_host(addr)) != NULL || (name = addr_to_network(addr)) != NULL) return name; return addr_to_dotted(addr); } static char * mask_to_dotted(const struct in_addr *mask) { int i; static char buf[20]; u_int32_t maskaddr, bits; maskaddr = ntohl(mask->s_addr); if (maskaddr == 0xFFFFFFFFL) /* we don't want to see "/32" */ return ""; i = 32; bits = 0xFFFFFFFEL; while (--i >= 0 && maskaddr != bits) bits <<= 1; if (i >= 0) sprintf(buf, "/%d", i); else /* mask was not a decent combination of 1's and 0's */ sprintf(buf, "/%s", addr_to_dotted(mask)); return buf; } static void print_mac(const unsigned char *mac, int l) { int j; for (j = 0; j < l; j++) printf("%02x%s", mac[j], (j==l-1) ? "" : ":"); } static void print_mac_and_mask(const unsigned char *mac, const unsigned char *mask, int l) { int i; print_mac(mac, l); for (i = 0; i < l ; i++) if (mask[i] != 255) break; if (i == l) return; printf("/"); print_mac(mask, l); } static uint8_t arpt_to_ipt_flags(uint16_t invflags) { uint8_t result = 0; if (invflags & ARPT_INV_VIA_IN) result |= IPT_INV_VIA_IN; if (invflags & ARPT_INV_VIA_OUT) result |= IPT_INV_VIA_OUT; if (invflags & ARPT_INV_SRCIP) result |= IPT_INV_SRCIP; if (invflags & ARPT_INV_TGTIP) result |= IPT_INV_DSTIP; if (invflags & ARPT_INV_ARPPRO) result |= IPT_INV_PROTO; if (invflags & ARPT_INV_MASK) result |= IPT_INV_MASK; return result; } static int nft_arp_add(struct nft_rule *r, void *data) { struct arpt_entry *fw = data; uint8_t flags = arpt_to_ipt_flags(fw->arp.invflags); if (fw->arp.iniface[0] != '\0') add_iniface(r, fw->arp.iniface, flags); if (fw->arp.outiface[0] != '\0') add_outiface(r, fw->arp.outiface, flags); if (fw->arp.arhrd != 0) { add_payload(r, offsetof(struct arphdr, ar_hrd), 2); add_cmp_u16(r, fw->arp.arhrd, NFT_CMP_EQ); } if (fw->arp.arpro != 0) { add_payload(r, offsetof(struct arphdr, ar_pro), 2); add_cmp_u16(r, fw->arp.arpro, NFT_CMP_EQ); } if (fw->arp.arhln != 0) add_proto(r, offsetof(struct arphdr, ar_hln), 1, fw->arp.arhln, flags); add_proto(r, offsetof(struct arphdr, ar_pln), 1, 4, 0); if (fw->arp.arpop != 0) { add_payload(r, offsetof(struct arphdr, ar_op), 2); add_cmp_u16(r, fw->arp.arpop, NFT_CMP_EQ); } if (fw->arp.src_devaddr.addr[0] != '\0') { add_payload(r, sizeof(struct arphdr), fw->arp.arhln); add_cmp_ptr(r, NFT_CMP_EQ, fw->arp.src_devaddr.addr, fw->arp.arhln); } if (fw->arp.src.s_addr != 0) add_addr(r, sizeof(struct arphdr) + fw->arp.arhln, &fw->arp.src.s_addr, 4, flags); if (fw->arp.tgt_devaddr.addr[0] != '\0') { add_payload(r, sizeof(struct arphdr) + fw->arp.arhln + 4, fw->arp.arhln); add_cmp_ptr(r, NFT_CMP_EQ, fw->arp.tgt_devaddr.addr, fw->arp.arhln); } if (fw->arp.tgt.s_addr != 0) add_addr(r, sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr), &fw->arp.tgt.s_addr, 4, flags); return 0; } static uint16_t ipt_to_arpt_flags(uint8_t invflags) { uint16_t result = 0; if (invflags & IPT_INV_VIA_IN) result |= ARPT_INV_VIA_IN; if (invflags & IPT_INV_VIA_OUT) result |= ARPT_INV_VIA_OUT; if (invflags & IPT_INV_SRCIP) result |= ARPT_INV_SRCIP; if (invflags & IPT_INV_DSTIP) result |= ARPT_INV_TGTIP; if (invflags & IPT_INV_PROTO) result |= ARPT_INV_ARPPRO; if (invflags & IPT_INV_MASK) result |= ARPT_INV_MASK; return result; } static void nft_arp_parse_meta(struct nft_rule_expr *e, uint8_t key, void *data) { struct arpt_entry *fw = data; uint8_t flags; parse_meta(e, key, fw->arp.iniface, fw->arp.iniface_mask, fw->arp.outiface, fw->arp.outiface_mask, &flags); fw->arp.invflags |= ipt_to_arpt_flags(flags); } static void nft_arp_parse_target(struct xtables_target *t, void *data) { struct arpt_entry *fw = data; size_t size = sizeof(struct arpt_entry); struct xt_entry_target **target; fw->target_offset = size; fw->next_offset = size + t->t->u.target_size; target = (void *) fw + fw->target_offset; *target = t->t; } static void nft_arp_parse_immediate(const char *jumpto, bool nft_goto, void *data) { struct xtables_target *target; size_t size; target = xtables_find_target(XT_STANDARD_TARGET, XTF_LOAD_MUST_SUCCEED); size = sizeof(struct xt_entry_target) + target->size; target->t = xtables_calloc(1, size); target->t->u.target_size = size; strcpy(target->t->u.user.name, jumpto); nft_arp_parse_target(target, data); } static void nft_arp_parse_payload(struct nft_rule_expr_iter *iter, uint32_t offset, void *data) { struct arpt_entry *fw = data; struct in_addr addr; unsigned short int ar_hrd, ar_pro, ar_op, ar_hln; bool inv; switch (offset) { case offsetof(struct arphdr, ar_hrd): get_cmp_data(iter, &ar_hrd, sizeof(ar_hrd), &inv); fw->arp.arhrd = htons(ar_hrd); fw->arp.arhrd_mask = htons(0xffff); if (inv) fw->arp.invflags |= ARPT_INV_ARPHRD; break; case offsetof(struct arphdr, ar_pro): get_cmp_data(iter, &ar_pro, sizeof(ar_pro), &inv); fw->arp.arpro = htons(ar_pro); fw->arp.arpro_mask = htons(0xffff); if (inv) fw->arp.invflags |= ARPT_INV_ARPPRO; break; case offsetof(struct arphdr, ar_op): get_cmp_data(iter, &ar_op, sizeof(ar_op), &inv); fw->arp.arpop = htons(ar_op); fw->arp.arpop_mask = htons(0xffff); if (inv) fw->arp.invflags |= ARPT_INV_ARPOP; break; case offsetof(struct arphdr, ar_hln): get_cmp_data(iter, &ar_hln, sizeof(ar_op), &inv); fw->arp.arhln = ar_hln; fw->arp.arhln_mask = 0xff; if (inv) fw->arp.invflags |= ARPT_INV_ARPOP; break; default: if (!fw->arp.arhln) break; if (offset == sizeof(struct arphdr) + fw->arp.arhln) { get_cmp_data(iter, &addr, sizeof(addr), &inv); fw->arp.src.s_addr = addr.s_addr; fw->arp.smsk.s_addr = 0xffffffff; if (inv) fw->arp.invflags |= ARPT_INV_SRCIP; } else if (offset == sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr)) { get_cmp_data(iter, &addr, sizeof(addr), &inv); fw->arp.tgt.s_addr = addr.s_addr; fw->arp.tmsk.s_addr = 0xffffffff; if (inv) fw->arp.invflags |= ARPT_INV_TGTIP; } break; } } static void nft_rule_to_arpt_entry(struct nft_rule *r, struct arpt_entry *fw) { 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, &fw->counters); else if (strcmp(name, "payload") == 0) nft_parse_payload(expr, iter, family, fw); else if (strcmp(name, "meta") == 0) nft_parse_meta(expr, iter, family, fw); else if (strcmp(name, "immediate") == 0) nft_parse_immediate(expr, iter, family, fw); else if (strcmp(name, "target") == 0) nft_parse_target(expr, iter, family, fw); expr = nft_rule_expr_iter_next(iter); } nft_rule_expr_iter_destroy(iter); } static void nft_arp_print_firewall(struct nft_rule *r, unsigned int num, unsigned int format) { struct arpt_entry fw = {}; const char *targname; struct xtables_target *target = NULL; const struct xt_entry_target *t; char buf[BUFSIZ]; int i; char iface[IFNAMSIZ+2]; int print_iface = 0; nft_rule_to_arpt_entry(r, &fw); if (format & FMT_LINENUMBERS) printf("%u ", num); if (fw.target_offset) { t = nft_arp_get_target(&fw); targname = t->u.user.name; target = xtables_find_target(targname, XTF_TRY_LOAD); if (!(format & FMT_NOTARGET)) printf("-j %s ", targname); } iface[0] = '\0'; if (fw.arp.iniface[0] != '\0') { strcat(iface, fw.arp.iniface); print_iface = 1; } else if (format & FMT_VIA) { print_iface = 1; if (format & FMT_NUMERIC) strcat(iface, "*"); else strcat(iface, "any"); } if (print_iface) printf("%s-i %s ", fw.arp.invflags & ARPT_INV_VIA_IN ? "! ": "", iface); print_iface = 0; iface[0] = '\0'; if (fw.arp.outiface[0] != '\0') { strcat(iface, fw.arp.outiface); print_iface = 1; } else if (format & FMT_VIA) { print_iface = 1; if (format & FMT_NUMERIC) strcat(iface, "*"); else strcat(iface, "any"); } if (print_iface) printf("%s-o %s ", fw.arp.invflags & ARPT_INV_VIA_OUT ? "! " : "", iface); if (fw.arp.smsk.s_addr != 0L) { printf("%s", fw.arp.invflags & ARPT_INV_SRCIP ? "! " : ""); if (format & FMT_NUMERIC) sprintf(buf, "%s", addr_to_dotted(&(fw.arp.src))); else sprintf(buf, "%s", addr_to_anyname(&(fw.arp.src))); strcat(buf, mask_to_dotted(&(fw.arp.smsk))); printf("-s %s ", buf); } for (i = 0; i < ARPT_DEV_ADDR_LEN_MAX; i++) if (fw.arp.src_devaddr.mask[i] != 0) break; if (i == ARPT_DEV_ADDR_LEN_MAX) goto after_devsrc; printf("%s", fw.arp.invflags & ARPT_INV_SRCDEVADDR ? "! " : ""); printf("--src-mac "); print_mac_and_mask((unsigned char *)fw.arp.src_devaddr.addr, (unsigned char *)fw.arp.src_devaddr.mask, ETH_ALEN); printf(" "); after_devsrc: if (fw.arp.tmsk.s_addr != 0L) { printf("%s",fw.arp.invflags & ARPT_INV_TGTIP ? "! " : ""); if (format & FMT_NUMERIC) sprintf(buf, "%s", addr_to_dotted(&(fw.arp.tgt))); else sprintf(buf, "%s", addr_to_anyname(&(fw.arp.tgt))); strcat(buf, mask_to_dotted(&(fw.arp.tmsk))); printf("-d %s ", buf); } for (i = 0; i print) /* Print the target information. */ target->print(&fw.arp, t, format & FMT_NUMERIC); } if (!(format & FMT_NOCOUNTS)) { printf(", pcnt="); xtables_print_num(fw.counters.pcnt, format); printf("-- bcnt="); xtables_print_num(fw.counters.bcnt, format); } if (!(format & FMT_NONEWLINE)) fputc('\n', stdout); } struct nft_family_ops nft_family_ops_arp = { .add = nft_arp_add, .is_same = NULL, .print_payload = NULL, .parse_meta = nft_arp_parse_meta, .parse_payload = nft_arp_parse_payload, .parse_immediate = nft_arp_parse_immediate, .print_firewall = nft_arp_print_firewall, .post_parse = NULL, };