/* This code unpacks a rule into a Perl hash, for passing to * a script for output and manipulation purposes. */ /* * Author: Derrik Pates * * 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #define __USE_GNU #include "EXTERN.h" #include "perl.h" #include "XSUB.h" /* for iptables/ip6tables internals */ #include "local_types.h" /* for getprotobynumber() */ #include /* for inet_ntop() */ #include #include #include /* for strtoul() */ #include #include #include "unpacker.h" #include "loader.h" #include "module_iface.h" /* Translate an address/netmask pair into a string */ static SV *addr_and_mask_to_sv(ADDR_TYPE addr, ADDR_TYPE mask, bool inv) { char *temp, *temp2, addrstr[ADDR_STRLEN + 1]; unsigned char *mask_access; int i, j, maskwidth = 0, at_zeros = FALSE, contiguous = TRUE; SV *sv; /* We always translate the address into a string */ inet_ntop(ADDR_FAMILY, (void *)&addr, addrstr, ADDR_STRLEN); temp = strdup(addrstr); mask_access = (void *)&mask; /* Do the magic work of converting the netmask to a width value, or a * plain netmask, if it can't be stored as a width value */ for (i = 0; i < sizeof(ADDR_TYPE) && mask_access[i] != 0; i++) { switch (mask_access[i]) { case 0: at_zeros = TRUE; break; case 0xFF: /* Optimize out whole bytes with all bits set */ maskwidth += 8; if (at_zeros) contiguous = FALSE; break; default: for (j = 7; j >= 0; j--) { if ((mask_access[i] >> j) & 1) { maskwidth++; if (at_zeros) { contiguous = FALSE; break; } } else at_zeros = TRUE; } break; } if (!contiguous) break; } if(maskwidth < sizeof(ADDR_TYPE) * 8) { /* Ok, this is not a host entry */ if(contiguous) /* If it was contiguous, it can be expressed * as a single mask value */ asprintf(&temp2, "%s/%u", temp, maskwidth); else { /* Otherwise, express it as a full netmask value */ inet_ntop(ADDR_FAMILY, &mask, addrstr, ADDR_STRLEN); asprintf(&temp2, "%s/%s", temp, addrstr); } free(temp); temp = temp2; } if(inv) { asprintf(&temp2, "%c%s", INVCHAR, temp); free(temp); temp = temp2; } sv = newSVpv(temp, 0); free(temp); return(sv); } /* We have a job, kids - to turn a (ENTRY *) into a hash... */ HV *ipt_do_unpack(ENTRY *entry, HANDLE *table) { SV *sv; HV *hash; AV *match_list = NULL; char *temp, *rawkey, *targetname = NULL, *protoname = NULL; struct protoent *protoinfo; ModuleDef *module = NULL; ENTRY_MATCH *match = NULL; ENTRY_TARGET *target = NULL; /* If the pointer is NULL, then we've got a slight problem. */ if(!entry) return(NULL); hash = newHV(); /* Ok, let's break this down point by point. First off, the source * address... */ if(entry->nfcache & NFC_IPx_SRC) { sv = addr_and_mask_to_sv(ENTRY_ADDR(entry).src, ENTRY_ADDR(entry).smsk, ENTRY_ADDR(entry).invflags & INV_SRCIP); hv_store(hash, "source", 6, sv, 0); } /* Now, the destination address */ if(entry->nfcache & NFC_IPx_DST) { sv = addr_and_mask_to_sv(ENTRY_ADDR(entry).dst, ENTRY_ADDR(entry).dmsk, ENTRY_ADDR(entry).invflags & INV_DSTIP); hv_store(hash, "destination", 11, sv, 0); } /* Now, the packet incoming interface */ if(entry->nfcache & NFC_IPx_IF_IN) { char *ifname = strdup(ENTRY_ADDR(entry).iniface); if(ENTRY_ADDR(entry).invflags & INV_VIA_IN) { asprintf(&temp, "%c%s", INVCHAR, ifname); free(ifname); ifname = temp; } hv_store(hash, "in-interface", 12, newSVpv(ifname, 0), 0); free(ifname); } /* Packet outgoing interface */ if(entry->nfcache & NFC_IPx_IF_OUT) { char *ifname = strdup(ENTRY_ADDR(entry).outiface); if(ENTRY_ADDR(entry).invflags & INV_VIA_OUT) { asprintf(&temp, "%c%s", INVCHAR, ifname); free(ifname); ifname = temp; } hv_store(hash, "out-interface", 13, newSVpv(ifname, 0), 0); free(ifname); } /* Protocol */ if(entry->nfcache & NFC_IPx_PROTO) { char *protostr; if((protoinfo = getprotobynumber(ENTRY_ADDR(entry).proto))) { protostr = strdup(protoinfo->p_name); protoname = protostr; if(ENTRY_ADDR(entry).invflags & INV_PROTO) { asprintf(&temp, "%c%s", INVCHAR, protostr); free(protostr); protostr = temp; protoname = protostr + 1; } protoname = strdup(protoname); sv = newSVpv(protostr, 0); free(protostr); } else if(ENTRY_ADDR(entry).invflags & INV_PROTO) { asprintf(&protostr, "%c%u", INVCHAR, ENTRY_ADDR(entry).proto); sv = newSVpv(protostr, 0); free(protostr); } else sv = newSViv(ENTRY_ADDR(entry).proto); hv_store(hash, "protocol", 8, sv, 0); } #ifndef INET6 /* Fragment flag */ if(ENTRY_ADDR(entry).flags & IPT_F_FRAG) { hv_store(hash, "fragment", 8, newSViv(!(ENTRY_ADDR(entry).invflags & IPT_INV_FRAG)), 0); } #endif /* !INET6 */ /* Jump target */ if((targetname = (char *)GET_TARGET(entry, table))) { target = (void *)entry + entry->target_offset; if(strcmp("", targetname)) hv_store(hash, "jump", 4, newSVpv(targetname, 0), 0); module = ipt_find_module(targetname, MODULE_TARGET, table); /* If we didn't find a module for the target, stuff the raw target * data into the hash with an appropriately-named key. */ if(!module) { char *data; int data_size = target->u.target_size - ALIGN(sizeof(ENTRY_TARGET)); if(data_size > 0) { asprintf(&rawkey, "%s" TARGET_RAW_POSTFIX, targetname); data = malloc(data_size); memcpy(data, target->data, data_size); hv_store(hash, rawkey, strlen(rawkey), newSVpv(data, data_size), 0); free(rawkey); free(data); } } else if(module->get_fields) module->get_fields(hash, ((void *)entry + entry->target_offset), entry); } /* And now, iterate through the match modules */ for(match = (void *)entry->elems; (void *)match < ((void *)entry + entry->target_offset); match = (void *)match + match->u.match_size) { /* If it's a protocol match, make sure it doesn't end up on the * match list. */ if(protoname ? strcmp(protoname, match->u.user.name) : TRUE) { /* If we haven't setup the match list already, create the array * now. */ if(!match_list) match_list = newAV(); av_push(match_list, newSVpv(match->u.user.name, 0)); } module = ipt_find_module(match->u.user.name, MODULE_MATCH, table); /* If we didn't find a module for the current match, stuff the raw * match data into the hash with an appropriately-named key. */ if(!module) { char *data; int data_size = match->u.match_size - ALIGN(sizeof(ENTRY_MATCH)); asprintf(&rawkey, "%s" MATCH_RAW_POSTFIX, match->u.user.name); data = malloc(data_size); memcpy(data, match->data, data_size); hv_store(hash, rawkey, strlen(rawkey), newSVpv(data, data_size), 0); free(rawkey); free(data); } else if(module->get_fields) module->get_fields(hash, match, entry); } if(match_list) hv_store(hash, "matches", 7, newRV_noinc((SV *)match_list), 0); /* And the byte and packet counters */ asprintf(&temp, "%llu", entry->counters.bcnt); hv_store(hash, "bcnt", 4, newSVpv(temp, 0), 0); free(temp); asprintf(&temp, "%llu", entry->counters.pcnt); hv_store(hash, "pcnt", 4, newSVpv(temp, 0), 0); free(temp); if(protoname) free(protoname); return hash; } /* vim: ts=4 */